1. 在微服务父工程中pom文件中引入,jwtToken依赖
代码语言:javascript
复制 <dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
2. 配置创建JwtToken的工具类
代码语言:javascript
复制package priv.kuki.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
/**
* @author Tang-J-L
* @Description JwtUtil
* @Date 2023/4/3 周一 19:18
*/
public class JwtUtil{
/**
* 默认有效期 30min
*/
public static final Long JWT_TTL = 3600000L;
/**
* jwt令牌信息
*/
public static final String JWT_KEY = "Tang-J-L";
/**
* 创建令牌
* @param id 令牌唯一标识
* @param subject 主题信息
* @param ttlMillis 有效期
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis){
// 指定算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 系统当前时间
long nowMillis = System.currentTimeMillis();
// 令牌颁发时间
Date now = new Date(nowMillis);
// 若传入的令牌有效期为null,则使用默认有效期
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
// 令牌过期时间
long expMillis = nowMillis ttlMillis;
Date expDate = new Date(expMillis);
// 生成密钥
SecretKey secretKey = generalKey();
// 封装令牌信息
JwtBuilder jwtBuilder = Jwts.builder()
.setId(id) //令牌唯一标识
.setSubject(subject) // 主题,可以是JSON数据
.setIssuer("Tang-J-L") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey) // 签名以及密钥.参1:签名算法;参2:密钥(盐)
.setExpiration(expDate);// 设置过期时间
return jwtBuilder.compact();
/* // 令牌过期时间设置
long expMillis = nowMillis ttlMillis;
Date expDate = new Date(expMillis);
// 构建jwt令牌
JwtBuilder jwtBuilder = Jwts.builder();
// jwt颁发者
jwtBuilder.setIssuer("酷奇");
// 颁发日期
jwtBuilder.setIssuedAt(new Date());
// 主题信息
jwtBuilder.setSubject("jwt令牌");
// 签名。参1:签名算法;参2:密钥(盐)
jwtBuilder.signWith(SignatureAlgorithm.HS256,"Tang-J-L");
// 过期时间,一般为30分钟,当前系统时间 30min
jwtBuilder.setExpiration(new Date(System.currentTimeMillis() 1800000));
// 自定义载荷信息,添加附加信息
HashMap<String, Object> userInfo = new HashMap<>();
userInfo.put("company","CODER");
userInfo.put("vip","coder-v");
// 将自定义信息添加到令牌中
jwtBuilder.addClaims(userInfo);
// 生成jwtToken
String token = jwtBuilder.compact(); */
}
/**
* 生成加密 generalKey:对当前密钥进行再次Base64加密
* @return
*/
public static SecretKey generalKey(){
byte[] encodeKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
return key;
}
/**
* 解析令牌
*/
public static Claims parseJWT(String jwt){
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
/**
* setSigningKey():解析密钥
* parseClaimsJws():解析token
* getBody():获取解析后的数据
* toString():讲解析后的令牌转换为String得到最终的令牌
*/
// Claims jwtToken = Jwts.parser().setSigningKey("Tang-J-L").parseClaimsJws().getBody().toString();
}
}
3. 创建用户鉴权的工具类
代码语言:javascript
复制package priv.kuki.utils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.lang.annotation.Annotation;
/**
* @author Tang-J-L
* @Description 用户权限校验
* @Date 2023/4/3 周一 20:44
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Order{
/**
* 令牌名字
*/
public static final String AUTHORIZE_TOKEN = "Authorization";
/**
* 全局拦截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 获取令牌信息:令牌可能在 1)参数中, 2)请求头中, 3)Cookie中
// 1)从 请求头中 获取
String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
// 出于微服务原因,其他服务需要从请求头中获取token,若前台不是放在请求头中,则我们需要将其封装到请求头中
boolean hasToken = true;
// 2)从 参数中 获取
if (StringUtils.isEmpty(token)) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
hasToken = false;
}
// 3)从 Cookie中 获取
if (StringUtils.isEmpty(token)) {
HttpCookie httpCookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
if (httpCookie != null) {
token = httpCookie.getValue();
}
}
// 若没有令牌则拦截
if(StringUtils.isEmpty(token)){
// 设置没有权限的状态码 401
// response.setStatusCode(HttpStatus.UNAUTHORIZED);
R.fail("你还没有登录",token);
// 响应空数据
return response.setComplete();
}
// 若有令牌,则校验令牌是否有效
try{
JwtUtil.parseJWT(token);
}catch(Exception e) {
// 无效则拦截
// 设置没有权限的状态码 401
// response.setStatusCode(HttpStatus.UNAUTHORIZED);
R.fail("你还没有登录",e);
// 响应空数据
return response.setComplete();
}
// 将令牌封装到请求头中,在其他微服务中通过 OAuthor 框架鉴权
request.mutate().header(AUTHORIZE_TOKEN, token);
// 有效则放行
return chain.filter(exchange);
}
/**
* @return
*/
@Override
public int value(){
return 0;
}
/**
* Returns the annotation type of this annotation.
*
* @return the annotation type of this annotation
*/
@Override
public Class<? extends Annotation> annotationType(){
return null;
}
}
4. 用户登录成功后创建jwtToken,并将token信息返回给前端
代码语言:javascript
复制 // 创建jwtToken
// 创建token以外的自定义额外信息
Map<String,Object> tokenMap = new HashMap<>();
tokenMap.put("role","USER");
tokenMap.put("success","SUCCESS");
tokenMap.put("username",user.getUserName());
String token = JwtUtil.createJWT(
UUID.randomUUID().toString(), // uuid作为令牌id
JSONUtils.toJSONString(tokenMap), // 令牌中的额外信息
1800000L // 过期时间 30min
);
// 把令牌信息存入Cookie
Cookie cookie = new Cookie("Authorization", token);
// 所属域名
cookie.setDomain("localhost");
// 根路径
cookie.setPath("/");
// 将令牌存入Cookie响应给前端
response.addCookie(cookie);
// 把令牌作为信息返回给客户端
return R.ok("登录成功!",cookie);