框架
springboot+security+oauth2
問題
默認(rèn)token過期的返回格式是這樣的
{
"error": "invalid_token",
"error_description": "Invalid access token: XXXXXXXXXXXXXXXXXXXXXXX"
}
通常情況下闽巩,這與我們自己定義的數(shù)據(jù)返回的response格式不統(tǒng)一,不方便客戶端做統(tǒng)一處理征炼。如果要改成自己的格式的,需要怎么做呢疆柔?
效果
修改格式前
{
"error": "invalid_token",
"error_description": "Invalid access token: XXXXXXXXXXXXXXXXXXXXXXX"
}
修改格式后
{
"errCode": -1,
"errorMsg": "登陸憑證過期"
}
處理流程
1.新建或者打開security的繼承自ResourceServerConfigurerAdapter的配置類(一般情況都已經(jīng)創(chuàng)建了這個(gè)類),添加如下代碼
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
authenticationEntryPoint.setExceptionTranslator(new CustomExceptionTranslator());
resources.authenticationEntryPoint(authenticationEntryPoint);
}
2.創(chuàng)建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.創(chuàng)建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.創(chuàng)建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內(nèi)容適當(dāng)?shù)淖鲆恍╁e(cuò)誤類型判斷
gen.writeStartObject();
gen.writeObjectField("errCode",-1);
gen.writeObjectField("errorMsg","登陸憑證過期");
gen.writeEndObject();
}
}
打完收工
參考了這篇文章
參考源代碼分析一波原因
由于我用的生成token的類是JdbcTokenStore
@Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
所以打開JdbcTokenStore類弊攘,可以看到有很多跟查詢token相關(guān)的方法,逐個(gè)斷點(diǎn)姑曙,嘗試一個(gè)無效的token襟交,最終斷點(diǎn)命中在readAccessToken函數(shù)
果不其然,斷點(diǎn)處會(huì)報(bào)錯(cuò)伤靠,并且這個(gè)函數(shù)最終accessToken會(huì)返回null捣域。token_invalid的錯(cuò)誤信息寫入response的代碼這里沒有,說明在這個(gè)函數(shù)更上層宴合,往外找到DefaultTokenServices.loadAuthentication
同理焕梅,經(jīng)過幾層的查看,最終找到OAuth2AuthenticationProcessingFilter.doFilter,可以看到這里在操作response
實(shí)際上這里框出來的authenticationEntryPoint對象就是我們前面提到的解決方案中形纺,被我們替換成自定義ExceptionTranslator的authenticationEntryPoint,再看一下原始的commence方法里跟我們的操作response相關(guān)的代碼做了些什么丘侠,debug進(jìn)去,來到DefaultWebResponseExceptionTranslator.translate
translate方法里面一頓操作逐样,最終會(huì)返回一個(gè)ResponseEntity蜗字,看一下ResponseEntity接受的三個(gè)參數(shù),分別是一個(gè)會(huì)被轉(zhuǎn)成json的對象脂新,headers挪捕,http狀態(tài)碼。這三個(gè)東西熟悉http請求的就感覺很親切了争便。也就是說我不管這里這一大坨代碼做了什么级零,只要我按照我自己的要求返回一個(gè)ResponseEntity就可以。于是乎就有了開頭解決方法中的替換成自己定義的ExceptionTranslator的解決思路滞乙。
現(xiàn)在框架有很多這種設(shè)計(jì)模式中的策略模式的設(shè)計(jì)來方便開發(fā)人員自己定義執(zhí)行方法奏纪。類似DefaultXXXXXX的類,基本上都可以通過自己創(chuàng)建自定義對象來實(shí)現(xiàn)多樣化的需求斩启。當(dāng)然我也是先百度了解決方案之后序调,再去看原因的,多看看原理,萬一以后百度沒作業(yè)給你抄的時(shí)候,你自己也能通過閱讀代碼兔簇,找到方法发绢。