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());
  }
}