前言
赠书活动马上就要结束啦!还没参与的小伙伴查看文章参与吧。
《赠书|感谢关注猿天地的朋友们》
上篇文章 Spring Cloud中如何保证各个微服务之间调用的安全性 我们介绍了各个微服务之间调用认证的方式以及原理。
今天我们继续接着上篇文章来聊一聊如何能够在调用方实现token的自动设置以及刷新。
我们的认证token是放在请求头中的,相对于把token放在请求参数中更为友好,对业务接口无侵入性。
但是这种方式如果需要自己设置token就麻烦了,如果是参数的形式,那么在调用的时候就把获取的token当做参数传就可以了。
House getHouseInfo(Long id, String token);
传参的方式不好的就是每个接口需要增加一个参数的定义:
/**
* 获取房产信息
* @param houseId 房产编号
* @return
*/
@GetMapping("/{houseId}/{token}")
public ResponseData hosueInfo(@PathVariable("houseId")Long houseId,@PathVariable("token")String token) {
return ResponseData.ok(houseService.getHouseInfo(houseId));
}
或者下面这种方式:
/**
* 获取房产信息
* @param houseId 房产编号
* @return
*/
@GetMapping("/")
public ResponseData hosueInfo(Long houseId,String token) {
return ResponseData.ok(houseService.getHouseInfo(houseId));
}
如果是PathVariable这种方式,参数是必传的,不然无法进入接口内,如果是RequestParam这种方式,方法中不定义token参数,我估计也是可以的,至少不会报错,反正我们是统一的去判断有无权限。
所以说我们的token放在请求头中,是非常友好的一种方式。
接下来我们说说使用的问题
在调用接口的时候怎么往请求头中添加token呢?
每次调用的地方都去添加token是不是太烦了?
其实在Zuul中我们可以用过滤器来统一添加token,这个时候可以使用置前的过滤器pre
**
* 调用服务前添加认证请求头过滤器
*
* @author yinjihuan
* @create 2017-11-07 16:06
**/
public class AuthHeaderFilter extends ZuulFilter {
public AuthHeaderFilter() {
super();
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("Authorization", TokenScheduledTask.token);
return null;
}
}
这样在每个请求转发到具体的微服务之前,我们给它添加了token信息,这个token信息是我们从TokenScheduledTask获取的
TokenScheduledTask是怎么获取token的呢?
/**
* 定时刷新token
*
* @author yinjihuan
* @create 2017-11-09 15:39
**/
@Component
public class TokenScheduledTask {
private static Logger logger = LoggerFactory.getLogger(TokenScheduledTask.class);
public final static long ONE_Minute = 60 * 1000 * 60 * 20;
public static String token = "";
@Autowired
private AuthService authService;
/**
* 刷新Token
*/
@Scheduled(fixedDelay = ONE_Minute)
public void reloadApiToken() {
token = authService.getToken();
while (StringUtils.isBlank(token)) {
try {
Thread.sleep(1000);
token = authService.getToken();
} catch (InterruptedException e) {
logger.error("", e);
}
}
}
}
原来是一个定时任务,通过调用认证的方法来获取认证好的token。
为什么要做成定时的呢
如果按照一般的做法那就是请求之后都去获取一次token, 这种方式是最不好的,性能太差。
稍微好点那就是在获取的地方加上缓存,貌似不错,但是有个问题是在并发的时候会存在N个请求去获取token,这边需要控制下。
定时的就不存在上面的问题了,但是一定要确保定时任务的正常。
我这边一个token的失效时间为24小时,所以我这边刷新的间隔是20小时,也就是说在token还没过期之前,我会自动刷新成最新的,这样就不会出现token过期的问题了。
while循环是为了确保token能够正确的刷新成功。
同时这个任务是在项目启动之后立马去刷新token的,这样就能确保刚过来的请求不会受到影响。
具体代码可以参考我的github:
https://github.com/yinjihuan/spring-cloud
推荐阅读:
《知识点-Spring Boot 统一异常处理汇总》
《Spring Boot 1.X和2.X优雅重启实战》
《Spring Boot中快速操作Mongodb》
《面试-线程池的成长之路》