Spring Security anonymous 401 вместо 403
у меня проблема с поведением по умолчанию в spring security с запросами авторизации, предоставленными с конфигурацией Java.
http
....
.authorizeRequests()
.antMatchers("/api/test/secured/*").authenticated()
когда я делаю вызов, например /api/test/secured/user
без входа в систему (с анонимным пользователем) он возвращает 403 Forbidden. Есть ли простой способ изменить статус на 401 Unauthorized, когда анонимный пользователь хочет получить защищенный authenticated()
или @PreAuthorize
ресурс?
3 ответов
у меня есть решение здесь:
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");
}
}
С безопасностью весны 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 Боб класса
так, например, с spring boot app:
@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);
вам нужно продлить 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());
}
}