【问题标题】:Spring Security anonymous 401 instead of 403Spring Security 匿名 401 而不是 403
【发布时间】:2015-08-19 00:24:16
【问题描述】:

Spring Security 中的默认行为与 Java Config 提供的授权请求有关。

http
       ....
       .authorizeRequests()
          .antMatchers("/api/test/secured/*").authenticated()

当我在没有登录(使用匿名用户)的情况下调用例如 /api/test/secured/user 时,它返回 403 Forbidden。当匿名用户想要通过authenticated()@PreAuthorize 资源获得保护时,是否有一种简单的方法可以将状态更改为 401 Unauthorized?

【问题讨论】:

标签: java spring spring-security spring-boot


【解决方案1】:

我有解决方案here:

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

【讨论】:

  • 坦克,这个解决方案也适用于我的问题!我想做相反的事情:将 401 更改为 403。它接缝 authenticationEntryPoint 已移至 httpBasic(),我要求对此进行编辑。
  • 我向那些想要自定义错误响应的人推荐这个解决方案。如果您满足于将状态从 403 更改为 401... 并使用默认的错误消息格式.. le0diaz 回答下面的作品。至于我的情况,我需要自定义错误响应格式 (JSON),而这个解决方案的想法就成功了。谢谢梅布隆!
  • @adil 检查 jhipster 项目
【解决方案2】:

使用 spring security 4.x 已经有一个类

org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint 

Spring boot 也包括一个

org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint

并且必须设置他们要求开发人员使用符合规范的 401 responses requires that header WWW-Authenticate 的两个好处,例如 401 响应可能是:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
                   error="invalid_token",
                   error_description="The access token expired"

因此,在您的安全配置中,您定义并自动装配一个类 bean

例如使用 spring boot 应用程序:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Bean
    public Http401AuthenticationEntryPoint securityException401EntryPoint(){

        return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
    }

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

相关行是:

 .exceptionHandling().authenticationEntryPoint(securityException401EntryPoint());

【讨论】:

  • 您可以直接调用函数:.exceptionHandling().authenticationEntryPoint(securityException401EntryPoint());,而不是注入您刚刚创建的 bean。它将获得相同的实例,因为对 @Bean 注释函数的调用已被代理。
  • Spring Boot 2 中的那个类 has been removed。我刚刚从 Spring Boot 1.5.10 源代码控制 here 在我的应用程序中重新创建了它
【解决方案3】:

您需要扩展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());
  }
}

在您的 SecurityConfig 中指定上述自定义 AuthenticationEntryPoint,如下所示:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity (prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.exceptionHandling()
        .authenticationEntryPoint(new MyAuthenticationEntryPoint());
  }
}

【讨论】:

    【解决方案4】:

    截至 Spring Boot 2 类 Http401AuthenticationEntryPoint 已被删除(请参阅Spring Boot Issue 10725)。

    使用带有 HttpStatus.UNAUTHORIZED 的 HttpStatusEntryPoint 代替 Http401AuthenticationEntryPoint:

    http.exceptionHandling()
        .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
    

    【讨论】:

      【解决方案5】:

      Spring Boot 2 中使用 lambda 表达式的简单方法:

      @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.\" }");
                  })
              ...
      }
      

      【讨论】:

        【解决方案6】:

        谁对工作机制感兴趣。
        如果你不设置http.exceptionHandling().authenticationEntryPoint() spring 将使用defaultAuthenticationEntryPoint() 和方法ExceptionHandlingConfigurer.createDefaultEntryPoint() 将返回new Http403ForbiddenEntryPoint()
        所以,只需创建Http401UnauthorizedEntryPoint()。以上回答了怎么做,没有重复。

        附:这是 Spring Security 5.2.5.RELEASE 的实际情况

        【讨论】:

          猜你喜欢
          • 2015-08-16
          • 2018-08-20
          • 2018-09-04
          • 2021-01-28
          • 2017-03-21
          • 2021-07-28
          • 2021-10-27
          • 2011-05-15
          • 2019-09-18
          相关资源
          最近更新 更多