Jwt令牌创建

添加依赖

<dependencies>
        <!--   jwt     -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <!--注意这里要加上这些,不然证书无法加载-->
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.jks</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.jks</include>
                </includes>
            </resource>
        </resources>

    </build>

普通方式创建jwt令牌

package com.example.demo;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;

/**
 * @author liuyalong
 */
public class JwtTest {

    public static void main(String[] args) {
        final String keyStr = "4379";
        Key key = new SecretKeySpec(keyStr.getBytes(), SignatureAlgorithm.HS512.getJcaName());

        // expire_time为token有效时长, 单位毫秒
        final long expire_time =10000;
        Date expiresDate = new Date(System.currentTimeMillis() + expire_time);

        //生成jwt令牌
        JwtBuilder jwtBuilder = Jwts.builder()
                //设置jwt编码
                .setId("66")
                //设置jwt主题
                .setSubject("我是主题")
                //设置jwt签发日期
                .setIssuedAt(new Date())
                .claim("a", "admin")
                .claim("b", "aaaa")
                .claim("c", "yalong")
                //设置jwt的过期时间,好像必须在claim后面
                .setExpiration(expiresDate)
                //注意,这里的密码应该使用Key类型,不应使用String类型
                //如果使用String类型,比如4379123加密,那么解密使用4379也可以解开,
                // 因为这个String类型是base64EncodedSecretKey
                .signWith(SignatureAlgorithm.HS256, key);

        //生成jwt
        String jwtToken = jwtBuilder.compact();
        System.out.println(jwtToken);


        //解析jwt,得到其内部的数据
        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwtToken).getBody();
        System.out.println(claims.get("exp"));
        System.out.println(claims);
    }
}

使用keytool生成证书

keytool -genkeypair -alias test -keyalg RSA -keypass yalong -keystore "D://yalong.jks" -storepass yalong -validity 3650 -dname "CN=localhost,OU=localhost,O=localhost,L=SH,ST=SH,C=CN" -storetype pkcs12

注意:这里有个大坑,keypass 必须和storepass一样,不然私钥解不开,或许是keytool的原因造成的

  • alias:密钥的别名
  • keyalg:使用的hash算法
  • keypass:密钥的访问密码
  • keystore:密钥库文件名,yalong.jks保存了生成的证书
  • storepass:密钥库的访问密码
  • validity 3650 : 有效期10年
  • dname :证书的相关信息,
    • CN:名字与形式
    • OU:组织单位名称
    • O:组织机构名称
    • L:城市信息
    • ST:省份信息
    • C:国家信息

查看密钥库

keytool -list -keystore "D://yalong.jks" -storepass liuyalong

导出公钥证书文件.cer

keytool -export -alias test -keystore "D://yalong.jks" -file "D://publicKey.cer" -storepass yalong

使用密钥对生成JWT并加密解密

使用私钥生成JWT令牌,使用公钥解密,公钥可以公开,私钥留在授权服务里,公钥可以导出,私钥不可以导出

package com.example.demo;

import java.io.FileInputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;

/**
 * @author liuyalong
 */
public class GetRsaKey {

    public static void main(String[] args) throws Exception {
        //证书库文件路径
        // 这里使用ClassPathResource路径,所以不带D:/
        String storePath = "yalong.jks";

        //这个路径用来获取私钥
        String storePath1 = "D:/yalong.jks";

        //公钥证书文件路径
        //从keystore 导出公钥证书文件.cer,命令如下
        //keytool -export -alias test -keystore "D://yalong.keystore" -file "D://publicKey.cer"
        String cerPath = "D:/publicKey.cer";

        //证书别名
        String alias = "test";
        //证书库密码
        String storePw = "yalong";
        //证书密码
        String keyPw = "yalong";
        String publicKey = getPublicKey(cerPath);
        String privateKey = getPrivateKey(storePath1, alias, storePw, keyPw);

        System.out.println("从证书获取的公钥为:" + publicKey);
        System.out.println("从证书获取的私钥为:" + privateKey);


        Map<String, String> map = new HashMap<>();
        map.put("company", "honbow");
        map.put("address", "shenzhen");
        String jwt = createJwt(storePath, alias, storePw, keyPw, map);
        System.out.println("生成JWT为:" + jwt);

        String s = parseJwt(jwt, publicKey);
        System.out.println("校验后:" + s);
    }

