当前位置: 首页 > 知识库问答 >
问题:

如何处理Spring Security中过滤器引发的自定义异常

滕翔飞
2023-03-14

我是Spring Security的新手。

我有一段代码,我检查是否在请求中传递了授权头,如果缺少,我抛出一个异常。

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对于从这个和这个抛出的控制器之外的异常不起作用,所以我也遵循了这些链接中的建议。

重新验证入口点。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);
    }
}

xception.java

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

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

}

调试后,我发现分解器。resolveException(…) 和处理授权错误(…)从未被调用。

我希望以优雅的方式处理抛出新的InvalidCredentialsException(“缺少身份验证令牌”),并在响应中显示一个不错的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

共有1个答案

宓季同
2023-03-14

Spring Security有一个名为ExceptionTranslationFilter的过滤器,它将AccessDeniedExceptionAuthentiationException转换为响应。此过滤器捕获Spring Security过滤器链中抛出的这些异常。

因此,如果您想返回一个自定义异常,可以从这些类中的一个继承,而不是从运行时异常(RuntimeException)中继承,并添加一条自定义消息。

我只想强调一下,这一点无论说多少次都不为过:

从安全的角度来看,在生产应用程序中提供友好的错误消息通常是不好的做法。这些类型的消息可以使恶意行为者在尝试时受益,以便他们意识到自己做错了什么并指导他们进行黑客攻击尝试。

在测试环境中提供友好的消息可能是可以的,但请确保它们在生产中被禁用。在生产中,所有失败的身份验证尝试都建议返回没有附加信息的401。在图形客户端中,应显示通用错误消息,例如“身份验证失败”,没有给定的细节。

还:

像您所做的那样编写自定义安全性在一般情况下也是不好的做法。Spring Security性经过了100000个在生产环境中运行它的应用程序的战斗测试。通常不需要编写自定义过滤器来处理令牌和密码。Spring Security已经使用BASIC身份验证和TOKEN/JWT等标准实现了过滤器来处理安全性和身份验证。如果您实现了非标准登录,一个错误可能会使您的应用程序面临巨大风险。

  • Spring用户名密码认证
  • SpringOauth2认证
 类似资料:
  • 我有一个验证令牌的自定义筛选器tokenLoginFilter 我的Spring安全 令牌登录过滤器 在authenticationmanager . authenticate调用中,我从TokenAuthenticationProvider中抛出了BadCredentialsException 令牌身份验证提供程序 Tomcat将此BadCredentialsException解释为错误代码50

  • 我正试图自己解决参数问题。 我可以很容易地从NativeWebRequest获取输入参数,并将它们分派到相应的自定义@Param注释参数中。 问题是我还想在这方面做一些语法检查/验证。但如果我在“resolveArgument”中抛出异常,则会向用户显示完整的堆栈跟踪。这将是过度和不安全的。我只想向用户返回一条JSON格式的消息,以显示哪个输入参数语法有错误。

  • 问题内容: 我想使用通用方式来管理5xx错误代码,特别是当整个Spring应用程序中的db关闭时。我想要一个漂亮的错误json而不是堆栈跟踪。 对于控制器,我有一个针对不同异常的类,这也捕获了db在请求中间停止的情况。但这并不是全部。我也碰巧有一个自定义扩展名,在那里,当我打电话给我时,它将不受的管理。我在网上阅读了几件事,这只会让我更加困惑。 所以我有很多问题: 我是否需要实施自定义过滤器?我找

  • 我正在使用Spring4和Tomcat。问题是有时我不得不在我的筛选器中抛出一个(自定义的)RuntimeException(控件甚至还没有到达控制器)。问题是,由于我没有抛出tomcat理解的异常,它被转换为500(内部服务器错误)。我相信403禁止会比500好(对于我的定制例外)。我查看了和注释。但只有当控件到达控制器时,这些才起作用。 到目前为止,我在筛选器的中将状态手动设置为403。有没有

  • 我在我的Android应用程序中使用了一个自定义的和改型客户端,它在某些特定情况下会引发异常。我正试着用Kotlin Coroutines来让它工作。

  • 我是Spring Boot的新手,但在阅读了Spring Boot REST中关于异常handlig的帖子和博客几个小时之后,我决定在这里写一些关于如何处理自定义转换器抛出的异常的文章。 我开发了一个基于Spring Boot的小型REST应用程序,简单地从IntelliJ生成。示例性方法如下所示 使用、和的所有类型的处理异常,我无法捕获控制器中转换器的异常,从而将json错误响应原始字段的“st