OAuth2前置知识-jwt的生成
OAuth2前置知识-jwt的生成
(小提示:其实也不是什么OAuth2的前置知识,只是后面会用到相关知识。主要是不知道起什么标题,哈哈哈哈
)
这次模拟一下jwt的生成
这是上篇文章中的一张图,这张图是基于JWT的认证
我们知道,在生成token中,包括三个部分,其中签名=算法(base64Url(header)+base64Url(payload),'secret-key')。header中会显示签名算法,payload也会显示的返回,那么如何避免前端传递过来的token被伪造,重担就交给secret-key 了,也可以说成盐,这个secret-key在后端生成,那么如何保证这个盐的安全性,这时可以采用非对称加密的方式对盐进行加密,后面提到的秘钥就是已经加密的盐。
在第4步中,服务器是如何进行校验浏览器发送的token的呢?
服务器会通过签名算法以及key(秘钥)解析计算token ,如果解析不成功,说明token有可能被篡改过,认证失败
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwtToken);
所以这个秘钥key的生成和存储显得非常重要,这个key涉及到了我们header payload信息的加密和解析。
- 对于token的生成,可以通过对称加密或者非对称加密来加密。
- 对称加密:对称加密就是信息的发送方和接收方使用的是同一份秘钥,加密和解密过程使用的是同一份秘钥
- 非对称机密:非对称加密使用的是一对秘钥,就是加密和解密过程使用的不是同一把秘钥,而是一对秘钥:公钥和私钥,例如:可以通过公钥对我们的数据信息进行加密,私钥对我们的数据信息进行解密
- 公钥机密,私钥解密
- 私钥加密,公钥解密
下面就看看如何生成一对秘钥
非对称加密生成一对秘钥
- 引入相关的依赖
<properties> <jjwt.version>0.11.2</jjwt.version> </properties> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>${jjwt.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>${jjwt.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>${jjwt.version}</version> <scope>runtime</scope> </dependency>
- 生成秘钥的工具类,当然可以通过keytool去生成,主要是通过KeyPairGenerator去生成一对秘钥
public class SecretKeyUtil { /** * 生成秘钥,写到指定的文件中 * (也可以根据keytool工具 去生成) * RSA 表示一种算法 * * @param publicKeyFilePath 公钥路径 * @param privateKeyFilePath 私钥路径 * @param secret 生成秘钥的密文 */ public static void generateKey(String publicKeyFilePath, String privateKeyFilePath, String secret) throws NoSuchAlgorithmException, IOException { //keyPairGenerator RSA 表示一种非对称加密的算法 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048,new SecureRandom(secret.getBytes())); //keyPair KeyPair keyPair = keyPairGenerator.genKeyPair(); //获得公钥 byte[] publicKey = keyPair.getPublic().getEncoded(); //获得私钥 byte[] privateKey = keyPair.getPrivate().getEncoded(); //生成的key 放到一个指定的文件中 writeToFile(publicKeyFilePath, Base64.getEncoder().encode(publicKey)); writeToFile(privateKeyFilePath,Base64.getEncoder().encode(privateKey)); } // 根据文件 获得公钥 public static PublicKey getPublicKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] bytes = Files.readAllBytes(new File(filename).toPath()); //解析 bytes = Base64.getDecoder().decode(bytes); X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePublic(spec); } //根据文件 获得私钥 public static PrivateKey getPrivateKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] bytes = Files.readAllBytes(new File(filename).toPath()); //解析 bytes = Base64.getDecoder().decode(bytes); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(spec); } private static void writeToFile(String filePath, byte[] key) throws IOException { File dest = new File(filePath); File dir = dest.getParentFile(); if(!dir.exists()){ dir.mkdir(); } if(!dest.exists()){ dest.createNewFile(); } Files.write(dest.toPath(), key); } //测试一下 生成公钥 私钥 到文件中 public static void main(String[] args) throws IOException, NoSuchAlgorithmException { // File file = new File("src/main/resources/test/test.txt"); // System.out.println(file.getAbsoluteFile()); String publicKeyPath = "key/public_key.txt"; String privateKeyPath = "key/private_key.txt"; String secret = "hxx-secret"; generateKey(publicKeyPath,privateKeyPath,secret); } }
jwt的生成
创建一个实际的数据信息类
@Data @AllArgsConstructor @NoArgsConstructor public class TokenInfo { private String username; // 实现spring security 的 GrantedAuthority private List<ClaimAuthorities> authoritiesList; @Data public static class ClaimAuthorities implements GrantedAuthority { private String authority; @Override public String getAuthority() { return this.authority; } } }
下面是生成Jwt的工具类
/** * JWT 生成器 * * @author hxx */ public class JwtUtil { /** * 通过公钥publicKey 生成token * * @param claimInfo * @param key 秘钥 * @return */ <1> public static String generateToken(TokenInfo claimInfo, Key key) { long expire = 24 * 60 * 60 * 1000; long now = System.currentTimeMillis(); String token = Jwts.builder().claim("user", claimInfo) // claim 数据信息 .setId(UUID.randomUUID().toString()) //jti .setExpiration(new Date(now + expire)) //失效日期 .setIssuedAt(new Date(now)) //签发日期 .signWith(key, SignatureAlgorithm.RS256) //通过RS256签名算法 .compact(); return token; } /** * 通过私钥进行解密token * * @param token * @param key * @return */ public static Jws<Claims> parseToken(String token, Key key) { return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); } <2> public static TokenInfo getFromClaimToken(String token, Key key) { Jws<Claims> claimsJws = parseToken(token, key); Claims body = claimsJws.getBody(); TokenInfo info = new TokenInfo(); ObjectMapper mapper = new ObjectMapper(); info = mapper.convertValue(body.get("user"),TokenInfo.class); // claimsJws.get return info; } public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeySpecException { TokenInfo info = new TokenInfo(); //权限 List authorities = new ArrayList<>(); TokenInfo.ClaimAuthorities claimAuthorities = new TokenInfo.ClaimAuthorities(); claimAuthorities.setAuthority("ROLE_ADMIN"); authorities.add(claimAuthorities); info.setAuthoritiesList(authorities); info.setUsername("hxx-wm"); PrivateKey privateKey = SecretKeyUtil.getPrivateKey("key/private_key.txt"); String token = generateToken(info, privateKey); System.out.println(token); //通过公钥解密 PublicKey publicKey = SecretKeyUtil.getPublicKey("key/public_key.txt"); TokenInfo fromClaimToken = getFromClaimToken(token, publicKey); System.out.println(fromClaimToken); } }
主要是通过Jwts来组装token。详情看generateToken方法
- <1>通过私钥加密组装生成一个token
eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjp7InVzZXJuYW1lIjoiaHh4LXdtIiwiYXV0aG9yaXRpZXNMaXN0IjpbeyJhdXRob3JpdHkiOiJST0xFX0FETUlOIn1dfSwianRpIjoiYzhmMzYyZTctMjZlOS00MjZjLTgzNzgtMmUxYjQxYzVlMDE3IiwiZXhwIjoxNjQwNzQyMjU2LCJpYXQiOjE2NDA2NTU4NTZ9.HeCDeuvfPn8_mPcy_PjgaRixn7VPwozRkqQZzYSCDgUthEBgJ4JTsoTqzLXnRX3uijUoQFIemMhOeA5ZacgIcBi0ruiPXfee_mW-1oYP9bIPnH7fIEl38k6PJi_X5jOgeqz0Ok8ONd2YBm5F2fhaXM3QMi8m9wkceKYa3KftjiI0d6Ce35ceFcy37uC9ChkbHeXWXPa0L5yrFo8CEqGzC52sfI4AtjpQ3RQf_M60MvzFgYvdDmYZFIwaPyMS0iDu9vOsATu2sEYi7Kd01401djf0EBZFSdCM3rIQx7cGOP40sWNdj241AkhaE7Y0uaK0fhuB5CCawDuNyY2KMOhG1A
在官网输入上面的token,通过解析出来后:
- <2>通过公钥解密生成的token
解密出来得到的TokenInfo:TokenInfo(username=hxx-wm, authoritiesList=[TokenInfo.ClaimAuthorities(authority=ROLE_ADMIN)])
下面是代码
可以看看 解析之后,生成的jws是一个什么样子的,其实解析出来就是我们的三大部分:头部、payload、sig
本文作者:hu_volsnow
本文链接:https://www.cnblogs.com/volsnow/p/15739583.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步