token是一个短活的东西,session多是3天,可是token可能就2个小时,此时就会出现一种状况,session还有效可是token失效了,此时再拿着这个token去调用其余微服务就会失败了。java
这就涉及到了OAuth2协议中的Refresh token,刷新令牌。刷新令牌说的是,无论你使用OAuth协议中的四种受权类型中的哪种(密码模式、受权码模式、简化模式、客户端模式),当token失效的时候,你能够拿着一个refresh_token去从新获取一个令牌,而不须要输入用户名密码。这样用户拿到一个短生命周期的access_token,和一个长生命周期的refresh_token,当access_token失效的时候,就拿refresh_token去换取一个新的access_token。git
理论上来讲,access_token能够设置一个很长的有效期,可是这样是不安全的,只要拿到access_token,就能够访问你的服务了,因此若是这样作,风险很高。而refresh_token就不同了,以下图能够看到,要想经过refresh_token换取access_token,须要携带clientId和clientSecret,认证服务器会校验他俩,这二者是保存在服务器端的,别人是拿不到的,因此即便别人拿到了refresh_token也是没用的。github
前面写了二十来篇文章了,历来没见到过refresh_token,这是由于,在客户端应用的表oauth_client_details 里面有一个字段 refresh_token_validity,若是不配这个字段,认证服务器就不会发refresh_token ,若是配了这个值,认证服务器就会在发access_token的同时,也发一个refresh_token。web
清空掉 access_token表(清掉以前的长有效期的token)spring
将客户端应用表的 admin 应用的token有效期设置为10秒,这样在登陆成功后,客户端应用的session和认证服务器的session还未失效的状况下,token就会失效数据库
从新登陆,获取一个有效期10秒的token,调用订单微服务,前几回调用因为token还没过时,调用成功了,10秒以后,token失效,调用订单微服务,返回401json
在客户端admin里,调用微服务前,会从session中取出token信息,放在请求头,此时的token多是已通过期的,在这里处理刷新令牌安全
改造前,是直接从session中拿出token,放在zuul请求头的,改造后的 SessionTokenFilter :服务器
package com.nb.security.admin; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletRequest; /** * 从session获取token,统一加到请求头中去 */ @Component public class SessionTokenFilter extends ZuulFilter { private RestTemplate restTemplate = new RestTemplate(); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); AccessToken accessToken = (AccessToken)request.getSession().getAttribute("token"); if(accessToken != null){ //token值,若是没过时就用Access_token String tokenValue = accessToken.getAccess_token(); //若是token已过时,拿refresh_token换取新的access_token if(accessToken.isExpired()){ String oauthServiceUrl = "http://gateway.nb.com:9070/token/oauth/token"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);//不是json请求 //网关的appId,appSecret,须要在数据库oauth_client_details注册 headers.setBasicAuth("admin","123456"); MultiValueMap<String,String> params = new LinkedMultiValueMap<>(); params.add("refresh_token",accessToken.getRefresh_token());//受权码 params.add("grant_type","refresh_token");//受权类型-刷新令牌 HttpEntity<MultiValueMap<String,String>> entity = new HttpEntity<>(params,headers); ResponseEntity<AccessToken> newToken = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST, entity, AccessToken.class); request.getSession().setAttribute("token",newToken.getBody().init());//调一下init方法,设置过时时间 //token值,若是过时了,就设置成新获取的token tokenValue = newToken.getBody().getAccess_token(); } requestContext.addZuulRequestHeader("Authorization","Bearer "+tokenValue); } return null; } }
AccessToken 类也改造了:session
package com.nb.security.admin; import lombok.Data; import java.time.LocalDateTime; import java.util.Date; /** * access_token * Created by: 李浩洋 on 2020-01-02 **/ @Data public class AccessToken { private String access_token; private String refresh_token; private String token_type; private Long expires_in; //过时时间 秒 private String scope; private LocalDateTime expireTime; //过时时间 //设置token的失效日期 public AccessToken init(){ //expires_in -3 秒,在token失效以前就失效 expireTime = LocalDateTime.now().plusSeconds(expires_in -3); return this; } //令牌是否过时 public boolean isExpired(){ return expireTime.isBefore(LocalDateTime.now()); } }
客户端应用表里,添加刷新令牌受权类型
重启四个微服务
从新登陆客户端应用,连续点击获取订单信息微服务,能够看到请求,隔10秒就有一个请求时间比较长的,这就是token失效后,又去认证服务器刷新令牌的请求耗时多。
认证服务器日志也有token失效记录
token表里也有的refresh_token
本节github :https://github.com/lhy1234/springcloud-security/tree/chapt-5-5-refreshtoken 若是对你帮助了,给个小星星吧。