赞
踩
目录
JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:JSON Web Tokens - jwt.io (JWT,生成Token加密字符串的一个标准或格式!)
GitHub上jwt的java客户端:https://github.com/jwtk/jjwt
JWT包含三部分数据:
Header:头部,通常头部有两部分信息:
声明类型,这里是JWT
签名算法,自定义
我们会对头部进行base64编码,得到第一部分数据
如图所示:头部是不具备安全性的。
Payload:载荷,就是有效数据,一般包含下面信息:
用户身份信息(注意,这里因为采用base64加密,可解密,因此不要存放敏感信息)
tokenID:当前这个JWT的唯一标示
注册声明:如token的签发时间,过期时间,签发人等
这部分也会采用base64加密,得到第二部分数据
如图所示:载荷也不具备安全性。
- Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性
对称加密,如AES
基本原理:将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。
优势:算法公开、计算量小、加密速度快、加密效率高
缺陷:双方都使用同样密钥,安全性得不到保证
非对称加密,如RSA
基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端
私钥加密,持有私钥或公钥才可以解密
公钥加密,持有私钥才可解密
优点:安全,难以破解
缺点:算法比较耗时
不可逆加密,如MD5,SHA,HS加密
基本原理:加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。
有了非对称加密,我们就可以改变签名和验签的方式了:
生成RSA密钥对,私钥存放在授权中心,公钥下发给微服务
在授权中心利用私钥对JWT签名
在微服务利用公钥验证签名有效性
因为非对称加密的特性,不用担心公钥泄漏问题,因为公钥是无法伪造签名的,但要确保私钥的安全和隐秘。
非对称加密后的授权和鉴权流程:
- public class RsaUtils {
-
- private static final int DEFAULT_KEY_SIZE = 2048;
- /**
- * 从文件中读取公钥
- *
- * @param filename 公钥保存路径,相对于classpath
- * @return 公钥对象
- * @throws Exception
- */
- public static PublicKey getPublicKey(String filename) throws Exception {
- byte[] bytes = readFile(filename);
- return getPublicKey(bytes);
- }
-
- /**
- * 从文件中读取密钥
- *
- * @param filename 私钥保存路径,相对于classpath
- * @return 私钥对象
- * @throws Exception
- */
- public static PrivateKey getPrivateKey(String filename) throws Exception {
- byte[] bytes = readFile(filename);
- return getPrivateKey(bytes);
- }
-
- /**
- * 获取公钥
- *
- * @param bytes 公钥的字节形式
- * @return
- * @throws Exception
- */
- private static PublicKey getPublicKey(byte[] bytes) throws Exception {
- bytes = Base64.getDecoder().decode(bytes);
- X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
- KeyFactory factory = KeyFactory.getInstance("RSA");
- return factory.generatePublic(spec);
- }
-
- /**
- * 获取密钥
- *
- * @param bytes 私钥的字节形式
- * @return
- * @throws Exception
- */
- private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
- bytes = Base64.getDecoder().decode(bytes);
- PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
- KeyFactory factory = KeyFactory.getInstance("RSA");
- return factory.generatePrivate(spec);
- }
-
- /**
- * 根据密文,生存rsa公钥和私钥,并写入指定文件
- *
- * @param publicKeyFilename 公钥文件路径
- * @param privateKeyFilename 私钥文件路径
- * @param secret 生成密钥的密文
- */
- public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
- SecureRandom secureRandom = new SecureRandom(secret.getBytes());
- keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
- KeyPair keyPair = keyPairGenerator.genKeyPair();
- // 获取公钥并写出
- byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
- publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
- writeFile(publicKeyFilename, publicKeyBytes);
- // 获取私钥并写出
- byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
- privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
- writeFile(privateKeyFilename, privateKeyBytes);
- }
-
- private static byte[] readFile(String fileName) throws Exception {
- return Files.readAllBytes(new File(fileName).toPath());
- }
-
- private static void writeFile(String destPath, byte[] bytes) throws IOException {
- File dest = new File(destPath);
- if (!dest.exists()) {
- dest.createNewFile();
- }
- Files.write(dest.toPath(), bytes);
- }
- }

- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-api</artifactId>
- <version>0.10.5</version>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-impl</artifactId>
- <version>0.10.5</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-jackson</artifactId>
- <version>0.10.5</version>
- <scope>runtime</scope>
- </dependency>

JWT中,会保存载荷数据,我们计划存储3部分:
id:jwt的id
用户信息:用户数据,不确定,可以是任意类型
过期时间:Date
为了方便后期获取,我们定义一个类来封装
- @Data
- public class Payload<T> {
- private String id;
- private T info;
- private Date expiration;
- }
封装用户信息类:UserInfo
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class UserInfo {
- private Long id;//用户ID
- private String username;//用户名称
- private String role;//用户角色
- }
JWT工具
- public class JwtUtils {
-
- private static final String JWT_PAYLOAD_USER_KEY = "user";
-
- /**
- * 私钥加密token
- *
- * @param info 载荷中的数据
- * @param privateKey 私钥
- * @param expire 过期时间,单位分钟
- * @return JWT
- */
- public static String generateTokenExpireInMinutes(Object info, PrivateKey privateKey, int expire) {
- return Jwts.builder()
- //claim: 往Jwt的载荷存入数据
- .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(info))
- //往Jwt的载荷存入数据,设置固定id的key
- .setId(createJTI())
- //往Jwt的载荷存入数据,设置固定exp的key
- .setExpiration(DateTime.now().plusMinutes(expire).toDate())
- //设置token的签名
- .signWith(privateKey, SignatureAlgorithm.RS256)
- .compact();
- }
-
- /**
- * 私钥加密token
- *
- * @param info 载荷中的数据
- * @param privateKey 私钥
- * @param expire 过期时间,单位秒
- * @return JWT
- */
- public static String generateTokenExpireInSeconds(Object info, PrivateKey privateKey, int expire) {
- return Jwts.builder()
- .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(info))
- .setId(createJTI())
- .setExpiration(DateTime.now().plusSeconds(expire).toDate())
- .signWith(privateKey, SignatureAlgorithm.RS256)
- .compact();
- }
-
- /**
- * 公钥解析token
- *
- * @param token 用户请求中的token
- * @param publicKey 公钥
- * @return Jws<Claims>
- */
- private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
- return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
- }
-
- private static String createJTI() {
- return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
- }
-
- /**
- * 获取token中的用户信息
- *
- * @param token 用户请求中的令牌
- * @param publicKey 公钥
- * @return 用户信息
- */
- public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
- Jws<Claims> claimsJws = parserToken(token, publicKey);
- Claims body = claimsJws.getBody();
- Payload<T> claims = new Payload<>();
- claims.setId(body.getId());
- claims.setInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
- claims.setExpiration(body.getExpiration());
- return claims;
- }
-
- /**
- * 获取token中的载荷信息
- *
- * @param token 用户请求中的令牌
- * @param publicKey 公钥
- * @return 用户信息
- */
- public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
- Jws<Claims> claimsJws = parserToken(token, publicKey);
- Claims body = claimsJws.getBody();
- Payload<T> claims = new Payload<>();
- claims.setId(body.getId());
- claims.setExpiration(body.getExpiration());
- return claims;
- }
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。