SpringBoot Security的自定义异常处理
这篇文章主要介绍了SpringBoot Security的自定义异常处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
目录
SpringBoot Security自定义异常
access_denied 方面异常
Invalid access token 方面异常
Bad credentials 方面异常(登陆出错)
其他类
补充
SpringSecurity自定义响应异常信息
SpringBoot Security自定义异常
access_denied 方面异常
原异常
1 2 3 4 | { "error" : "access_denied" , "error_description" : "不允许访问" } |
现异常
1 2 3 4 5 6 7 8 | { "success" : false , "error" : "access_denied" , "status" : 403 , "message" : "不允许访问" , "path" : "/user/get1" , "timestamp" : 1592378892768 } |
实现
1 2 3 4 5 6 7 8 9 | public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) { // access_denied 方面异常 OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler = new OAuth2AccessDeniedHandler(); oAuth2AccessDeniedHandler.setExceptionTranslator( new CustomWebResponseExceptionTranslator()); resources.accessDeniedHandler(oAuth2AccessDeniedHandler); } } |
Invalid access token 方面异常
原异常
1 2 3 4 | { "error" : "invalid_token" , "error_description" : "Invalid access token: 4eb58ecf-e66de-4155-9477-64a1c9805cc8" } |
现异常
1 2 3 4 5 6 7 8 | { "success" : false , "error" : "invalid_token" , "status" : 401 , "message" : "Invalid access token: 8cd45925dbf6-4502-bd13-8101bc6e1d4b" , "path" : "/user/get1" , "timestamp" : 1592378949452 } |
实现
1 2 3 4 5 6 7 8 9 | public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) { // Invalid access token 方面异常 OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint(); authenticationEntryPoint.setExceptionTranslator( new CustomWebResponseExceptionTranslator()); resources.authenticationEntryPoint(authenticationEntryPoint); } } |
Bad credentials 方面异常(登陆出错)
原异常
1 2 3 4 | { "error" : "invalid_grant" , "error_description" : "用户名或密码错误" } |
现异常
1 2 3 4 5 6 7 8 | { "success" : false , "error" : "invalid_grant" , "status" : 400 , "message" : "用户名或密码错误" , "path" : "/oauth/token" , "timestamp" : 1592384576019 } |
实现
1 2 3 4 5 6 7 8 9 10 11 | public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.userDetailsService(detailsService) .tokenStore(memoryTokenStore()) .exceptionTranslator( new CustomWebResponseExceptionTranslator()) .authenticationManager(authenticationManager) //接收GET和POST .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); } } |
其他类
1 2 3 4 5 6 7 8 9 10 11 | @Getter @JsonSerialize (using = CustomOauthExceptionSerializer. class ) public class CustomOauthException extends OAuth2Exception { private String oAuth2ErrorCode; private int httpErrorCode; public CustomOauthException(String msg, String oAuth2ErrorCode, int httpErrorCode) { super (msg); this .oAuth2ErrorCode = oAuth2ErrorCode; this .httpErrorCode = httpErrorCode; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> { private static final long serialVersionUID = 2652127645704345563L; public CustomOauthExceptionSerializer() { super (CustomOauthException. class ); } @Override public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); gen.writeObjectField( "success" , false ); gen.writeObjectField( "error" ,value.getOAuth2ErrorCode()); gen.writeObjectField( "status" , value.getHttpErrorCode()); gen.writeObjectField( "message" , value.getMessage()); gen.writeObjectField( "path" , request.getServletPath()); gen.writeObjectField( "timestamp" , ( new Date()).getTime()); if (value.getAdditionalInformation()!= null ) { for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) { String key = entry.getKey(); String add = entry.getValue(); gen.writeObjectField(key, add); } } gen.writeEndObject(); } } |
1 2 3 4 5 6 7 8 9 10 11 | public class CustomWebResponseExceptionTranslator extends DefaultWebResponseExceptionTranslator { @Override public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception { ResponseEntity<OAuth2Exception> translate = super .translate(e); OAuth2Exception body = translate.getBody(); CustomOauthException customOauthException = new CustomOauthException(body.getMessage(),body.getOAuth2ErrorCode(),body.getHttpErrorCode()); ResponseEntity<OAuth2Exception> response = new ResponseEntity<>(customOauthException, translate.getHeaders(), translate.getStatusCode()); return response; } } |
补充
1 2 3 4 | { "error" : "invalid_client" , "error_description" : "Bad client credentials" } |
如果client_secret错误依然还是报错,如上内容,针对这个异常需要在如下方法中的addTokenEndpointAuthenticationFilter添加过滤器处理
1 2 3 4 5 6 7 8 9 10 | @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) { oauthServer // 开启/oauth/token_key验证端口无权限访问 .tokenKeyAccess( "permitAll()" ) // 开启/oauth/check_token验证端口认证权限访问 .checkTokenAccess( "isAuthenticated()" ) .addTokenEndpointAuthenticationFilter( null ) .allowFormAuthenticationForClients(); } |
SpringSecurity自定义响应异常信息
此处的异常信息设置的话,其中还是有坑的,比如你想自定义token过期信息,无效token这些,如果按照SpringSecurity的设置是不会生效的,需要加到资源的配置中。
如果只是SpringSecurity的话,只需要实现AccessDeniedHandler和AuthenticationEntryPoint这2个接口就可以了。他们都是在ExceptionTranslationFilter中生效的。
AuthenticationEntryPoint
用来解决匿名用户访问无权限资源时的异常ruAccessDeineHandler
用来解决认证过的用户访问无权限资源时的异常
如果你想自定义token过期的话,需要实现AuthenticationEntryPoint这个接口,因为token过期了,访问的话也算是匿名访问。
但是SpringSecurity的过滤器链中其实是有顺序的,校验token的OAuth2AuthenticationProcessingFilter在它前面,导致一直没有办法生效,所有需要添加到资源的配置上,demo如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /** * @author WGR * @create 2021/8/23 -- 16:52 */ @Component public class SimpleAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws ServletException { Throwable cause = authException.getCause(); try { if (cause instanceof InvalidTokenException) { Map map = new HashMap(); map.put( "error" , "无效token" ); map.put( "message" , authException.getMessage()); map.put( "path" , request.getServletPath()); map.put( "timestamp" , String.valueOf( new Date().getTime())); response.setContentType( "application/json" ); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); try { ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(response.getOutputStream(), map); } catch (Exception e) { throw new ServletException(); } } } catch (Exception e) { e.printStackTrace(); } } } |
则可以生效,返回信息具体如下:
如果想设置没有权限的自定义异常信息的话:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * @author WGR * @create 2021/8/23 -- 17:09 */ @Component public class SimpleAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { Map map = new HashMap(); map.put( "message" , "无权操作" ); map.put( "path" , request.getServletPath()); map.put( "timestamp" , String.valueOf( new Date().getTime())); response.setContentType( "application/json" ); response.setStatus(HttpServletResponse.SC_FORBIDDEN); try { ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(response.getOutputStream(), map); } catch (Exception e) { throw new ServletException(); } } } |
把它设置到springsecurity中,添加进去就可以了,如果不是想要捕获token过期的话,就直接添加进去也可以
以上为个人经验,希望能给大家一个参考
原文链接:https://blog.csdn.net/zyt807/article/details/106806617
伪原创工具 SEO网站优化 https://www.237it.com/