wangjiedadada  

 

1.引入jwt的概念:

 1.1有状态登录,无状态登录

  有状态登录:服务器当中记录每一次的登录信息,从而根据客户端发送的数据来判断登录过来的用户是否合法。

  缺点:

      • 服务器当中需要保存大量的session信息,从而增加了服务器的压力。
      • 客户端请求依赖登录服务器,多个请求过来访问同一个服务器。
      • 服务器拓展困难,需要将session存储到其他服务器当中。

 

  无状态登录:服务器当中不记录用户的登录信息,而是将登录成功后的合法用户信息以token方式保存到客户端当中,

     用户每次请求都携带token信息。

  好处:

    • 减少服务器存储session信息的压力。
    • 客户端请求不依赖服务器。

 1.2如何实现无状态登录?

   流程:

    1. 客户端第一次请求服务器,服务器端对登录用户信息进行认证。
    2. 认证通过后,对客户信息进行加密处理形成token登录凭证,然后返回给客户端。
    3. 以后客户端每一次请求都携带这个jwt信息去请求服务器。
    4. 服务器端请求信息进行解密处理。判断登录用户是否有效。

 

      授权-鉴权流程图:

  

2.加密技术的引入

  2.1对称加密,非堆成加密,不可逆加密方式。

  对称加密:将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。

  优势:加密速度快,加密效率高。

  缺点:双方都使用同样的密钥,安全性得不到保证。

  

  非对称加密方式:同时生成两把密钥,公钥和私钥。私钥服务器自己保存,公钥下发到受信任的客户端

  优势:安全性高,只要私钥不暴露出去,信息就不会泄露。

  缺点:加密效率低。

 

  不可逆加密方式:MD5

  加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文

  严格意义上,这种不属于加密方式。因为加密算法要既能加密也能解密。

  

3.JWT

  概念:全称json wek token 是一种轻量级的身份认证 ,可实现无状态 ,分布式的web应用授权。

  

  3.1组成:

    JWT由三部分组成分别是:header+payload+签名

    header:头部,通常由两部分信息,  

          。声明类型:通常为jwt

          。签名算法:签名部分使用的算法,需要在header中进行定义。

    payload:负载。

          这里保存着有效的数据部分。

          jwt当中实际存储数据的部分。

          官方规定了7个可以选择的数据。

    签名部分:通常是整个数据的认证信息,一般根据前面两部的数据,再加上服务的密钥,通过加密算法生成,用于验证整个数据的完成性。

        

  如图所示,这是生成的jwt信息。

  3.2jwt系统交互流程图

          

 

  对上面的交互流程的分析:

  通过交互图可以观察到:用户登陆微服务后,还需要拿着jwt到鉴权中心去验证用户的登陆权限,能不能让用户就在服务端就可以完成鉴权的工作,这样就可以减少一次网络请求,加快系统的响应时间。

  结论:我们可以使用jwt+rsa的方式,由鉴权中心生成私钥,公钥。在授权中心通过私钥生成jwt信息,然后公钥下发给受信任的服务。再使用公钥再服务器端进行鉴权处理。

     如果通过公钥可以获取到jwt当中信息,说明该用户具有对应的权限。可以进行登陆操作。

 

  3.3 使用jwt+rsa方式的授权+鉴权方式

  

 

4 JWT实例:

 

  4.1使用jwt所依赖的maven依赖都有哪些:

    

 1 <!--json web token相关坐标-->
 2         <dependency>
 3             <groupId>io.jsonwebtoken</groupId>
 4             <artifactId>jjwt-api</artifactId>
 5             <version>0.10.5</version>
 6         </dependency>
 7         <dependency>
 8             <groupId>io.jsonwebtoken</groupId>
 9             <artifactId>jjwt-impl</artifactId>
