SpringBoot Security的自定义异常处理

 更新时间:2021年12月20日 09:30:10   作者:香酥蟹  
这篇文章主要介绍了SpringBoot Security的自定义异常处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

SpringBoot Security自定义异常

access_denied 方面异常

原异常

{
    "error": "access_denied",
    "error_description": "不允许访问"
}

现异常

{
    "success": false,
    "error": "access_denied",
    "status": 403,
    "message": "不允许访问",
    "path": "/user/get1",
    "timestamp": 1592378892768
}

实现

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 方面异常

原异常

{
    "error": "invalid_token",
    "error_description": "Invalid access token: 4eb58ecf-e66de-4155-9477-64a1c9805cc8"
}

现异常

{
    "success": false,
    "error": "invalid_token",
    "status": 401,
    "message": "Invalid access token: 8cd45925dbf6-4502-bd13-8101bc6e1d4b",
    "path": "/user/get1",
    "timestamp": 1592378949452
}

实现

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 方面异常(登陆出错)

原异常

{
    "error": "invalid_grant",
    "error_description": "用户名或密码错误"
}

现异常

{
    "success": false,
    "error": "invalid_grant",
    "status": 400,
    "message": "用户名或密码错误",
    "path": "/oauth/token",
    "timestamp": 1592384576019
}

实现

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);
    }
}

其他类

@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;
    }
}
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();
    }
}
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;
    }
}

补充

{
    "error": "invalid_client",
    "error_description": "Bad client credentials"
}

如果client_secret错误依然还是报错,如上内容,针对这个异常需要在如下方法中的addTokenEndpointAuthenticationFilter添加过滤器处理

  @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如下:

/**
 * @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

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

/**
 * @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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot多租户配置与实现示例

    SpringBoot多租户配置与实现示例

    本文详细介绍了在SpringBoot中实现多租户架构的方法和步骤,包括配置数据源、Hibernate拦截器、租户解析器等,以共享数据库、共享数据表的方式,确保数据隔离和安全性,感兴趣的可以了解一下
    2024-09-09
  • Mybatis配置文件之动态SQL配置备忘录

    Mybatis配置文件之动态SQL配置备忘录

    这篇文章主要介绍了Mybatis配置文件之动态SQL配置备忘录的相关资料,需要的朋友可以参考下
    2017-05-05
  • Spring注解@RestControllerAdvice原理解析

    Spring注解@RestControllerAdvice原理解析

    这篇文章主要介绍了Spring注解@RestControllerAdvice原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • 浅析MMAP零拷贝在RocketMQ中的运用

    浅析MMAP零拷贝在RocketMQ中的运用

    零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除传输数据在存储器之间不必要的中间拷贝次数,从而有效地提高数据传输效率,这篇文章主要介绍了MMAP零拷贝在RocketMQ中的运用,需要的朋友可以参考下
    2022-07-07
  • Javaweb接收表单数据并处理中文乱码

    Javaweb接收表单数据并处理中文乱码

    这篇文章主要介绍了Javaweb接收表单数据并处理中文乱码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • java商城项目实战之购物车功能实现

    java商城项目实战之购物车功能实现

    这篇文章主要为大家详细介绍了java商城项目实战之购物车功能实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • 详解SpringIOC容器相关知识

    详解SpringIOC容器相关知识

    这篇文章主要记录自己在狂神说java中的学习情况,文章里有自己学习的理解和扩展,新手难免有理解偏差或者错误,恳请大佬指正下,需要的朋友可以参考下
    2021-05-05
  • 深入理解Spring事务及传播机制之原理解析与实际应用

    深入理解Spring事务及传播机制之原理解析与实际应用

    Spring事务管理机制提供了多种传播行为,可以控制事务的范围和隔离级别,保证数据一致性和完整性。在实际应用中,需要根据具体业务场景选择合适的传播行为实现事务控制
    2023-04-04
  • 基于Scala和Java方法的相互调用

    基于Scala和Java方法的相互调用

    这篇文章主要介绍了Scala和Java方法的相互调用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • JDBC建立数据库连接的代码

    JDBC建立数据库连接的代码

    这篇文章主要为大家详细介绍了JDBC建立数据库连接的代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11

最新评论