整体流程如下:
第一步:使用java自带工具生成证书
在jdk的bin目录下用cmd命令操作keytool工具生成证书,记得以管理员身份运行否则会失败,命令如下:
keytool -genkeypair -alias jwt -keyalg RSA -keypass 123456 -keystore jwt.jks -storepass 123456
Keytool 是一个java提供的证书管理工具
得到文件后将文件拷贝到鉴权微服务的source目录下。
第二步:添加依赖
cn.hutool
hutool-all
5.7.20
org.springframework.security
spring-security-rsa
org.springframework
spring-core
org.projectlombok
lombok
第三步:添加权鉴方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JwtTemplate {
// 证书文件
private String path = "jwt.jks";
// 密钥库文件的密码
private String keyStoreSecurity = "123456";
// 密钥库的别名
private String alias = "jwt";
/**
* 创建密钥对对象
*/
private KeyPair keyPair(){
// 创建密钥对对象
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
new ClassPathResource(path),keyStoreSecurity.toCharArray()
);
KeyPair keyPair = factory.getKeyPair(alias);
return keyPair;
}
/**
* 创建签名器
*/
private JWTSigner jwtSigner(){
return JWTSignerUtil.createSigner("RSA",keyPair());
}
/**
* 【生成token】
*/
public String createToken(Map playload) {
return JWTUtil.createToken(playload,jwtSigner());
}
/**
* 【校验token】
*/
public boolean verify(String token) {
return JWTUtil.verify(token, jwtSigner());
}
/**
* 【解析token】
*/
public Object parseToken(String token,String key) {
JWT jwt = JWTUtil.parseToken(token);
return jwt.getPayload(key);
}
}
第四步:添加依赖
这里添加权鉴微服务的依赖以便调用
com.woniu
sk-common-jwt
1.0-SNAPSHOT
第五步:网关yml文件添加配置
这里的path就是权鉴证书名,别名和秘钥要和前面保持一致。
auth:
security:
path: jwt.jks
keyStoreSecurity: 123456
alias: jwt
第六步:增加redis和JWT的配置类
Redis配置类
@Configuration
public class RedisConfig {
/**
* 配置RedisTemplate的序列化方式
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(factory);
// 指定key的序列化方式:string
redisTemplate.setKeySerializer(RedisSerializer.string());
// 指定value的序列化方式:json
redisTemplate.setValueSerializer(RedisSerializer.json());
return redisTemplate;
}
}
JWT的配置类
@Configuration
public class JwtConfig {
@Value("${auth.security.path}")
private String path;
@Value("${auth.security.keyStoreSecurity}")
private String keyStoreSecurity;
@Value("${auth.security.alias}")
private String alias;
@Bean
public JwtTemplate jwtTemplate(){
return new JwtTemplate(path,keyStoreSecurity,alias);
}
}
第七步:增加过滤器
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {
@Autowired
private JwtTemplate jwtTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 1、获取请求路径,并进行判断路径中是否包含/auth
String path = request.getURI().getPath();
AntPathMatcher pathMatcher = new AntPathMatcher();
// 请求路径如:/promotion-seckill/auth/order/100
boolean match = pathMatcher.match("/**/auth/**", path);
if (match) { // 需要对当前路径进行鉴权
//2. 鉴权要求:请求头中Authorizaiton中携带token
String token = request.getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(token)) {
// 鉴权失败,返回错误
log.error("请求头中未携带token!");
return error(response);
}
//3. 请求头中的token值的格式: Bearer xx
token = token.replace("Bearer ","");
//4. token校验, 校验失败:JWTException: The token was expected 3 parts, but got 1.
boolean verify = jwtTemplate.verify(token);
if (!verify) {
log.error("token不合法!");
return error(response);
}
//5. 解析token,获取token中存储的userId。后面会作为redis中的key
String userId = (String) jwtTemplate.parseToken(token, "userId");
//6. 从Redis中获取token并校验
String tokenKey = "user:token:"+userId;
String redisToken = (String) redisTemplate.opsForValue().get(tokenKey);
if (StringUtils.isEmpty(redisToken)) {
log.error("token无效!");
return error(response);
}
//7. 重写请求头,把userId放入请求头中;这样各个微服务就可以从请求头中获取认证的userId
ServerHttpRequest.Builder mutate = request.mutate();
mutate.header("userId",userId);
//8. 更新redis中token的有效时间
redisTemplate.expire(tokenKey, Duration.ofMinutes(30));
}
// 放行,执行下一个过滤器或者执行微服务
return chain.filter(exchange);
}
// 自动处理检查异常的注解
@SneakyThrows
private Mono error(ServerHttpResponse response) {
Result