1、access_token的有效期
我們知道 token的有效期,是控制登陸一次能訪問多長時間微服務。那么access_token一般設置多長時間比較好呢?因為有access_token可以直接訪問微服務,而不需要再次認證,所以access_token不建議設置很長的有效期,一般為一到兩個小時,因為access_token一旦泄漏,風險很高。但是這樣又會有一個問題,access_token過期了,而session沒有過期,這樣會導致,我們還是登陸的狀態,但是無法訪問微服務了。
將access_toen過期時間設置為10s,后訪問如下

2、OAuth2的refresh_token
為了解決access_token過期的問題,OAuth2協議為我們提供了refresh_token。我們在配置支持refresh_token后,在通過密碼模式和授權碼模式獲取access_token的同時,也會發給我們一個refresh_token。在當access_token過期后,客戶端應用可以拿着refresh_token和clientId、clientSecret去授權服務器獲取新的access_token,在這個過程中不需要用戶再次輸入用戶名和密碼。因為refresh_token要配合clientId、clientSecret一起使用才有效,所以就算泄漏了refresh_token也不要緊,保護好clientSecret就好了。因此refresh_token可以設置較長的時間,一般與認證服務器session過期時間設置一致即可。

3.1、配置授權服務器支持refresh_token
3.1.1、數據庫配置authorized_grant_types添加refresh_token,refresh_token_validity設置refresh_token過期時間,在使用密碼或授權碼模式申請令牌是refresh_token會一起返回

配置完成后,通過http請求工具,測試獲取令牌中已經存在refresh_token

3.1.2、
/** * 配置授權服務器終端的非安全特征 * authenticationManager 校驗用戶信息是否合法 * tokenStore:token存儲 * userDetailsService:配合刷新令牌使用 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager).tokenStore(new JdbcTokenStore(dataSource)) .userDetailsService(userDetailsService); }
3.2、客戶端應用改造access_token過期時,可以刷新令牌
SessionTokenFilter中獲取出token是要判斷access_token是否過期,過期執行刷新令牌操作
/** * 將session中的token取出放到請求頭中 * * @author caofanqi * @date 2020/2/6 0:34 */ @Slf4j @Component public class SessionTokenFilter extends ZuulFilter { private RestTemplate restTemplate = new RestTemplate(); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); TokenInfoDTO token = (TokenInfoDTO) request.getSession().getAttribute("token"); if (token != null) { String accessToken = token.getAccess_token(); //判斷access_token是否過期,需要刷新令牌 if (token.isExpires()){ String oauthTokenUrl = "http://gateway.caofanqi.cn:9010/token/oauth/token"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.setBasicAuth("webApp", "123456"); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.set("grant_type", "refresh_token"); params.set("refresh_token", token.getRefresh_token()); HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers); ResponseEntity<TokenInfoDTO> refreshTokenResult = restTemplate.exchange(oauthTokenUrl, HttpMethod.POST, httpEntity, TokenInfoDTO.class); request.getSession().setAttribute("token", refreshTokenResult.getBody().init()); accessToken = refreshTokenResult.getBody().getAccess_token(); log.info("refresh_token......"); } requestContext.addZuulRequestHeader("Authorization","bearer " + accessToken); } return null; } }
啟動個項目,測試

4、refresh_token過期了怎么辦
雖然refresh_token的有效期比較長,但還是會過期的。如果refresh_token也過期了怎么處理呢?下面提供兩種方案,具體使用那種,根據需求來決定。
4.1、refresh_token過期了,不管session過沒過期,強制退出,從新輸用戶名密碼進行認證。
4.1.1、修改SessionTokenFilter,刷新token失敗后,返回錯誤信息供頁面處理

4.1.2、修改index.html,獲取異常,如果是token刷新失敗,直接退出登陸
//攔截全局錯誤響應 ,使用方法https://www.runoob.com/jquery/ajax-ajaxerror.html
$(document).ajaxError(function(event, jqXHR, options, errorMsg) {
//jqXHR打印如下 alert(JSON.stringify(jqXHR));
//{"readyState":4,"responseText":"{\"message\":\"refresh fail\"}","responseJSON":{"message":"token refresh fail"},"status":500,"statusText":"error"}
if (jqXHR.status == 500 && jqXHR.responseJSON.message == 'token refresh fail') {
alert("登陸信息以失效");
//方案1、退出,重新登陸,走認證流程
logout();
});
//攔截全局錯誤響應 ,使用方法https://www.runoob.com/jquery/ajax-ajaxerror.html
$(document).ajaxError(function(event, jqXHR, options, errorMsg) {
//jqXHR打印如下 alert(JSON.stringify(jqXHR));
//{"readyState":4,"responseText":"{\"message\":\"refresh fail\"}","responseJSON":{"message":"token refresh fail"},"status":500,"statusText":"error"}
if (jqXHR.status == 500 && jqXHR.responseJSON.message == 'token refresh fail') {
alert("登陸信息以失效");
//方案1、退出,重新登陸,走認證流程
//logout();
//方案2、去認證服務器認證
location.href = "http://auth.caofanqi.cn:9020/oauth/authorize?" +
"client_id=webApp&" +
"redirect_uri=http://web.caofanqi.cn:9000/oauth/callback&" +
"response_type=code&" +
"state=abc";
}
});
5、基於session的SSO的優缺點及適用場景
5.1、優點
安全:所有的token信息都是存放在session中,在瀏覽器里只有一個JSESSIONID,只要做好防session固定攻擊,一般不會有什么風險。
可控性高:token和session存在了數據庫,可控性較高。
跨域:客戶端應用部署在哪個域名下,都可以直接跟認證服務器交互。
5.2、缺點
復雜度高:各種過期時間較多,兩個session(客戶端和認證服務器),兩個token(access_token、refresh_token),必須請求每個東西是干什么的,過期后對系統會產生什么樣的行為,怎么進行處理,都要清楚。
性能低:session、token的存取都會占用服務器資源,用戶量很大的情況下,會產生各種問題。
5.3、適用場景
系統用戶百萬以下(token表的性能可以得到保證)。
