Spring Безопасность анонимного 401 вместо 403
У меня проблема с поведением по умолчанию в безопасности spring с авторизацией запросов, предоставляемых с помощью Java Config.
http
....
.authorizeRequests()
.antMatchers("/api/test/secured/*").authenticated()
Когда я вызываю, например, /api/test/secured/user
без входа (с анонимным пользователем), он возвращает 403 Forbidden. Есть ли простой способ изменить статус на 401 Несанкционированный, когда анонимный пользователь хочет получить защиту с помощью ресурсов authenticated()
или @PreAuthorize
?
Ответы
Ответ 1
У меня есть решение здесь:
http
.authenticationEntryPoint(authenticationEntryPoint)
Исходный код AuthenticationEntryPoint:
@Component
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(Http401UnauthorizedEntryPoint.class);
/**
* Always returns a 401 error code to the client.
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException,
ServletException {
log.debug("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
Ответ 2
С spring security 4.x уже существует класс для
org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
Spring boot также включает в себя один
org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint
и оба преимущества, которые они требуют, чтобы разработчик использовал спецификацию в качестве 401 ответов требует, чтобы заголовок WWW-Authenticate должен быть установлен, например, 401 ответ может быть:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
error="invalid_token",
error_description="The access token expired"
Итак, в вашей конфигурации безопасности вы определяете и autowire bean класса
Так, например, с spring загрузочным приложением:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
public Http401AuthenticationEntryPoint securityException401EntryPoint(){
return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
}
@Autowired
private Http401AuthenticationEntryPoint authEntrypoint;
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").anonymous()
.antMatchers("/").anonymous()
.antMatchers("/api/**").authenticated()
.and()
.csrf()
.disable()
.headers()
.frameOptions().disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.logout()
.permitAll()
.exceptionHandling().authenticationEntryPoint(authEntrypoint);
}
соответствующая строка:
.exceptionHandling().authenticationEntryPoint(authEntrypoint);
Ответ 3
Начиная с Spring Boot 2 класс Http401AuthenticationEntryPoint был удален (см. Spring Boot Issue 10725).
Вместо Http401AuthenticationEntryPoint используйте HttpStatusEntryPoint с HttpStatus.UNAUTHORIZED:
http.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
Ответ 4
Вам нужно расширить AuthenticationEntryPoint
, чтобы выполнить настройку на основе исключений.
@ControllerAdvice
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
// 401
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
@ExceptionHandler (value = {AccessDeniedException.class})
public void commence(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
// 401
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization Failed : " + accessDeniedException.getMessage());
}
}
Укажите указанный выше пользовательский AuthenticationEntryPoint в вашем SecurityConfig, как показано ниже:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity (prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new MyAuthenticationEntryPoint());
}
}
Ответ 5
Простой подход в Spring Boot 2 с использованием лямбда-выражений:
@Override
public void configure(HttpSecurity http) throws Exception {
http.
...
.exceptionHandling()
.authenticationEntryPoint((request, response, e) -> {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json");
response.getWriter().write("{ \"error\": \"You are not authenticated.\" }");
})
...
}