Spring Security匿名401而不是403

Mat*_*ati 19 java spring spring-security spring-boot

我在Spring安全性中的默认行为存在问题,并且Java Config提供了授权请求.

http
       ....
       .authorizeRequests()
          .antMatchers("/api/test/secured/*").authenticated()
Run Code Online (Sandbox Code Playgroud)

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

le0*_*iaz 15

有了spring security 4.x,已经有了一个类

org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint 
Run Code Online (Sandbox Code Playgroud)

Spring boot还包括一个

org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint
Run Code Online (Sandbox Code Playgroud)

并且它们需要开发人员使用符合规范的401响应的两个好处要求必须设置头WWW-Authenticate,示例401响应可以是:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
                   error="invalid_token",
                   error_description="The access token expired"
Run Code Online (Sandbox Code Playgroud)

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

所以例如使用spring boot app:

@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());
}
Run Code Online (Sandbox Code Playgroud)

相关的是:

 .exceptionHandling().authenticationEntryPoint(securityException401EntryPoint());
Run Code Online (Sandbox Code Playgroud)

  • Spring Boot 2中的那个类[已被删除](https://github.com/spring-projects/spring-boot/issues/10715)我只是在我的应用程序中从Spring Boot 1.5.10源代码控制中重新创建它[这里](https://github.com/spring-projects/spring-boot/blob/v1.5.10.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/Http401AuthenticationEntryPoint. JAVA) (4认同)
  • 除了注入刚刚创建的bean之外,还可以直接调用该函数:.exceptionHandling()。authenticationEntryPoint(securityException401EntryPoint());。它将得到相同的实例,因为对@Bean注释的函数的调用已被代理。 (3认同)

Mei*_*orn 13

我在这里有解决方案:

http
   .authenticationEntryPoint(authenticationEntryPoint)
Run Code Online (Sandbox Code Playgroud)

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");
    }
}
Run Code Online (Sandbox Code Playgroud)


Tai*_*ong 10

从Spring Boot 2开始,类Http401AuthenticationEntryPoint已被删除(参见Spring Boot Issue 10725).

而不是Http401AuthenticationEntryPoint使用带HttpStatus.UNAUTHORIZED的HttpStatusEntryPoint:

http.exceptionHandling()
    .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
Run Code Online (Sandbox Code Playgroud)


Ham*_*eji 6

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.\" }");
            })
        ...
}
Run Code Online (Sandbox Code Playgroud)


Sah*_*bra 5

您需要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());
  }
}
Run Code Online (Sandbox Code Playgroud)

在您的 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());
  }
}
Run Code Online (Sandbox Code Playgroud)