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


浙公网安备 33010602011771号