1.Jwt令牌介绍
2.基于私钥生成jwt令牌
3.基于公钥解析jwt令牌
4.应用场景
1.Jwt令牌介绍
Jwt令牌主要由三部分组成头部,负载,签名,每个部分用点隔开
-
头部主要包含令牌类型和使用的加密算法
-
载荷主要存放信息,可以解码还原
-
签名:
签名=头部+载荷+签名
头部和载荷都采用Base64Url编码和签名进行(用**.**进行)拼接生成签名
2.基于私钥生成Jwt令牌
准备私钥,公钥放入resource文件下,基于私钥携带数据生成jwt令牌
public class CreateJwtTest {
/**
* 创建令牌测试
*/
@Test
public void createJWT() {
//基于私钥生成jwt
//1. 创建一个秘钥工厂
//1: 指定私钥的位置
ClassPathResource classPathResource = new ClassPathResource("changgou.jks");
//2: 指定秘钥库的密码
String keyPass = "changgou";
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, keyPass.toCharArray());
//2. 基于工厂获取私钥
String alias = "changgou";
String password = "changgou";
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, password.toCharArray());
//将当前的私钥转换为rsa私钥
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
//3.生成jwt
//存储一些数据
Map<String, String> map = new HashMap();
map.put("company", "baidu");
map.put("address", "beijing");
Jwt jwt = JwtHelper.encode(JSON.toJSONString(map), new RsaSigner(rsaPrivateKey));
String jwtEncoded = jwt.getEncoded();
System.out.println(jwtEncoded);
}
}
3.基于公钥解析jwt令牌
public class ParseJwtTest {
/**
* 校验令牌
*/
@Test
public void testParseToken() {
//令牌
String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZGRyZXNzIjoiYmVpamluZyIsImNvbXBhbnkiOiJiYWlkdSJ9.JdtrwOQMvg6ZzP9zKvUZAXrd7zF_ufgi0ye_DQHeSukVwHXR9pN3MxQUCMp0uAawbFkCeNNglghbOflusDBK20qel5fZQLOZCAiL3kdx1z2Edvsu4QheTdRmNDtlA0vppmwWueRZgUrRiGK7GVhJeEOnNhp4DzKOSThy0XBdL6ECela7PHyGV1wyq51TxiDHK7onvTk2L-0CRoLwmqk27AsDd9X49Z-HnGxxSCpRBoEoVJEpVLo6qJxBYzpVN13zQqmefjb-tmgjeJaM-CtYHDvez-hkUj4Tu--sYtnBxbntiOCTFTztxOjs5AtLOK2B46qJbMZnxqYt15Nh6Rt-9A";
//公钥
String publicKey ="-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvFsEiaLvij9C1Mz+oyAmt47whAaRkRu/8kePM+X8760UGU0RMwGti6Z9y3LQ0RvK6I0brXmbGB/RsN38PVnhcP8ZfxGUH26kX0RK+tlrxcrG+HkPYOH4XPAL8Q1lu1n9x3tLcIPxq8ZZtuIyKYEmoLKyMsvTviG5flTpDprT25unWgE4md1kthRWXOnfWHATVY7Y/r4obiOL1mS5bEa/iNKotQNnvIAKtjBM4RlIDWMa6dmz+lHtLtqDD2LF1qwoiSIHI75LQZ/CNYaHCfZSxtOydpNKq8eb1/PGiLNolD4La2zf0/1dlcr5mkesV570NxRmU1tFm8Zd3MZlZmyv9QIDAQAB-----END PUBLIC KEY-----";
//校验Jwt
Jwt jwt = JwtHelper.decodeAndVerify(token,new RsaVerifier(publicKey));
//获取jwt中存储的数据
String claims = jwt.getClaims();
System.out.println(claims);
//jwt令牌
String encoded = jwt.getEncoded();
System.out.println(encoded);
}
}
4.应用场景
1.用于微服务鉴权
1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进 行登录
2. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
3. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN
4. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误
提供两个生产解析的jwt令牌的工具类
生成工具类
public class JwtUtil {
//有效期为
public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000 一个小时
//设置秘钥明文
public static final String JWT_KEY = "baidu";
/**
* 创建token
* @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);
if(ttlMillis==null){
ttlMillis=JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
SecretKey secretKey = generalKey();
JwtBuilder builder = Jwts.builder()
.setId(id) //唯一的ID
.setSubject(subject) // 主题 可以是JSON数据
.setIssuer("admin") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
.setExpiration(expDate);// 设置过期时间
return builder.compact();
}
/**
* 生成加密后的秘钥 secretKey
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 解析
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
}
颁发令牌,使用场景
if (login) {
//登录成功,生成令牌,返回jwt令牌 和用户名
HashMap<String, String> map = new HashMap<>();
map.put("LoginName",admin.getLoginName());
String token = JwtUtil.createJWT(UUID.randomUUID().toString(), admin.getLoginName(), null);
map.put("token",token);
return new Result(true, StatusCode.OK, "登录成功",map);
}
}
解析令牌,使用场景,在网关服务中定义拦截器,进行鉴权操作
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求对象
ServerHttpRequest request = exchange.getRequest();
//2.获得响应对象
ServerHttpResponse response = exchange.getResponse();
//3.判断是否为登录请求,是就放行
String path = request.getURI().getPath();
if (path.contains("/admin/login")){
return chain.filter(exchange);
}
//4. 获取请求头信息
HttpHeaders headers = request.getHeaders();
//5.获取jwt令牌信息
String jwtToken = headers.getFirst("token");
//6.判断当前令牌是否存在,不存在,返回错误信息
if (jwtToken == null) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//6.1存在,判断是否合法,不合法返回错误信息
//6.2存在,合法,放行
try {
JwtUtil.parseJWT(jwtToken);
}catch (Exception e){
e.printStackTrace();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}