这两天在写项目的全局权限校验,用 Zuul 作为服务网关,在 Zuul 的前置过滤器里做的校验。
权限校验或者身份验证就不得不提 Token,目前 Token 的验证方式有很多种,有生成 Token 后将 Token 存储在 Redis 或数据库的,也有很多用 JWT(JSON Web Token)的。
说实话这方面我的经验不多,又着急赶项目,所以就先用个简单的方案。
登录成功后将 Token 返回给前端,同时将 Token 存在 Redis 里。每次请求接口都从 Cookie 或 Header 中取出 Token,在从 Redis 中取出存储的 Token,比对是否一致。
我知道这方案不是最完美的,还有安全性问题,容易被劫持。但目前的策略是先把项目功能做完,上线之后再慢慢优化,不在一个功能点上扣的太细,保证项目进度不至于太慢。
项目地址:https://github.com/cachecats/coderiver
本文将分四部分介绍
- 登录逻辑
- AuthFilter 前置过滤器校验逻辑
- 工具类
- 演示验证
一、登录逻辑
登录成功后,将生成的 Token 存储在 Redis 中。用 String 类型的 key, value 格式存储,key是 TOKEN_userId
,如果用户的 userId 是 222222
,那键就是 TOKEN_222222
;值是生成的 Token。
只贴出登录的 Serive 代码
@Override
public UserInfoDTO loginByEmail(String email, String password) {
if (StringUtils.isEmpty(email) || StringUtils.isEmpty(password)) {
throw new UserException(ResultEnum.EMAIL_PASSWORD_EMPTY);
}
UserInfo user = userRepository.findUserInfoByEmail(email);
if (user == null) {
throw new UserException(ResultEnum.EMAIL_NOT_EXIST);
}
if (!user.getPassword().equals(password)) {
throw new UserException(ResultEnum.PASSWORD_ERROR);
}
//生成 token 并保存在 Redis 中
String token = KeyUtils.genUniqueKey();
//将token存储在 Redis 中。键是 TOKEN_用户id, 值是token
redisUtils.setString(String.format(RedisConsts.TOKEN_TEMPLATE, user.getId()), token, 2l, TimeUnit.HOURS);
UserInfoDTO dto = new UserInfoDTO();
BeanUtils.copyProperties(user, dto);
dto.setToken(token);
return dto;
}
二、AuthFilter 前置过滤器
AuthFilter
继承自 ZuulFilter
,必须实现 ZuulFilter
的四个方法。
filterType()
: Filter 的类型,前置过滤器返回PRE_TYPE
filterOrder()
: Filter 的顺序,值越小越先执行。这里的写法是PRE_DECORATION_FILTER_ORDER - 1
, 也是官方建议的写法。
shouldFilter()
: 是否应该过滤。返回 true 表示过滤,false 不过滤。可以在这个方法里判断哪些接口不需要过滤,本例排除了注册和登录接口,除了这两个接口,其他的都需要过滤。
run()
: 过滤器的具体逻辑
为了方便前端,考虑到要给 pc、app、小程序等不同平台提供服务,token 设置在 cookie 和 header 任选一均可,会先从 cookie 中取,cookie 中没有再从 header 中取。
package com.solo.coderiver.gateway.filter;
import com.google.gson.Gson;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.solo.coderiver.gateway.VO.ResultVO;
import com.solo.coderiver.gateway.consts.RedisConsts;
import com.solo.coderiver.gatewa