redis 保存 key => token ,values => userId
效果在最下面
TokenUtil:
@AllArgsConstructor
public class TokenUtil {
RedisTemplate redisTemplate;
ValueOperations valueOperations;
private static final int TIME = 30;
private static final String NAME = "token";
//添加 保存为String 方便zuul过滤器 设置请求头参数
public String addToken(String userId){
String token = UUID.randomUUID().toString();
valueOperations.set(token,userId, TIME, TimeUnit.MINUTES);
return token;
}
//删除
public void deleteToken(HttpServletRequest request){
String token = request.getHeader(NAME);
if (token!=null){
redisTemplate.delete(token);
}
}
//解析
public String parseToken(HttpServletRequest request){
String token = request.getHeader(NAME);
return token == null ? null : (String)this.valueOperations.get(token);
}
//刷新
public boolean refreshToken(HttpServletRequest request){
String token = request.getHeader(NAME);
return token == null ? false : this.redisTemplate.expire(token, TIME, TimeUnit.MINUTES);
}
//解析 并 刷新
public String parseRrefreshToken(HttpServletRequest request){
String token = request.getHeader(NAME);
if (null!=token && redisTemplate.expire(token, TIME, TimeUnit.MINUTES))
return (String)this.valueOperations.get(token);
else return null;
}
}
zuul 过滤器:
@Component
public class TokenFilter extends ZuulFilter {
@Autowired
TokenUtil tokenUtil;
@Resource
String USER_ID_LOGIN_OVERDUE;
static final String TARGET_URI = "ByUserId";
// run()中 ctx.setSendZuulResponse(false); 经常导致 获取不了请求头信息,使用逻辑写这里了
@Override
public boolean shouldFilter() { //是否使用该过滤器
RequestContext ctx = RequestContext.getCurrentContext();//获取当前的 上下文对象
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
response.setHeader("Access-Control-Allow-Origin",request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Headers","token, content-type");
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Allow-Methods","GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH");
response.setHeader("Access-Control-Expose-Headers","X-forwared-port, X-forwarded-host");
response.setHeader("Vary","Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
String requestURI = request.getRequestURI(); //获取请求的uri
if (requestURI.endsWith(TARGET_URI)){ //以 ByUserId 结尾的uri进行验证
String userId = tokenUtil.parseRrefreshToken(request); //解析并刷新出userId
System.out.println("token => "+request.getHeader("token"));
System.out.println("userId => "+userId);
if (userId == null ){
return true;
}else {
// request.getParameterMap(); //必须加这句 不然ctx.getRequestQueryParams()失败
// Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();//参数键值对map
//如果一开始请求头里没参数 那么requestQueryParams为空
// if (requestQueryParams==null) requestQueryParams=new HashMap<>();
//为了提高性能 ,其他参数都放请求体了,用post请求
Map<String, List<String>> requestQueryParams=new HashMap<>();
List<String> arrayList = new ArrayList<>();
arrayList.add(userId); //添加 对应的values
requestQueryParams.put("userId", arrayList); //把参数键值对放入map
ctx.setRequestQueryParams(requestQueryParams); //重新设置请求头所含有的参数
}
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();//获取当前的 上下文对象
ctx.setResponseBody(USER_ID_LOGIN_OVERDUE);
ctx.setSendZuulResponse(false);
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
} //数字越小优先级越高
}
前端部分可以拦截器配合使用
比如vue 的axios:
// vue 的拦截器请求头设置token 和 zuul过滤器获取token 并验证得到userId
axios.interceptors.request.use(
config => {
config.headers.token = localStorage.getItem("token");
return config;
}
);
效果:
localhost:7010 为zuul网关 , user是微服务名字
请求头带了 token
请求头没带token
token信息不对 或者 token在redis中已经过期