阅读 955

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 用来解决认证过的用户访问无权限资源时的异常

    image-20210823192313538

    如果你想自定义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();
            }
        }
    }

    image-20210823192757483

    则可以生效,返回信息具体如下:

    image-20210823192828621

    如果想设置没有权限的自定义异常信息的话:

    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过期的话,就直接添加进去也可以

    image-20210823194105642

    image-20210823193825241

    以上为个人经验,希望能给大家一个参考

    原文链接:https://blog.csdn.net/zyt807/article/details/106806617

     伪原创工具 SEO网站优化  https://www.237it.com/ 


    文章分类
    百科问答
    版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
    相关推荐