欢迎来到我的博客|

hu_volsnow

园龄:3年9个月粉丝:1关注:6

OAuth2前置知识-jwt的生成

OAuth2前置知识-jwt的生成

小提示:其实也不是什么OAuth2的前置知识,只是后面会用到相关知识。主要是不知道起什么标题,哈哈哈哈

前面说了什么是jwt上篇

这次模拟一下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信息的加密和解析。

  1. 对于token的生成,可以通过对称加密或者非对称加密来加密。
    1. 对称加密:对称加密就是信息的发送方和接收方使用的是同一份秘钥,加密和解密过程使用的是同一份秘钥
    2. 非对称机密:非对称加密使用的是一对秘钥,就是加密和解密过程使用的不是同一把秘钥,而是一对秘钥:公钥和私钥,例如:可以通过公钥对我们的数据信息进行加密,私钥对我们的数据信息进行解密
    1. 公钥机密,私钥解密
    2. 私钥加密,公钥解密

下面就看看如何生成一对秘钥

非对称加密生成一对秘钥

  1. 引入相关的依赖
<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>
  1. 生成秘钥的工具类,当然可以通过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. <1>通过私钥加密组装生成一个token
eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjp7InVzZXJuYW1lIjoiaHh4LXdtIiwiYXV0aG9yaXRpZXNMaXN0IjpbeyJhdXRob3JpdHkiOiJST0xFX0FETUlOIn1dfSwianRpIjoiYzhmMzYyZTctMjZlOS00MjZjLTgzNzgtMmUxYjQxYzVlMDE3IiwiZXhwIjoxNjQwNzQyMjU2LCJpYXQiOjE2NDA2NTU4NTZ9.HeCDeuvfPn8_mPcy_PjgaRixn7VPwozRkqQZzYSCDgUthEBgJ4JTsoTqzLXnRX3uijUoQFIemMhOeA5ZacgIcBi0ruiPXfee_mW-1oYP9bIPnH7fIEl38k6PJi_X5jOgeqz0Ok8ONd2YBm5F2fhaXM3QMi8m9wkceKYa3KftjiI0d6Ce35ceFcy37uC9ChkbHeXWXPa0L5yrFo8CEqGzC52sfI4AtjpQ3RQf_M60MvzFgYvdDmYZFIwaPyMS0iDu9vOsATu2sEYi7Kd01401djf0EBZFSdCM3rIQx7cGOP40sWNdj241AkhaE7Y0uaK0fhuB5CCawDuNyY2KMOhG1A

在官网输入上面的token,通过解析出来后:

  1. <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 中国大陆许可协议进行许可。

posted @   hu_volsnow  阅读(385)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起