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

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

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

  1. public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  2. private static final String BEARER = "Bearer";
  3. public TokenAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
  4. super(requiresAuthenticationRequestMatcher);
  5. }
  6. @Override
  7. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  8. throws AuthenticationException, IOException, ServletException {
  9. String username = request.getParameter("username");
  10. String authorization = request.getHeader("AUTHORIZATION");
  11. if (!request.getRequestURI().equals(UniversalConstants.LOGIN_PATH)) {
  12. if (authorization == null || authorization.length() == 0 || !authorization.startsWith(BEARER)) {
  13. throw new InvalidCredentialsException("Missing authentication token"); //<-----------------
  14. }
  15. }
  16. String password = request.getParameter("password");
  17. return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(username, password));
  18. }

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

  1. @Component("restAuthenticationEntryPoint")
  2. public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
  3. public RestAuthenticationEntryPoint() {
  4. System.out.println("RestAuthenticationEntryPoint");
  5. }
  6. @Autowired
  7. @Qualifier("handlerExceptionResolver")
  8. private HandlerExceptionResolver resolver;
  9. @Override
  10. public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
  11. resolver.resolveException(request, response, null, authException);
  12. }
  13. }

以下是我配置authenticationentrypoint的方式:

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint()).and().cors().and().csrf().disable().exceptionHandling().defaultAuthenticationEntryPointFor(new RestAuthenticationEntryPoint(), PROTECTED_URLS)
  4. .and().authenticationProvider(customAuthenticationProvider())
  5. .addFilterBefore(tokenAuthenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests()
  6. .requestMatchers(PROTECTED_URLS).authenticated().and().formLogin().disable().httpBasic().disable();
  7. }

customexceptionhandler.java

  1. @ControllerAdvice
  2. public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
  3. @ExceptionHandler({InvalidCredentialsException.class, AuthenticationException.class})
  4. public ResponseEntity<ErrorResponse> handleUnauthorizedError(InvalidCredentialsException e, WebRequest request) {
  5. String errorMessage = e.getLocalizedMessage();
  6. ErrorResponse errorResponse = new ErrorResponse(errorMessage, null);
  7. return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED);
  8. }
  9. }

invalidcredentialsexception.java

  1. @ResponseStatus(HttpStatus.UNAUTHORIZED)
  2. public class InvalidCredentialsException extends RuntimeException {
  3. public InvalidCredentialsException(String errorMessage) {
  4. super(errorMessage);
  5. }
  6. }

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

  1. 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/**']
  2. 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/**'
  3. 2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
  4. 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**']
  5. 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**'
  6. 2021-05-20 17:41:29.986 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
  7. 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'
  8. 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'
  9. 2021-05-20 17:41:29.988 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
  10. 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'
  11. 2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.s.HttpSessionRequestCache : saved request doesn't match
  12. 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'
  13. 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'
  14. 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/**']
  15. 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/**'
  16. 2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
  17. 2021-05-20 17:41:29.989 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.w.u.matcher.NegatedRequestMatcher : matches = true
  18. 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
  19. 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.
  20. 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
  21. 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
  22. com.spring.fieldSecurity.Exceptions.InvalidCredentialsException: Missing authentication token
  23. at com.spring.fieldSecurity.Service.TokenAuthenticationFilter.attemptAuthentication(TokenAuthenticationFilter.java:44) ~[classes/:na]
  24. at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
  25. .
  26. . // more error trace here
  27. .
  28. 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/**']
  29. 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/**'
  30. 2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
  31. 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**']
  32. 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**'
  33. 2021-05-20 17:41:38.034 DEBUG 24808 --- [nio-8181-exec-3] o.s.s.web.util.matcher.OrRequestMatcher : matched
  34. 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
  35. 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}
  36. 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)
  37. 2021-05-20 17:41:38.035 DEBUG 24808 --- [nio-8181-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
  38. 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]
  39. 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)...]
  40. 2021-05-20 17:41:38.726 DEBUG 24808 --- [nio-8181-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
  41. 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身份验证

相关问题