10             <version>0.10.5</version>
11             <scope>runtime</scope>
12         </dependency>
13         <dependency>
14             <groupId>io.jsonwebtoken</groupId>
15             <artifactId>jjwt-jackson</artifactId>
16             <version>0.10.5</version>
17             <scope>runtime</scope>
18         </dependency>

    4.2RSA工具类:用于生成私钥和公钥

 

  1 package com.mi.demo.author;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.nio.file.Files;
  6 import java.security.*;
  7 import java.security.spec.InvalidKeySpecException;
  8 import java.security.spec.PKCS8EncodedKeySpec;
  9 import java.security.spec.X509EncodedKeySpec;
 10 import java.util.Base64;
 11 
 12 /**
 13  * Rsa工具类
 14  * @author 王杰
 15  */
 16 public class RsaUtils {
 17 
 18     private static final int DEFAULT_KEY_SIZE = 2048;
 19 
 20     /**
 21      * 从文件中读取公钥
 22      *
 23      * @param filename 公钥保存路径,相对于classpath
 24      * @return  PublicKey 公钥对象
 25      * @throws Exception
 26      */
 27     public static PublicKey getPublicKey(String filename) throws Exception {
 28         byte[] bytes = readFile (filename);
 29         return getPublicKey (bytes);
 30     }
 31 
 32     /**
 33      * 从文件中读取密钥
 34      *
 35      * @param filename 私钥保存路径,相对于classpath
 36      * @return  PrivateKey 私钥对象
 37      * @throws Exception
 38      */
 39     public static PrivateKey getPrivateKey(String filename) throws Exception {
 40         byte[] bytes = readFile (filename);
 41         return getPrivateKey (bytes);
 42     }
 43 
 44     /**
 45      * 获取公钥
 46      * 公钥的字节形式。
 47      * @param bytes 公钥的字节形式
 48      * @return
 49      * @throws Exception
 50      */
 51     private static PublicKey getPublicKey(byte[] bytes) throws Exception {
 52         bytes = Base64.getDecoder ( ).decode (bytes);
 53         X509EncodedKeySpec spec = new X509EncodedKeySpec (bytes);
 54         KeyFactory factory = KeyFactory.getInstance ("RSA");
 55         return factory.generatePublic (spec);
 56     }
 57 
 58     /**
 59      * 获取密钥
 60      *
 61      * @param bytes 私钥的字节形式
 62      * @return
 63      * @throws Exception
 64      */
 65     private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
 66         bytes = Base64.getDecoder ( ).decode (bytes);
 67         PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec (bytes);
 68         KeyFactory factory = KeyFactory.getInstance ("RSA");
 69         return factory.generatePrivate (spec);
 70     }
 71 
 72     /**
 73      * 根据密文,生存rsa公钥和私钥,并写入指定文件
 74      *
 75      * @param publicKeyFilename  公钥文件路径
 76      * @param privateKeyFilename 私钥文件路径
 77      * @param secret             生成密钥的密文
 78      */
 79     public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
 80         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance ("RSA");
 81         SecureRandom secureRandom = new SecureRandom (secret.getBytes ( ));
 82         keyPairGenerator.initialize (Math.max (keySize, DEFAULT_KEY_SIZE), secureRandom);
 83         KeyPair keyPair = keyPairGenerator.genKeyPair ( );
 84         // 获取公钥并写出
 85         byte[] publicKeyBytes = keyPair.getPublic ( ).getEncoded ( );
 86         publicKeyBytes = Base64.getEncoder ( ).encode (publicKeyBytes);
 87         writeFile (publicKeyFilename, publicKeyBytes);
 88         // 获取私钥并写出
 89         byte[] privateKeyBytes = keyPair.getPrivate ( ).getEncoded ( );
 90         privateKeyBytes = Base64.getEncoder ( ).encode (privateKeyBytes);
 91         writeFile (privateKeyFilename, privateKeyBytes);
 92     }
 93 
 94     private static byte[] readFile(String fileName) throws Exception {
 95         return Files.readAllBytes (new File (fileName).toPath ( ));
 96     }
 97 
 98     private static void writeFile(String destPath, byte[] bytes) throws IOException {
 99         File dest = new File (destPath);
100         if (!dest.exists ( )) {
101             dest.createNewFile ( );
102         }
103         Files.write (dest.toPath ( ), bytes);
104     }
105 
106 }

 

 1 package com.mi.demo.utils;
 2 
 3 import com.mi.demo.author.JwtUtils;
 4 import com.mi.demo.author.Payload;
 5 import com.mi.demo.author.UserInfo;
 6 import org.junit.Test;
 7 
 8 import java.security.PrivateKey;
 9 import java.security.PublicKey;
