在Spring Security中区分401(未经授权)/ 403(禁止)在Global ExceptionException中的拒绝异常

mcdcgff0  于 5个月前  发布在  Spring
关注(0)|答案(1)|浏览(59)

我在Sping Boot 应用程序中实现了Spring Security,默认情况下,它会正确地抛出401(未经授权)或403(禁止),这样下面的单元测试就可以工作了:

@Test
void notLoggedIn() throws Exception {
    mockMvc.perform(get("/service")) //
            .andExpect(status().isUnauthorized()); // 401 is returned and caught
}

void loggedInWithIncorrectRole() throws Exception {
    mockMvc.perform(get("/service").with(user("someuser").roles("invalidRole"))) //
            .andExpect(status().isForbidden());    // 403 is returned and caught
}

字符串
然后我添加了一个@ControllerAdvice全局ExceptionHandler来捕获应用程序抛出的所有异常。现在,所有的安全异常都以org.springframework.security.access.AccessDeniedException: Access Denied的形式出现在ExceptionHandler中。在这个异常中,401和403都被合并了。

@ControllerAdvice
public class AppExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception e, WebRequest request) {
        log.error(e);
        if ("org.springframework.security.access.AccessDeniedException".equals(e.getClass().getName())) {
            // This will catch BOTH 401 and 403, no distinction
            return new ResponseEntity<ErrorResponse>(HttpStatus.UNAUTHORIZED);
        } else {
            return ResponseEntity.internalServerError()
                    .body(new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, "Error occurred));
        }
    }


堆栈跟踪总是

org.springframework.security.access.AccessDeniedException: Access Denied
    at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:256)
    at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:197)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:717)


如何区分?

7gcisfzg

7gcisfzg1#

@ControllerAdvice无法处理BadCredentialsException,当没有找到用户或不正确的凭据时会抛出BadCredentialsException,因为尚未调用控制器方法。
在这种情况下,BasicAuthenticationEntryPoint会被触发。如果你想记录所有失败的尝试,你可以创建一个自定义入口点,如下所示:

public class BasicSecurityEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        // TODO: Add your logic here to log the failed attempts
    }

}

字符串
确保正确配置:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .httpBasic(c -> c.authenticationEntryPoint(new BasicSecurityEntryPoint()));
    return httpSecurity.build();
}

相关问题