spring security oauth2 自定义invali
框架
springboot+security+oauth2
问题
默认token过期的返回格式是这样的
{
"error": "invalid_token",
"error_description": "Invalid access token: XXXXXXXXXXXXXXXXXXXXXXX"
}
通常情况下,这与我们自己定义的数据返回的response格式不统一,不方便客户端做统一处理。如果要改成自己的格式的,需要怎么做呢?
效果
修改格式前
{
"error": "invalid_token",
"error_description": "Invalid access token: XXXXXXXXXXXXXXXXXXXXXXX"
}
修改格式后
{
"errCode": -1,
"errorMsg": "登陆凭证过期"
}
处理流程
1.新建或者打开security的继承自ResourceServerConfigurerAdapter的配置类(一般情况都已经创建了这个类),添加如下代码
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
authenticationEntryPoint.setExceptionTranslator(new CustomExceptionTranslator());
resources.authenticationEntryPoint(authenticationEntryPoint);
}
2.创建CustomExceptionTranslator类
public class CustomExceptionTranslator 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;
}
}
3.创建CustomOauthException类
@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 String getoAuth2ErrorCode() {
return oAuth2ErrorCode;
}
public int getHttpErrorCode() {
return httpErrorCode;
}
}
4.创建CustomOauthException类
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
public CustomOauthExceptionSerializer() {
super(CustomOauthException.class);
}
@Override
public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {
//value内容适当的做一些错误类型判断
gen.writeStartObject();
gen.writeObjectField("errCode",-1);
gen.writeObjectField("errorMsg","登陆凭证过期");
gen.writeEndObject();
}
}
打完收工
参考了这篇文章
参考源代码分析一波原因
由于我用的生成token的类是JdbcTokenStore
@Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
所以打开JdbcTokenStore类,可以看到有很多跟查询token相关的方法,逐个断点,尝试一个无效的token,最终断点命中在readAccessToken函数

果不其然,断点处会报错,并且这个函数最终accessToken会返回null。token_invalid的错误信息写入response的代码这里没有,说明在这个函数更上层,往外找到DefaultTokenServices.loadAuthentication

同理,经过几层的查看,最终找到OAuth2AuthenticationProcessingFilter.doFilter,可以看到这里在操作response

实际上这里框出来的authenticationEntryPoint对象就是我们前面提到的解决方案中,被我们替换成自定义ExceptionTranslator的authenticationEntryPoint,再看一下原始的commence方法里跟我们的操作response相关的代码做了些什么,debug进去,来到DefaultWebResponseExceptionTranslator.translate

translate方法里面一顿操作,最终会返回一个ResponseEntity,看一下ResponseEntity接受的三个参数,分别是一个会被转成json的对象,headers,http状态码。这三个东西熟悉http请求的就感觉很亲切了。也就是说我不管这里这一大坨代码做了什么,只要我按照我自己的要求返回一个ResponseEntity就可以。于是乎就有了开头解决方法中的替换成自己定义的ExceptionTranslator的解决思路。
现在框架有很多这种设计模式中的策略模式的设计来方便开发人员自己定义执行方法。类似DefaultXXXXXX的类,基本上都可以通过自己创建自定义对象来实现多样化的需求。当然我也是先百度了解决方案之后,再去看原因的,多看看原理,万一以后百度没作业给你抄的时候,你自己也能通过阅读代码,找到方法。