10 
11 public class RsaUtilsTest {
12     //生成私钥,公钥地址
13     private String privateFilePath = "E:\\develop\\ssh\\id_rsa";
14     private String publicFilePath = "E:\\develop\\ssh\\id_rsa_pub";
15 
16     @Test
17     public void testRSA() throws Exception {
18         // 生成密钥对
19         RsaUtils.generateKey(publicFilePath, privateFilePath, "hello", 2048);
20 
21         // 获取私钥
22         PrivateKey privateKey = RsaUtils.getPrivateKey(privateFilePath);
23         System.out.println("privateKey = " + privateKey);
24         // 获取公钥
25         PublicKey publicKey = RsaUtils.getPublicKey(publicFilePath);
26         System.out.println("publicKey = " + publicKey);
27     }

 

   4.3 jwt当中的载荷信息部分。

 

 1 package com.mi.demo.author;
 2 
 3 import lombok.Data;
 4 
 5 import java.util.Date;
 6 
 7 @Data
 8 public class Payload<T> {
 9 //    jwt的id
10     private String id;
11 //    用户信息
12     private T userInfo;
13 //    过期时间
14     private Date expiration;
15 
16 }

 

 1 package com.mi.demo.author;
 2 
 3 import lombok.AllArgsConstructor;
 4 import lombok.Data;
 5 import lombok.NoArgsConstructor;
 6 
 7 /**
 8  * @author 王杰
 9  */
10 @Data
11 @NoArgsConstructor
12 @AllArgsConstructor
13 /**
14  * 载荷 :UserInfo
15  */
16 public class UserInfo {
17 
18     private Long id;
19 
20     private String username;
21 
22     private String role;
23 }

 

  4.4 jwtutils工具类,用于生成jwt信息,解析jwt信息。

  1 package com.mi.demo.author;
  2 
  3 import io.jsonwebtoken.Claims;
  4 import io.jsonwebtoken.Jws;
  5 import io.jsonwebtoken.Jwts;
  6 import io.jsonwebtoken.SignatureAlgorithm;
  7 import org.joda.time.DateTime;
  8 
  9 
 10 import java.security.PrivateKey;
 11 import java.security.PublicKey;
 12 import java.util.Base64;
 13 import java.util.UUID;
 14 
 15 public class JwtUtils {
 16 
 17     private static final String JWT_PAYLOAD_USER_KEY = "user";
 18 
 19     /**
 20      *
 21      * 私钥加密token
 22      *
 23      * @param userInfo   载荷中的数据
 24      * @param privateKey 私钥
 25      * @param expire     过期时间,单位分钟
 26      * @return JWT
 27      */
 28     public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
 29         return Jwts.builder()
 30                 .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
 31                 .setId(createJTI())
 32                 .setExpiration(DateTime.now().plusMinutes(expire).toDate())
 33                 .signWith(privateKey, SignatureAlgorithm.RS256)
 34                 .compact();
 35     }
 36 
 37     /**
 38      * 私钥加密token
 39      *
 40      * @param userInfo   载荷中的数据
 41      * @param privateKey 私钥
 42      * @param expire     过期时间,单位秒
 43      * @return JWT
 44      */
 45     public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
 46         return Jwts.builder()
 47                 .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
 48                 .setId(createJTI())
 49                 .setExpiration(DateTime.now().plusSeconds(expire).toDate())
 50                 .signWith(privateKey, SignatureAlgorithm.RS256)
 51                 .compact();
 52     }
 53 
 54     /**
 55      * 公钥解析token
 56      *
 57      * @param token     用户请求中的token
 58      * @param publicKey 公钥
 59      * @return Jws<Claims>
 60      */
 61     private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
 62         return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
 63     }
 64 
 65     private static String createJTI() {
 66         return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
 67     }
 68 
 69     /**
 70      * 获取token中的用户信息
 71      *
 72      * @param token     用户请求中的令牌
 73      * @param publicKey 公钥
 74      * @return 用户信息
 75      */
 76     public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
 77         Jws<Claims> claimsJws = parserToken(token, publicKey);
 78         Claims body = claimsJws.getBody();
 79         Payload<T> claims = new Payload<>();
 80         claims.setId(body.getId());
 81         claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
 82         claims.setExpiration(body.getExpiration());
 83         return claims;
 84     }
 85 
 86     /**
 87      * 获取token中的载荷信息
 88      *
 89      * @param token     用户请求中的令牌
 90      * @param publicKey 公钥
 91      * @return 用户信息
 92      */
 93     public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
 94         Jws<Claims> claimsJws = parserToken(token, publicKey);
 95         Claims body = claimsJws.getBody();
 96         Payload<T> claims = new Payload<>();
 97         claims.setId(body.getId());
 98         claims.setExpiration(body.getExpiration());
 99         return claims;
