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表的性能可以得到保证)。