SpringBoot Security的自定义异常处理

目录
  • SpringBoot Security自定义异常
    • access_denied 方面异常
    • Invalid access token 方面异常
    • Bad credentials 方面异常(登陆出错)
    • 其他类
    • 补充
  • SpringSecurity自定义响应异常信息

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

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

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

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

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

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

(0)

相关推荐

  • SpringBoot错误处理机制以及自定义异常处理详解

    上篇文章我们讲解了使用Hibernate Validation来校验数据,当校验完数据后,如果发生错误我们需要给客户返回一个错误信息,因此这节我们来讲解一下SpringBoot默认的错误处理机制以及如何自定义异常来处理请求错误. 一.SpringBoot默认的错误处理机制 我们在发送一个请求的时候,如果发生404 SpringBoot会怎么处理呢?我们来发送一个不存在的请求来验证一下看看页面结果.如下所示: 当服务器内部发生错误的时候,页面会返回什么呢? @GetMapping("/user/{

  • 浅谈SpringBoot 中关于自定义异常处理的套路

    在 Spring Boot 项目中 ,异常统一处理,可以使用 Spring 中 @ControllerAdvice 来统一处理,也可以自己来定义异常处理方案.Spring Boot 中,对异常的处理有一些默认的策略,我们分别来看. 默认情况下,Spring Boot 中的异常页面 是这样的: 我们从这个异常提示中,也能看出来,之所以用户看到这个页面,是因为开发者没有明确提供一个 /error 路径,如果开发者提供了 /error 路径 ,这个页面就不会展示出来,不过在 Spring Boot 中

  • 详解Springboot自定义异常处理

    背景 Springboot 默认把异常的处理集中到一个ModelAndView中了,但项目的实际过程中,这样做,并不能满足我们的要求.具体的自定义异常的处理,参看以下 具体实现 如果仔细看完spring boot的异常处理详解,并且研究过源码后,我觉得具体的实现可以不用看了... 重写定义错误页面的url,默认只有一个/error @Bean public EmbeddedServletContainerCustomizer containerCustomizer(){ return new E

  • Springboot如何实现自定义异常数据

    一.源码分析 自定义异常数据之前我们先看看一下源码 上述代码意思是如果你没有提供就使用springboot提供的类 这是springboot提供的异常属性类,我们想要自定义,自己的异常数据就只需要继承DefaultErrorAttribute类再重写getErrorAttribute()方法就可以达到我们想要的功能了 二.自定义异常数据 1.@controller 控制器(注入服务) 2.@service 服务(注入dao) 3.@repository dao(实现dao访问) 4.@compo

  • SpringBoot Security的自定义异常处理

    目录 SpringBoot Security自定义异常 access_denied 方面异常 Invalid access token 方面异常 Bad credentials 方面异常(登陆出错) 其他类 补充 SpringSecurity自定义响应异常信息 SpringBoot Security自定义异常 access_denied 方面异常 原异常 { "error": "access_denied", "error_description"

  • shiro与spring security用自定义异常处理401错误

    目录 shiro与spring security自定义异常处理401 背景 解决方案 SpringBoot整合Shiro自定义filter报错 No SecurityManager accessible to the calling code... 产生原因 解决办法 小结一下 shiro与spring security自定义异常处理401 背景 现在是前后端分离的时代,后端必然要统一处理返回结果,比如定义一个返回对象 public class ResponseData<T> { /** *

  • SpringBoot详解实现自定义异常处理页面方法

    目录 1.相关介绍 2.代码实现 3.运行测试 1.相关介绍 当发生异常时, 跳转到我们自定义的异常处理页面. SpringBoot中只需在静态资源目录下创建一个error文件夹, 并把异常处理页面放入其中, 页面的命名与异常错误代码对应, 如404.html, 500.html. 5xx.html可以对应所有错误代码为5开头的错误 默认静态资源目录为类路径(resources)下的: /static /public /resources /META-INF/resources 2.代码实现 H

  • SpringBoot+Hibernate实现自定义数据验证及异常处理

    目录 前言 Hibernate实现字段校验 自定义校验注解 使用AOP处理校验异常 全局异常类处理异常 前言 在进行 SpringBoot 项目开发中,经常会碰到属性合法性问题,而面对这个问题通常的解决办法就是通过大量的 if 和 else 判断来解决的,例如: @PostMapping("/verify") @ResponseBody public Object verify(@Valid User user){ if (StringUtils.isEmpty(user.getNam

  • SpringBoot Security从入门到实战示例教程

    目录 前言 入门 测试接口 增加依赖 自定义配置 配置密码加密方式 配置AuthenticationManagerBuilder 认证用户.角色权限 配置HttpSecurity Url访问权限 自定义successHandler 自定义failureHandler 自定义未认证处理 自定义权限不足处理 自定义注销登录 前后端分离场景 提供登录接口 自定义认证过滤器 鉴权 1.注解鉴权 2.自定义Bean动态鉴权 3.扩展默认方法自定义扩展根对象SecurityExpressionRoot 登出

  • SpringBoot接口如何统一异常处理

    目录 为什么要优雅的处理异常 实现案例 @ControllerAdvice异常统一处理 Controller接口 运行测试 进一步理解 @ControllerAdvice还可以怎么用? @ControllerAdvice是如何起作用的(原理)? 为什么要优雅的处理异常 如果我们不统一的处理异常,经常会在controller层有大量的异常处理的代码, 比如: @Slf4j @Api(value = "User Interfaces", tags = "User Interfac

  • Springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    前言: 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同.按照这个含义,最终的含义就是对数据库的影响只能是一次性的,不能重复处理.如何保证其幂等性,通常有以下手段: 1:数据库建立唯一性索引,可以保证最终插入数据库的只有一条数据 2:token机制,每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断toke

  • SpringBoot Security前后端分离登录验证的实现

    最近一段时间在研究OAuth2的使用,想整个单点登录,从网上找了很多demo都没有实施成功,也许是因为压根就不懂OAuth2的原理导致.有那么几天,越来越没有头绪,又不能放弃,转过头来一想,OAuth2是在Security的基础上扩展的,对于Security自己都是一无所知,干脆就先研究一下Security吧,先把Security搭建起来,找找感觉. 说干就干,在现成的SpringBoot 2.1.4.RELEASE环境下,进行Security的使用. 简单的Security的使用就不说了,目前

随机推荐