mna*_*dev 3 spring exception spring-security spring-boot
我是 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));
}
Run Code Online (Sandbox Code Playgroud)
我的目标是在全球范围内处理所有异常,因此我使用@ControllerAdvice。
注意:我知道 @ControllerAdvice 不适用于从this和this控制器外部抛出的异常,因此我也遵循了这些链接中的建议。
RestAuthenticationEntryPoint.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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我配置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();
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
InvalidCredentialsException.java
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public class InvalidCredentialsException extends RuntimeException {
public InvalidCredentialsException(String errorMessage) {
super(errorMessage);
}
}
Run Code Online (Sandbox Code Playgroud)
经过调试,我发现resolver.resolveException(...)
RestAuthenticationEntryPoint和CustomExceptionHandler永远不会被调用。handleUnauthorizedError(..)
我希望 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
Run Code Online (Sandbox Code Playgroud)
Spring Security 有一个过滤器,称为 ,它将和ExceptionTranslationFilter
转换为响应。该过滤器捕获 spring security 过滤器链中的这些抛出的异常。AccessDeniedException
AuthenticationException
因此,如果您想返回自定义异常,您可以从这些类之一继承,而不是RuntimeException
添加自定义消息。
我只是想强调,而且永远不能说太多次:
从安全角度来看,在身份验证/授权方面,在生产应用程序中提供友好的错误消息通常是不好的做法。这些类型的消息可以使恶意行为者在尝试时受益,以便他们意识到自己做错了什么,并指导他们进行黑客攻击。
在测试环境中提供友好的消息可能没问题,但请确保在生产中禁用它们。在生产中,所有失败的身份验证尝试都建议返回 401,不带任何其他信息。在图形客户端中,应该显示通用错误消息,例如“无法验证”,但没有给出具体信息。
还:
像您所做的那样编写自定义安全性通常也是不好的做法。Spring Security 经过了 100000 个在生产环境中运行的应用程序的实际测试。通常不需要编写自定义过滤器来处理令牌和密码。Spring security 已经实现了过滤器来使用身份验证BASIC
和TOKEN/JWT
. 如果您实现非标准登录,一个错误可能会使您的应用程序面临巨大风险。
归档时间: |
|
查看次数: |
6463 次 |
最近记录: |