Обработка исключений в Spring Security
Обработка исключений в Spring Security — это важная часть настройки безопасности, позволяющая управлять реакцией приложения на различные виды ошибок безопасности, такие как неудачная аутентификация, попытка доступа без разрешений или некорректные данные токена.
1. Виды исключений в Spring Security
1.1 Исключения аутентификации
Эти исключения возникают, когда пользователь пытается пройти аутентификацию, но предоставленные данные неверны:
BadCredentialsException — неверный логин или пароль.
UsernameNotFoundException — пользователь не найден.
AccountExpiredException — учетная запись пользователя истекла.
DisabledException — учетная запись отключена.
LockedException — учетная запись заблокирована.
CredentialsExpiredException — срок действия учетных данных истек.
1.2 Исключения авторизации
Эти исключения связаны с попытками доступа к ресурсам без соответствующих прав:
AccessDeniedException — у пользователя недостаточно прав для доступа.
InsufficientAuthenticationException — аутентификация отсутствует или недостаточна.
1.3 Исключения токенов (в случае использования JWT)
JwtException — ошибки валидации JWT токена.
ExpiredJwtException — токен истек.
SignatureException — подпись токена неверна.
2. Как Spring Security обрабатывает исключения?
Spring Security предоставляет встроенные обработчики для исключений:
AuthenticationEntryPoint — используется для обработки ошибок аутентификации. Например, если пользователь не аутентифицирован, возвращается HTTP-статус 401 (Unauthorized).
AccessDeniedHandler — обрабатывает ошибки авторизации (HTTP-статус 403, Forbidden).
Эти компоненты можно настроить или переопределить для обеспечения пользовательской логики.
3. Настройка обработки исключений в Spring Security
3.1 Обработка ошибок аутентификации
Создаем собственный AuthenticationEntryPoint:
3.2 Обработка ошибок авторизации
Создаем собственный AccessDeniedHandler:
#Java #Training #Spring #Security #Security_Exceptions
Обработка исключений в Spring Security — это важная часть настройки безопасности, позволяющая управлять реакцией приложения на различные виды ошибок безопасности, такие как неудачная аутентификация, попытка доступа без разрешений или некорректные данные токена.
1. Виды исключений в Spring Security
1.1 Исключения аутентификации
Эти исключения возникают, когда пользователь пытается пройти аутентификацию, но предоставленные данные неверны:
BadCredentialsException — неверный логин или пароль.
UsernameNotFoundException — пользователь не найден.
AccountExpiredException — учетная запись пользователя истекла.
DisabledException — учетная запись отключена.
LockedException — учетная запись заблокирована.
CredentialsExpiredException — срок действия учетных данных истек.
1.2 Исключения авторизации
Эти исключения связаны с попытками доступа к ресурсам без соответствующих прав:
AccessDeniedException — у пользователя недостаточно прав для доступа.
InsufficientAuthenticationException — аутентификация отсутствует или недостаточна.
1.3 Исключения токенов (в случае использования JWT)
JwtException — ошибки валидации JWT токена.
ExpiredJwtException — токен истек.
SignatureException — подпись токена неверна.
2. Как Spring Security обрабатывает исключения?
Spring Security предоставляет встроенные обработчики для исключений:
AuthenticationEntryPoint — используется для обработки ошибок аутентификации. Например, если пользователь не аутентифицирован, возвращается HTTP-статус 401 (Unauthorized).
AccessDeniedHandler — обрабатывает ошибки авторизации (HTTP-статус 403, Forbidden).
Эти компоненты можно настроить или переопределить для обеспечения пользовательской логики.
3. Настройка обработки исключений в Spring Security
3.1 Обработка ошибок аутентификации
Создаем собственный AuthenticationEntryPoint:
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"" + authException.getMessage() + "\"}");
}
}
Этот обработчик возвращает 401 Unauthorized с описанием ошибки в формате JSON.
3.2 Обработка ошибок авторизации
Создаем собственный AccessDeniedHandler:
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"error\": \"Forbidden\", \"message\": \"" + accessDeniedException.getMessage() + "\"}");
}
}
Этот обработчик возвращает 403 Forbidden с описанием причины отказа.
#Java #Training #Spring #Security #Security_Exceptions
3.3 Подключение обработчиков в конфигурации Spring Security
Подключаем наши обработчики в конфигурационном классе:
4. Обработка исключений для JWT токенов
При использовании JWT токенов можно добавить проверку токена в собственном фильтре, а также обрабатывать связанные исключения.
Пример обработки ошибок JWT
5. Дополнительная обработка исключений
Spring Security позволяет также настроить:
Обработчики неудачной аутентификации: Используйте AuthenticationFailureHandler.
Обработчики успешной аутентификации: Используйте AuthenticationSuccessHandler.
Пример настройки AuthenticationFailureHandler:
Добавьте его в цепочку фильтров:
#Java #Training #Spring #Security #Security_Exceptions
Подключаем наши обработчики в конфигурационном классе:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
private final CustomAuthenticationEntryPoint authenticationEntryPoint;
private final CustomAccessDeniedHandler accessDeniedHandler;
public SecurityConfig(CustomAuthenticationEntryPoint authenticationEntryPoint,
CustomAccessDeniedHandler accessDeniedHandler) {
this.authenticationEntryPoint = authenticationEntryPoint;
this.accessDeniedHandler = accessDeniedHandler;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
return http.build();
}
}
4. Обработка исключений для JWT токенов
При использовании JWT токенов можно добавить проверку токена в собственном фильтре, а также обрабатывать связанные исключения.
Пример обработки ошибок JWT
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
String errorMessage = "Unauthorized access";
Throwable cause = authException.getCause();
if (cause instanceof ExpiredJwtException) {
errorMessage = "JWT token expired";
} else if (cause instanceof SignatureException) {
errorMessage = "JWT signature invalid";
}
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"" + errorMessage + "\"}");
}
}
Подключите JwtAuthenticationEntryPoint вместо стандартного обработчика ошибок аутентификации.
5. Дополнительная обработка исключений
Spring Security позволяет также настроить:
Обработчики неудачной аутентификации: Используйте AuthenticationFailureHandler.
Обработчики успешной аутентификации: Используйте AuthenticationSuccessHandler.
Пример настройки AuthenticationFailureHandler:
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"Authentication failed\", \"message\": \"" + exception.getMessage() + "\"}");
}
}
Добавьте его в цепочку фильтров:
http.formLogin()
.failureHandler(customAuthenticationFailureHandler);
#Java #Training #Spring #Security #Security_Exceptions
6. Глобальная обработка исключений
Для обработки всех исключений в приложении (не только Spring Security), можно использовать аннотацию @ControllerAdvice:
7. Тестирование обработки исключений
Рекомендуется покрыть обработку исключений тестами, чтобы убедиться, что приложение корректно реагирует на все сценарии.
Пример теста с использованием MockMvc:
#Java #Training #Spring #Security #Security_Exceptions
Для обработки всех исключений в приложении (не только Spring Security), можно использовать аннотацию @ControllerAdvice:
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AccessDeniedException.class)
public void handleAccessDeniedException(AccessDeniedException ex, HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write("{\"error\": \"Access Denied\", \"message\": \"" + ex.getMessage() + "\"}");
}
@ExceptionHandler(AuthenticationException.class)
public void handleAuthenticationException(AuthenticationException ex, HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("{\"error\": \"Authentication Failed\", \"message\": \"" + ex.getMessage() + "\"}");
}
}
7. Тестирование обработки исключений
Рекомендуется покрыть обработку исключений тестами, чтобы убедиться, что приложение корректно реагирует на все сценарии.
Пример теста с использованием MockMvc:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class SecurityExceptionTest {
@Autowired
private MockMvc mockMvc;
@Test
void accessDeniedShouldReturn403() throws Exception {
mockMvc.perform(get("/protected-resource"))
.andExpect(status().isForbidden());
}
}
#Java #Training #Spring #Security #Security_Exceptions
Настройка кастомных страниц ошибок в Spring Security
Spring Security позволяет заменить стандартные страницы ошибок на пользовательские. Это улучшает пользовательский опыт и делает интерфейс приложения более профессиональным.
1. Общие страницы ошибок Spring Security
По умолчанию Spring Security использует встроенные страницы ошибок:
401 Unauthorized: для неудачной аутентификации.
403 Forbidden: для попыток доступа без соответствующих прав.
Эти страницы достаточно просты и не подходят для продакшн-приложений. Их можно заменить на кастомные.
2. Подходы к настройке кастомных страниц ошибок
Использование обработчиков (handler):
Настройка обработчиков AuthenticationEntryPoint и AccessDeniedHandler для возврата кастомных страниц.
Глобальная обработка исключений:
Настройка глобального обработчика через @ControllerAdvice.
Перенаправление на HTML-страницы:
Использование механизма перенаправления на заранее подготовленные HTML-страницы.
3. Настройка кастомных страниц ошибок в Spring Security
3.1 Замена страницы для ошибки 403 (Forbidden)
Настраиваем кастомный AccessDeniedHandler:
Добавляем обработчик в конфигурацию Spring Security:
Создаем контроллер для обработки ошибок:
Добавляем страницу src/main/resources/templates/error/403.html (например, для Thymeleaf):
#Java #Training #Spring #Security #Security_Exceptions
Spring Security позволяет заменить стандартные страницы ошибок на пользовательские. Это улучшает пользовательский опыт и делает интерфейс приложения более профессиональным.
1. Общие страницы ошибок Spring Security
По умолчанию Spring Security использует встроенные страницы ошибок:
401 Unauthorized: для неудачной аутентификации.
403 Forbidden: для попыток доступа без соответствующих прав.
Эти страницы достаточно просты и не подходят для продакшн-приложений. Их можно заменить на кастомные.
2. Подходы к настройке кастомных страниц ошибок
Использование обработчиков (handler):
Настройка обработчиков AuthenticationEntryPoint и AccessDeniedHandler для возврата кастомных страниц.
Глобальная обработка исключений:
Настройка глобального обработчика через @ControllerAdvice.
Перенаправление на HTML-страницы:
Использование механизма перенаправления на заранее подготовленные HTML-страницы.
3. Настройка кастомных страниц ошибок в Spring Security
3.1 Замена страницы для ошибки 403 (Forbidden)
Настраиваем кастомный AccessDeniedHandler:
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
// Перенаправление на кастомную страницу
response.sendRedirect("/error/403");
}
}
Добавляем обработчик в конфигурацию Spring Security:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
private final CustomAccessDeniedHandler accessDeniedHandler;
public SecurityConfig(CustomAccessDeniedHandler accessDeniedHandler) {
this.accessDeniedHandler = accessDeniedHandler;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler); // Подключение кастомного обработчика
return http.build();
}
}
Создаем контроллер для обработки ошибок:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ErrorController {
@GetMapping("/error/403")
public String error403() {
return "error/403"; // Возвращаем HTML-страницу
}
}
Добавляем страницу src/main/resources/templates/error/403.html (например, для Thymeleaf):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Access Denied</title>
</head>
<body>
<h1>403 - Access Denied</h1>
<p>Sorry, you don't have permission to access this page.</p>
</body>
</html>
#Java #Training #Spring #Security #Security_Exceptions
3.2 Замена страницы для ошибки 401 (Unauthorized)
Настраиваем кастомный AuthenticationEntryPoint:
Добавляем обработчик в конфигурацию Spring Security:
Создаем контроллер для обработки ошибок:
Создаем страницу src/main/resources/templates/error/401.html:
4. Обработка всех ошибок через глобальный контроллер
Если нужно обрабатывать все исключения централизованно, можно использовать @ControllerAdvice:
#Java #Training #Spring #Security #Security_Exceptions
Настраиваем кастомный AuthenticationEntryPoint:
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendRedirect("/error/401");
}
}
Добавляем обработчик в конфигурацию Spring Security:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint); // Подключение кастомного обработчика
return http.build();
}
Создаем контроллер для обработки ошибок:
@Controller
public class ErrorController {
@GetMapping("/error/401")
public String error401() {
return "error/401"; // Возвращаем HTML-страницу
}
}
Создаем страницу src/main/resources/templates/error/401.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Unauthorized</title>
</head>
<body>
<h1>401 - Unauthorized</h1>
<p>Please log in to access this page.</p>
</body>
</html>
4. Обработка всех ошибок через глобальный контроллер
Если нужно обрабатывать все исключения централизованно, можно использовать @ControllerAdvice:
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class GlobalErrorController {
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public String handleAccessDeniedException() {
return "error/403";
}
@ExceptionHandler(AuthenticationException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String handleAuthenticationException() {
return "error/401";
}
}
#Java #Training #Spring #Security #Security_Exceptions
5. Использование встроенного механизма Spring Boot для ошибок
Spring Boot предоставляет удобный способ настройки кастомных страниц через файл application.properties:
Затем создаем контроллер для обработки пути /error:
Добавляем страницу src/main/resources/templates/error/custom.html:
6. Обработка JSON-ответов для API
Для REST API ошибки обычно возвращаются в формате JSON. Пример глобального обработчика для API:
#Java #Training #Spring #Security #Security_Exceptions
Spring Boot предоставляет удобный способ настройки кастомных страниц через файл application.properties:
server.error.whitelabel.enabled=false
server.error.path=/error
Затем создаем контроллер для обработки пути /error:
@Controller
public class CustomErrorController {
@GetMapping("/error")
public String handleError() {
return "error/custom";
}
}
Добавляем страницу src/main/resources/templates/error/custom.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
<h1>An error occurred</h1>
<p>We are sorry, something went wrong.</p>
</body>
</html>
6. Обработка JSON-ответов для API
Для REST API ошибки обычно возвращаются в формате JSON. Пример глобального обработчика для API:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ApiErrorController {
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<?> handleAccessDeniedException(AccessDeniedException ex) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of(
"error", "Forbidden",
"message", ex.getMessage()
));
}
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<?> handleAuthenticationException(AuthenticationException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of(
"error", "Unauthorized",
"message", ex.getMessage()
));
}
}
#Java #Training #Spring #Security #Security_Exceptions