    /**
     * 从公钥证书中获取公钥
     *
     * @param cerPath 公钥证书的路径
     */
    private static String getPublicKey(String cerPath) throws Exception {
        CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
        FileInputStream fis = new FileInputStream(cerPath);
        // X509Certificate cert = (X509Certificate) certificatefactory.generateCertificate(fis);
        Certificate cert = certificatefactory.generateCertificate(fis);
        PublicKey pk = cert.getPublicKey();
        String pkString = new Base64().encodeToString(pk.getEncoded());
        //这里需要拼接开头和结尾,不然无法解析出来结果,可能有其他验证方法
        return "-----BEGIN PUBLIC KEY-----" + pkString + "-----END PUBLIC KEY-----";
    }


    private static String getPublicKey(String cerPath, String instanceType) throws Exception {
        CertificateFactory certificatefactory = CertificateFactory.getInstance(instanceType);
        FileInputStream fis = new FileInputStream(cerPath);
        Certificate cert = certificatefactory.generateCertificate(fis);
        PublicKey pk = cert.getPublicKey();
        return new Base64().encodeToString(pk.getEncoded());
    }

    /**
     * @param storePath 证书库的路径
     * @param alias     证书别名,创建时指定的
     * @param storePw   访问证书库的密码
     * @param keyPw     访问证书的密码
     * @param JwtMap    需要设置进Jwt的内容
     * @return jwt 字符串
     */
    private static String createJwt(String storePath,
                                    String alias,
                                    String storePw,
                                    String keyPw,
                                    Map<String, String> JwtMap) {

        ClassPathResource classPathResource = new ClassPathResource(storePath);

        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, keyPw.toCharArray());

        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, storePw.toCharArray());

        //将当前的私钥转换为rsa私钥
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        Jwt jwt = JwtHelper.encode(JSON.toJSONString(JwtMap), new RsaSigner(rsaPrivateKey));
        return jwt.getEncoded();
    }

    /**
     * 生成私钥
     *
     * @param storePath 证书库的路径
     * @param alias     证书别名,创建时指定的
     * @param storePw   访问证书库的密码
     * @param keyPw     访问证书的密码
     */
    private static String getPrivateKey(String storePath, String alias, String storePw, String keyPw) throws Exception {
        FileInputStream is = new FileInputStream(storePath);
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(is, storePw.toCharArray());
        is.close();
        PrivateKey key = (PrivateKey) ks.getKey(alias, keyPw.toCharArray());
        return new Base64().encodeToString(key.getEncoded());
    }

    private static String getPrivateKey(String storePath, String alias, String storePw, String keyPw, String storeType) throws Exception {
        FileInputStream is = new FileInputStream(storePath);
        KeyStore ks = KeyStore.getInstance(storeType);
        ks.load(is, storePw.toCharArray());
        is.close();
        PrivateKey key = (PrivateKey) ks.getKey(alias, keyPw.toCharArray());
        return new Base64().encodeToString(key.getEncoded());
    }


    /**
     * @param jwt jwt字符串
     * @param publicKey 公钥
     * @return 解密后的字符串token
     */
    private static String parseJwt(String jwt, String publicKey) {

        Jwt token = JwtHelper.decodeAndVerify(jwt, new RsaVerifier(publicKey));

        return token.getClaims();
    }
}

RSA的公钥和私钥到底哪个才是用来加密和哪个用来解密?

  • 既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密,私钥负责解密;同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名,公钥负责验证。

注:

JWT保证的是数据传输过程中的完整性而不是机密性。
由于payload是使用base64url编码的,所以相当于明文传输,如果在payload中携带了敏感信息(如存放密钥对的文件路径),单独对payload部分进行base64url解码,就可以读取到payload中携带的信息。

posted @ 2020-12-09 15:42  rm-rf*  阅读(312)  评论(0编辑  收藏  举报