如何在spring security中处理筛选器引发的自定义异常

mu0hgdu0  于 2021-10-10  发布在  Java
关注(0)|答案(1)|浏览(458)

我是Spring安全的新手。
我有一段代码,检查请求中是否传递了授权头,如果缺少,则抛出异常。

public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final String BEARER = "Bearer";

    public TokenAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
        super(requiresAuthenticationRequestMatcher);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
        String username = request.getParameter("username");
        String authorization = request.getHeader("AUTHORIZATION");

        if (!request.getRequestURI().equals(UniversalConstants.LOGIN_PATH)) {
            if (authorization == null || authorization.length() == 0 || !authorization.startsWith(BEARER)) {
                throw new InvalidCredentialsException("Missing authentication token"); //<-----------------
            }

        }

        String password = request.getParameter("password");
        return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }

我的目标是在全球范围内处理所有异常,因此我使用@controlleradvice。
注意:我知道@controlleradvice对于从this和this抛出的控制器之外的异常不起作用,所以我也遵循了这些链接中的建议。
重新验证EntryPoint.java

@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    public RestAuthenticationEntryPoint() {
        System.out.println("RestAuthenticationEntryPoint");
    }

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        resolver.resolveException(request, response, null, authException);

    }

}

以下是我配置authenticationentrypoint的方式:

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint()).and().cors().and().csrf().disable().exceptionHandling().defaultAuthenticationEntryPointFor(new RestAuthenticationEntryPoint(), PROTECTED_URLS)
            .and().authenticationProvider(customAuthenticationProvider())
            .addFilterBefore(tokenAuthenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests()
            .requestMatchers(PROTECTED_URLS).authenticated().and().formLogin().disable().httpBasic().disable();

}

customexceptionhandler.java

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({InvalidCredentialsException.class, AuthenticationException.class})
    public ResponseEntity<ErrorResponse> handleUnauthorizedError(InvalidCredentialsException e, WebRequest request) {
        String errorMessage = e.getLocalizedMessage();
        ErrorResponse errorResponse = new ErrorResponse(errorMessage, null);
        return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED);
    }
}

invalidcredentialsexception.java

@ResponseStatus(HttpStatus.UNAUTHORIZED)
public class InvalidCredentialsException extends RuntimeException {

    public InvalidCredentialsException(String errorMessage) {
        super(errorMessage);
    }

}

调试后,我发现 resolver.resolveException(...) 在重新验证入口点和 handleUnauthorizedError(..) 在CustomExceptionHandler中,永远不会接到呼叫。
我想处理 throw new InvalidCredentialsException("Missing authentication token") 以一种优雅的方式,并在响应中显示一个像样的json输出。任何帮助都将不胜感激。
编辑:堆栈跟踪

2021-05-20 17:41:29.985 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/public/**']
2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/user/hello'; against '/public/**'
2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/error**']
2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/user/hello'; against '/error**'
2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.security.web.FilterChainProxy        : /user/hello?username=user&password=user at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2021-05-20 17:41:29.988 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /user/hello' doesn't match 'DELETE /logout'
2021-05-20 17:41:29.988 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2021-05-20 17:41:29.988 DEBUG 24808 --- [nio-8181-exec-3] o.s.security.web.FilterChainProxy        : /user/hello?username=user&password=user at position 6 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.security.web.FilterChainProxy        : /user/hello?username=user&password=user at position 7 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.security.web.FilterChainProxy        : /user/hello?username=user&password=user at position 8 of 12 in additional filter chain; firing Filter: 'TokenAuthenticationFilter'
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/public/**']
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/user/hello'; against '/public/**'
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.NegatedRequestMatcher  : matches = true
2021-05-20 17:41:38.030 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7fb6b4e0
2021-05-20 17:41:38.030 DEBUG 24808 --- [nio-8181-exec-3] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2021-05-20 17:41:38.030 DEBUG 24808 --- [nio-8181-exec-3] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2021-05-20 17:41:38.033 ERROR 24808 --- [nio-8181-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

com.spring.fieldSecurity.Exceptions.InvalidCredentialsException: Missing authentication token
    at com.spring.fieldSecurity.Service.TokenAuthenticationFilter.attemptAuthentication(TokenAuthenticationFilter.java:44) ~[classes/:na]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
.
.   // more error trace here
.
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/public/**']
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/public/**'
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/error**']
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/error**'
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : matched
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.security.web.FilterChainProxy        : /error?username=user&password=user has an empty filter list
2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/error?username=user&password=user", parameters={masked}
2021-05-20 17:41:38.035 DEBUG 24808 --- [nio-8181-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2021-05-20 17:41:38.035 DEBUG 24808 --- [nio-8181-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2021-05-20 17:41:38.724 DEBUG 24808 --- [nio-8181-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [application/json] and supported [application/json, application/*+json, application/json, application/*+json]
2021-05-20 17:41:38.724 DEBUG 24808 --- [nio-8181-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [{timestamp=Thu May 20 17:41:38 IST 2021, status=500, error=Internal Server Error, message=, path=/us (truncated)...]
2021-05-20 17:41:38.726 DEBUG 24808 --- [nio-8181-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
2021-05-20 17:41:38.727 DEBUG 24808 --- [nio-8181-exec-3] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 500
31moq8wy

31moq8wy1#

spring security有一个过滤器,称为 ExceptionTranslationFilter 这就是 AccessDeniedExceptionAuthenticationException 我的回答是。此筛选器在spring安全筛选器链中捕获这些抛出的异常。
因此,如果要返回自定义异常,可以从这些类中的一个继承,而不是 RuntimeException 并添加自定义消息。
我只想强调一下,这句话永远不能说太多次:
从安全Angular 来看,在涉及身份验证/授权时,在生产应用程序中提供友好的错误消息通常是不好的做法。这些类型的消息可以使恶意参与者在尝试时受益,从而使他们意识到自己做错了什么,并引导他们进行黑客攻击。
在测试环境中提供友好的消息是可以的,但请确保在生产环境中禁用这些消息。在生产中,所有失败的身份验证尝试建议返回401,不包含任何附加信息。在图形化客户机中,应该显示通用错误消息,例如“身份验证失败”,而不显示任何特定信息。
也:
像您这样编写自定义安全性通常也是不好的做法。spring security经过了100000个应用程序的战斗测试,这些应用程序在生产环境中运行。通常不需要编写自定义筛选器来处理令牌和密码。SpringSecurity已经实现了过滤器,以使用诸如 BASIC 认证和 TOKEN/JWT . 如果实现非标准登录,一个bug可能会使应用程序面临巨大风险。
spring中的用户名和密码身份验证
spring中的oauth2身份验证

相关问题