spring cloud oauth2 自定义异常返回格式之认证服务器异常

2,228 阅读2分钟

本文主要记录如何自定义spring cloud oauth2认证服务器异常

想必用过spring cloud oauth2的都知道它默认的返回格式,在实际开发中不是很友好,通常情况下我们都是需要统一的数据返回格式,这样才可以不被前端大哥们吐槽

废话不过说直接开始正文了

自定义异常处理

  • 编辑LwWebResponseExceptionTranslator实现WebResponseExceptionTranslator,根据不同的异常返回不同的状态码
  • 将LwWebResponseExceptionTranslator注入认证服务器替换默认DefaultWebResponseExceptionTranslator实现

接下来看看代码,根据不同的项目有不同的实现方式,我这只是做一个简单的记录,只区别了权限异常和认证异常

@Slf4j
public class LwWebResponseExceptionTranslator implements WebResponseExceptionTranslator {

    private ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();

    @Override
    public ResponseEntity translate(Exception e) throws Exception {
    
        //获取异常栈信息
        Throwable[] throwables = this.throwableAnalyzer.determineCauseChain(e);

        //获取AuthenticationException异常信息,不存在则返回的是null
        Exception ase = (AuthenticationException) this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, throwables);
        if (ase != null) {
            log.error(ase.getMessage());
            return ResponseEntity.ok(R.failed(ResultStatus.AUTH_EXCEPTION.getCode(), ResultStatus.AUTH_EXCEPTION.getMsg()));
        }

     //获取AccessDeniedException异常信息,不存在则返回的是null
        ase = (AccessDeniedException) this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, throwables);
        if (ase != null) {
            log.error(ase.getMessage());
            return ResponseEntity.ok(R.failed(ResultStatus.ACCESS_DENIED_EXCEPTION.getCode(), ResultStatus.ACCESS_DENIED_EXCEPTION.getMsg()));
        }
        return ResponseEntity.ok(R.failed(e.getMessage()));
    }

}
// 这是响应实体类
@Builder
@ToString
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;


    @Getter
    @Setter
    private String code;

    @Getter
    @Setter
    private String msg;

    @Getter
    @Setter
    private T data;

    public static <T> R<T> ok() {
        return restResult(null, ResultStatus.SUCCESS.getCode(), "");
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, ResultStatus.SUCCESS.getCode(), "");
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, ResultStatus.SUCCESS.getCode(), msg);
    }

    public static <T> R<T> failed() {
        return restResult(null, ResultStatus.SUCCESS.getCode(), "");
    }

    public static <T> R<T> failed(String code, String msg) {
        return restResult(null, code, msg);
    }


    public static <T> R<T> failed(String msg) {
        return restResult(null, ResultStatus.FAIL.getCode(), msg);
    }

    public static <T> R<T> failed(T data) {
        return restResult(data, ResultStatus.FAIL.getCode(), "");
    }

    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, ResultStatus.FAIL.getCode(), msg);
    }

    public boolean isOk() { return this.code.equals(ResultStatus.SUCCESS.getCode()); }

    private static <T> R<T> restResult(T data, String code, String msg) {
        R<T> result = new R<>();
        result.setCode(code);
        result.setData(data);
        result.setMsg(msg);
        return result;
    }

}

自定义的LwWebResponseExceptionTranslator类就编写好了,在实际开发中,还可以通过自定义异常类继承OAuth2Exception来做到根据不同的异常返回不同的响应码,这样会更好一点
既然自定义的异常处理类已经完成,下面我们就要去将它替换原有的DefaultWebResponseExceptionTranslator

/**
 * 令牌访问端点配置
 *
 * @param endpoints
 */
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET)
            .authenticationManager(authenticationManagerBean)
            .tokenServices(tokenServices())
            .exceptionTranslator(new LwWebResponseExceptionTranslator()) //替换异常处理类
            .tokenStore(tokenStore)
            .userDetailsService(lwUserDetailsService);
}

效果展示

小知识: 本文响应实体类只有code,msg,data三个字段,但是实际返回多了一个ok字段,主要原因是R对象中我们有一个 public boolean isOk() { return this.code.equals(ResultStatus.SUCCESS.getCode()); }方法,判断当前对象是否成功状态,
我又使用到lombok插件,lombok布尔类型默认会在字段前面加is,比如 boolean ok,他的get和set方法就会是isOK(),lombok将我的方法默认成一个字段了,这也算是lombok的bug

image.png