100     }
101 }

 

4.5 测试类,使用jwt工具,生成Jwt信息。

  1 package com.mi.demo.author;
  2 
  3 import io.jsonwebtoken.Claims;
  4 import io.jsonwebtoken.Jws;
  5 import io.jsonwebtoken.Jwts;
  6 import io.jsonwebtoken.SignatureAlgorithm;
  7 import org.joda.time.DateTime;
  8 
  9 
 10 import java.security.PrivateKey;
 11 import java.security.PublicKey;
 12 import java.util.Base64;
 13 import java.util.UUID;
 14 
 15 public class JwtUtils {
 16 
 17     private static final String JWT_PAYLOAD_USER_KEY = "user";
 18 
 19     /**
 20      *
 21      * 私钥加密token
 22      *
 23      * @param userInfo   载荷中的数据
 24      * @param privateKey 私钥
 25      * @param expire     过期时间,单位分钟
 26      * @return JWT
 27      */
 28     public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
 29         return Jwts.builder()
 30                 .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
 31                 .setId(createJTI())
 32                 .setExpiration(DateTime.now().plusMinutes(expire).toDate())
 33                 .signWith(privateKey, SignatureAlgorithm.RS256)
 34                 .compact();
 35     }
 36 
 37     /**
 38      * 私钥加密token
 39      *
 40      * @param userInfo   载荷中的数据
 41      * @param privateKey 私钥
 42      * @param expire     过期时间,单位秒
 43      * @return JWT
 44      */
 45     public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
 46         return Jwts.builder()
 47                 .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
 48                 .setId(createJTI())
 49                 .setExpiration(DateTime.now().plusSeconds(expire).toDate())
 50                 .signWith(privateKey, SignatureAlgorithm.RS256)
 51                 .compact();
 52     }
 53 
 54     /**
 55      * 公钥解析token
 56      *
 57      * @param token     用户请求中的token
 58      * @param publicKey 公钥
 59      * @return Jws<Claims>
 60      */
 61     private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
 62         return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
 63     }
 64 
 65     private static String createJTI() {
 66         return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
 67     }
 68 
 69     /**
 70      * 获取token中的用户信息
 71      *
 72      * @param token     用户请求中的令牌
 73      * @param publicKey 公钥
 74      * @return 用户信息
 75      */
 76     public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
 77         Jws<Claims> claimsJws = parserToken(token, publicKey);
 78         Claims body = claimsJws.getBody();
 79         Payload<T> claims = new Payload<>();
 80         claims.setId(body.getId());
 81         claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
 82         claims.setExpiration(body.getExpiration());
 83         return claims;
 84     }
 85 
 86     /**
 87      * 获取token中的载荷信息
 88      *
 89      * @param token     用户请求中的令牌
 90      * @param publicKey 公钥
 91      * @return 用户信息
 92      */
 93     public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
 94         Jws<Claims> claimsJws = parserToken(token, publicKey);
 95         Claims body = claimsJws.getBody();
 96         Payload<T> claims = new Payload<>();
 97         claims.setId(body.getId());
 98         claims.setExpiration(body.getExpiration());
 99         return claims;
100     }
101 }

 

  4.6 生成的公钥,私钥内容,位置,jwt内容。

 


 

 

                    


 

                    

posted on 2021-09-13 17:19  wangjiedadada  阅读(3394)  评论(1编辑  收藏  举报