繁体   English   中英

Spring Security 匿名 401 而不是 403

[英]Spring Security anonymous 401 instead of 403

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

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

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

从 Spring Boot 2 开始,Http401AuthenticationEntryPoint 类已被删除(参见Spring Boot 问题 10725 )。

而不是 Http401AuthenticationEntryPoint 使用 HttpStatusEntryPoint 和 HttpStatus.UNAUTHORIZED:

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

使用 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"

因此,在您的安全配置中,您定义并自动装配了一个类的 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());

我在这里有解决方案:

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM