JWT简介

  • JWT 是基于 RFC 7519 标准定义的一种可以安全传输的规范, 这个规范允许我们使用 JWT 在前后端之间传递安全可靠的信息。
  • JWT 由于使用了数字签名,所以是可信任和安全的。

通过 session 管理用户登录状态成本越来越高,使用无状态的 token 的方式做登录身份校验显得越来越流行,不仅可以减轻服务器的压力, 由于 token 是在授权头(Authorization header)中发送的,因此做单点登陆的时候还没有cookie跨域问题.

而 JWT(java web token) 就是目前最流行的单点登录跨域身份验证解决方案。

JWT 组成

一个 JWT 实际上就是一个字符串,未加密前的 jwt 就是一个json, 它由头部、载荷与签名三部分组成。

  1. header(头部): 用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等。

    {"typ": "JWT","alg": "HS256"
    }
    

    header 主要包含两个部分, alg 指加密类型,可选值为 HS256RSA 等等,typ=JWT 为固定值,表示 token 的类型。

  2. payload(载荷): 载荷就是存放有效信息的地方。

    {"sub": "1340502208","name": "John Doe","role": "admin"
    }
    

    官方定义的 payload 一般包括以下内容

    • iss:Issuer,发行者
    • sub:Subject,主题
    • aud:Audience,观众
    • exp:Expiration time,过期时间
    • nbf:Not before
    • iat:Issued at,发行时间
    • jti:JWT ID
  3. signature(签证): jwt 的第三部分是一个签证信息,这个签证信息由三部分组成

    1. 头部经 base64 编码后的字符
    2. 是载荷经 base64 编码后的字符
    3. 是盐(密钥),通常存于服务器

    之后将12.连接,通过头部中声明的加密算法进行加盐(3)计算,得到第三部分, 伪代码如下.

    EncodeString = Base64(header) + "." + Base64(payload)
    token = HS256(EncodeString, "秘钥")
    

    签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的 token,它还可以验证 JWT 的发送方是否为它所称的发送方。

  • 下面是一个JWT示例, 对应的密钥是 123456

       eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0ODIzIiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDY1MzM3LCJleHAiOjE2NDM0Njg5MzcsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.JBmv97KOYYNRsKKcHpy26uLPN6JrwJzV_WdqzIpwEx0
    

    可以去 https://jwt.io/ 上面去验证解析.

JWT 工作原理

  1. 首先, 用户登录后, 服务器向前台发送一个 JWT.

  2. 用户想要访问受保护的路由或者资源的时候,应当携带 JWT,通常放在请求头中的 Authorization 上, 如 Bearer token

  3. 服务器的路由或网关去检查 Authorization header 中的 JWT 是否有效,如果有效,则用户可以访问受保护的资源。

JWT 验证过程

  1. 签名验证

    服务器获取到一个 JWT 的时候,首先要对这个 JWT 的完整性进行验证,这个就是签名认证。

    验证方式如下:

    1. 截开 JWT 前缀, 根据上面内容来看是 Bearer, 获取到 token.

    2. 分离 token 为 header, payload, signature.

    3. 对 header 做 base64 解码, 获得 JWT 使用的算法编码.

    4. 使用后端保存的密钥, 和得到的编码对 header, payload 进行签证计算, 算出 signature.

      如 header 使用的编码为 HS256. 则执行以下伪代码.

      EncodeString = Base64(header) + "." + Base64(payload)
      token = HS256(EncodeString, "秘钥")
      

      接收方生成签名的时候必须使用跟 JWT 发送方相同的密钥,意味着要做好密钥的安全传递或共享

    5. 将算出的 signature 与 token 带的 signature 进行对比, 若完全相同, 表明信息是正确的. 若不同,就可以认为这个 JWT 是一个被篡改过的串,自然就属于验证失败了。

  2. 载体验证

    在验证一个 JWT 的时候,签名认证是每个实现库都会自动做的,但是 payload 的认证是由使用者来决定的。由于载体里面存放的内容根据需求各有不同, 因此, 载体验证一般需要由使用者亲自完成.

    以下是官方的载体验证规范.

    • iss(Issuser):如果签发的时候这个 claim 的值是“a.com”,验证的时候如果这个 claim 的值不是“a.com”就属于验证失败
    • sub(Subject):如果签发的时候这个 claim 的值是“liuyunzhuge”,验证的时候如果这个 claim 的值不是“liuyunzhuge”就属于验证失败
    • aud(Audience):如果签发的时候这个 claim 的值是“[‘b.com’,‘c.com’]”,验证的时候这个 claim 的值至少要包含 b.com,c.com 的其中- 一个才能验证通过
    • exp(Expiration time):如果验证的时候超过了这个 claim 指定的时间,就属于验证失败;nbf(Not Before):如果验证的时候小于这- 个 claim 指定的时间,就属于验证失败
    • iat(Issued at):它可以用来做一些 maxAge 之类的验证,假如验证时间与这个 claim 指定的时间相差的时间大于通过 maxAge 指定的一个- 值,就属于验证失败
    • jti(JWT ID):如果签发的时候这个 claim 的值是“1”,验证的时候如果这个 claim 的值不是“1”就属于验证失败

    以登录认证来说,在签发 JWT 的时候,完全可以只用 sub 跟 exp 两个 claim,用 sub 存储用户的 id,用 exp 存储它本次登录之后的过期时间,然后在验证的时候仅验证 exp 这个 claim,以实现会话的有效期管理。

JWT 生成与解析代码实例

  1. 引入依赖

    <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
    </dependency>
    
  2. 编写测试类

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import java.util.Date;public class JwtTest {@Testpublic void testJwt() throws InterruptedException {Date date = new Date();final String encodeString = "123456";//生成jwt令牌JwtBuilder jwtBuilder = Jwts.builder()// 设置载荷 : jwt编码, 对象, 签发日期, 过期时间.setId("4823").setSubject("CPF").setIssuedAt(date).setExpiration(new Date(date.getTime() + 3600 * 1000))// 设置自定义的载荷.claim("roles","admin,user").claim("email","123@oh.com")// 使用 HS256 对称加密算法签名, 第二个参数为秘钥.signWith(SignatureAlgorithm.HS256, encodeString);// 生成 jwtfinal String jwtToken = jwtBuilder.compact();System.out.println("生成的JWT: " + jwtToken);// 解析 jwtfinal Claims claims = Jwts.parser().setSigningKey(encodeString).parseClaimsJws(jwtToken).getBody();System.out.println("解析的内容: " + claims);// 更换一个 IDjwtBuilder.setId("1234");final String jwtToken2 = jwtBuilder.compact();System.out.println("\n替换id后的Jwt: " + jwtToken2);final String[] split1 = jwtToken.split("\\.");final String[] split2 = jwtToken2.split("\\.");final String errToken = split1[0] + "." + split2[1] + "." + split1[2];System.out.println("将两个JWT的载荷进行拼接JWT : " + errToken);System.out.println("\n然后就发现解析会失败!");// 解析 jwtJwts.parser().setSigningKey(encodeString).parseClaimsJws(errToken).getBody();}}
    
  3. 输出

    生成的JWT: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0ODIzIiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDQ4MzEyLCJleHAiOjE2NDM0NTE5MTIsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.h-0EDrWj7Lnqfz7Jnjt0DKrGwvdicaA8BKlxJroYWB0
    解析的内容: {jti=4823, sub=CPF, iat=1643448312, exp=1643451912, roles=admin,user, email=123@oh.com}替换id后的Jwt: eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0Iiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDQ4MzEyLCJleHAiOjE2NDM0NTE5MTIsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.4cf8EQO1Vfm9Bcu2GCIJGlAt4eKbqb_Q_26-tfFUHao
    将两个JWT的载荷进行拼接JWT : eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0Iiwic3ViIjoiQ1BGIiwiaWF0IjoxNjQzNDQ4MzEyLCJleHAiOjE2NDM0NTE5MTIsInJvbGVzIjoiYWRtaW4sdXNlciIsImVtYWlsIjoiMTIzQG9oLmNvbSJ9.h-0EDrWj7Lnqfz7Jnjt0DKrGwvdicaA8BKlxJroYWB0然后就发现解析会失败!Exception in thread "main" io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354)at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
    

JWT 使用 RSA 加密实例

相对于 HS256 对称加密, RSA 非对称加密更加安全一些.

为了生成 JWT Token 我们还需要使用 RSA 算法来进行签名。这里我们使用 JDK 提供的证书管理工具 Keytool 来生成 RSA 证书 ,格式为 jks 格式。

JDK-keytool 生成证书命令参考:

keytool -genkey -alias felordcn -keypass felordcn -keyalg RSA -storetype PKCS12 -keysize 1024 -validity 365 -keystore d:/keystores/felordcn.jks -storepass 123456 -dname "CN=(Felord), OU=(felordcn), O=(felordcn), L=(zz), ST=(hn), C=(cn)"

命令参数详解

参数 默认 含义
genkey 在用户主目录中创建一个默认文件".keystore",还会产生一个 mykey 的别名,mykey 中包含用户的公钥、私钥和证书
alias mykey 产生别名
keystore 用户系统默认目录 指定密钥库的名称(产生的各类信息将不在.keystore 文件中)
keyalg DSA 指定密钥的算法 (如 RSA DSA(如果不指定默认采用 DSA))
validity 指定创建的证书有效期多少天
keysize 1024 指定密钥长度
storepass 指定密钥库的密码(获取 keystore 信息所需的密码)
keypass 指定别名条目的密码(私钥的密码)
dname 指定证书拥有者信息 例如: “CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码”
list 显示密钥库中的证书信息 keytool -list -v -keystore 指定 keystore -storepass 密码
v 显示密钥库中的证书详细信息
export 将别名指定的证书导出到文件 keytool -export -alias 需要导出的别名 -keystore 指定 keystore -file 指定导出的证书位置及证书名称 -storepass 密码
file 参数指定导出到文件的文件名
delete 删除密钥库中某条目 keytool -delete -alias 指定需删除的别 -keystore 指定 keystore -storepass 密码
printcert 查看导出的证书信息 keytool -printcert -file yushan.crt
keypasswd 修改密钥库中指定条目口令 keytool -keypasswd -alias 需修改的别名 -keypass 旧密码 -new 新密码 -storepass keystore 密码 -keystore sage
storepasswd 修改 keystore 口令 keytool -storepasswd -keystore e:/yushan.keystore(需修改口令的 keystore) -storepass 123456(原始密码) -new yushan(新密码)
import 将已签名数字证书导入密钥库 keytool -import -alias 指定导入条目的别名 -keystore 指定 keystore -file 需导入的证书

keytool 生成命令实操

  1. 用 JDK 中的 keytool 生成 RSA 证书, 生成 rsa.jws

    keytool -genkey -keyalg RSA -alias oh -keypass 212003 -storepass 456123 -keystore P:\git\rsa.jws

    其中 -alias oh -keypass 212003 -storepass 456123 我们要作为配置使用要记下来。我们要使用下面定义的这个类来读取证书

    • 在控制台输入之后, 会提示名字与姓氏, 组织名称之类的乱七八糟的东西, 先填就填, 不填可以直接回车通过, 最后在否的地方输入个 y 就可以.
    • 最后还会提示 迁移到行业标准格式 PKCS12 之类的, 可以不用管,
    D:\programing\sdk\jdk-current\bin>keytool -genkey -keyalg RSA -alias oh -keypass 212003 -storepass 456123 -keystore P:\git\rsa.jws
    您的名字与姓氏是什么?
    [Unknown]:  chen
    您的组织单位名称是什么?
    [Unknown]:  hydroxyl
    您的组织名称是什么?
    [Unknown]:
    您所在的城市或区域名称是什么?
    [Unknown]:
    您所在的省/市/自治区名称是什么?
    [Unknown]:
    该单位的双字母国家/地区代码是什么?
    [Unknown]:
    CN=chen, OU=hydroxy, O=Unknown, L=Unknown, ST=Unknown, C=Unknown是否正确?
    [否]:  yWarning:
    JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore P:\git\rsa.jws -destkeystore P:\git\rsa.jws -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。
    
  2. 通过代码去验证生成的rsa.jwt文件是否能正常使用

    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import java.io.FileInputStream;
    import java.security.*;
    import java.security.cert.Certificate;
    import java.util.Base64;
    import java.util.Enumeration;public class RsaUtils {private static Signature getSignature(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException {Signature signature;if (null == provider || provider.length() == 0) {signature = Signature.getInstance(algorithm);} else {signature = Signature.getInstance(algorithm, provider);}return signature;}/*** 验签*/public static boolean verify(byte[] message, byte[] signMessage, PublicKey publicKey, String algorithm,String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException {Signature signature = getSignature(algorithm, provider);signature.initVerify(publicKey);signature.update(message);return signature.verify(signMessage);}/*** 签名*/public static byte[] sign(byte[] message, PrivateKey privateKey, String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException {Signature signature = getSignature(algorithm, provider);signature.initSign(privateKey);signature.update(message);return signature.sign();}/*** 公钥加密*/public static byte[] encrypt(byte[] content, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(content);}/*** 私钥解密*/public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(content);}@SuppressWarnings("java:S106")public static void main(String[] args) throws Exception {final String jwtFilePath = "P:\\git\\rsa.jws";final String storePass = "456123";final String keyPass = "212003";final String keyAlias = "oh";// 加载 JWS 文件KeyStore keyStore = KeyStore.getInstance("JKS");try (FileInputStream in = new FileInputStream(jwtFilePath)) {keyStore.load(in, storePass.toCharArray());}// 获取公钥Certificate certificate = keyStore.getCertificate(keyAlias);PublicKey publicKey = certificate.getPublicKey();String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());System.out.println("publicKey ==> " + publicKeyString);// 加载私钥, 加载私钥需要密码PrivateKey privateKey  = (PrivateKey) keyStore.getKey(keyAlias, keyPass.toCharArray());String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());System.out.println("privateKey ==> "+ privateKeyString);System.out.println();/* 公钥加密, 私钥解密 */final String secretString = "这是密文-这是密文-这是密文";System.out.println("加密前 ==> " + secretString);byte[] secretByte = encrypt(secretString.getBytes(), publicKey);System.out.println("公钥加密 ==> " + Base64.getEncoder().encodeToString(secretByte));byte[] decodeByte = decrypt(secretByte, privateKey);System.out.println("私钥解密 ==> " + new String(decodeByte));System.out.println();/* 私钥签名加密, 公钥验证 */final String signSecretString = "密文信息";// 测试签名final byte[] sha1withRSAS = sign(signSecretString.getBytes(), privateKey, "SHA1withRSA", null);System.out.println("私钥签名: " + Base64.getEncoder().encodeToString(sha1withRSAS));// 公钥验证boolean verify = verify(signSecretString.getBytes(), sha1withRSAS, publicKey, "SHA1withRSA", null);System.out.println("公钥验证: " + verify);}
    }
    
  3. 打印输出

    keyAlias: oh
    publicKey ==> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlQUvw6ZEAhlxpSZWPyHuFsFiWAYgdSiM4hEVxzweZsOMian+VvsonVUzv9358Zqh+rZ/i1uQ5CeJmarfXL83LM7KNcv/O7YP98wh9N3BtCpyQwo9wcx0Ph7//T4mWnc75qCmTeOxIidi68NRisHDzg1T61u2LDyALWtb1SoOV5wt3Nts0cq+lPNxh4QTGGe2nP+mbI88VxQviCTUNneKPpBiJ7rQ2E8UYwC7WYrqLc7ZlpuYCUsZvDfsxFvBqWnCAuVMd/GxBkrOB+YbWCh7ez5TdEofU1LrgsPyrILRR7kW7NZZ2nCGfv/p8TEYqaDiyXZZzGvmDwsD/8b3YYgohwIDAQAB
    privateKey ==> MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCVBS/DpkQCGXGlJlY/Ie4WwWJYBiB1KIziERXHPB5mw4yJqf5W+yidVTO/3fnxmqH6tn+LW5DkJ4mZqt9cvzcszso1y/87tg/3zCH03cG0KnJDCj3BzHQ+Hv/9PiZadzvmoKZN47EiJ2Lrw1GKwcPODVPrW7YsPIAta1vVKg5XnC3c22zRyr6U83GHhBMYZ7ac/6ZsjzxXFC+IJNQ2d4o+kGInutDYTxRjALtZiuotztmWm5gJSxm8N+zEW8GpacIC5Ux38bEGSs4H5htYKHt7PlN0Sh9TUuuCw/KsgtFHuRbs1lnacIZ+/+nxMRipoOLJdlnMa+YPCwP/xvdhiCiHAgMBAAECggEAVM4O0JjeOxOfyQx4KJV2mRyUiuNxtTrOchim/CsKYhEG+ZD0XSuxgVfri1UX2JbXd4ZEL1p8qlqVxA2p724iSC2mhdcB+Uky7SIOcPuCMLW3MM+zNYbU4EVkCQpFaVZRkH38JnddZsJjWSheT0jV1X1gNKCMm8ASccaXDEhSwSgP2/0O7YbN2AuS6gP/9Ut/0zlX6KVNUQGmYZHA5Zyd2cPCsstkli/Jz9U6Cmyl4x0NH8PJo5d6Zyda/47BR5WHo37WIU6skejPYK94O4ymKnGGoT+SRFFaoMmrHgmAdX9uOoskYh+HHtwLbcQCNGo+8eiK8Lv6CQUQX4XLxeIbgQKBgQDfh7cero/xsPNGO/6vQ3vdFVwbwvb/7zjHRCS27F19Oz83B29Mj8WJbCAD+DNap16zrhby3cPPNUwvXocGbEltKrgnZWocBEpdoZRN4GgDt5lLuxUQ9ZI2DJscr9LSUJaIPD8r505xO4syHBRgshtXmF4dLgQHDDsPiaOKYxq75QKBgQCqqrdI5zn0GxFWFonTBQ4whPCr7O+dcmhCyyNZ3J/K9wF13xKTwfkDqeXPbyJLN1g0bUoLXZnBXbmJEvTndFIyaFS5qdWSXjeP2qGVYcDlNc8V3ynwk8LpnQH8e4CsDhbH1vb0F1kmvJH4c5ecrDTo5exwSEGUE58g6KSAWkFD+wKBgQCXtOVMdo8NKtpBHbDBxJxJNRj5Yn3+v54aZ54/Y/Yja1WBBJO+M4mOtgqYhxhbe2JjslCy7l3ZwMN/FrmvW0kORUMMweCdOTA7kdE0dYxCkZYB9uvaQcDE3BNeCdqckMNJnRIGuwrbAN182d/erKKv9aJSTYvAOMXQyspqvs5DHQKBgQCkMBKeP11guz2lbY9whJePFAYZ0JsBBNTLFXTP+dF8yL8N7+qGXgE7hhLByi/a3sarwUyPvJ+0CH/7IFKd7Sk6t2ZzK7F828lmSrZS6TVTDb5JU2WcvfqxFsyXYxV58R/3Z5YzY9bvzlA8DrCYGI/aU4Bw0QLN+0aGuWmw1aOeSwKBgDqbOYI6R7Lu5V5uBTD99cNfK9xtygLy3BVOQVAnyubUL1IWZMuwKKOiPAFRiSwin3OvcQVFJfndgifWRnUqWQdNktXr/xZGUa7t1yVSzuvLtZeTVkzjyeiWNLO2QqUdJsC4ZsDm9BUoK3IzFWCXrQxJU+MboiYAaopA5ZKJaRI7加密前 ==> 这是密文-这是密文-这是密文
    公钥加密 ==> HYQeegMdF9Jmp9jd2mvkGjaMJ0as/gVLnfp1WjxAG62/HNfVXQWpJZZa/4aioiHYP23JYuM3a84hMw+Rb2/fkZJrSWEZBF2IbOtzPsBi2EqOKrzTdnc4FyCWNnJHwMyz2TpTsQDuU1QB3imsuUvr2xz5OQpD4KiyiechpreWqFw8Ns+1lTMGyRIyTba9bFGTBvjR2KSBVro9cUN0XaCKiqMzwjg7E7CBiO4GgIfU6fCiGAPydnYUDsAufYv/qBSKv5EuFnXuo8uWzbfxaHqzHycQ/eRXlDHIfAIlYrk/jzFz2jEtUL6D49IFstHsykWzu+mzu/EXT8P4s5FWk6ZKzg==
    私钥解密 ==> 这是密文-这是密文-这是密文私钥签名: bXCQ974mmP5q3F19Pwzj3u+UHBTOWrRA057+RgWXu3EDbQa+w1pHo8S4YInI+JJFN18OPVoQOrIdoa3ipwJgTa1rNuDsCOE5FfPa2yqiHDpD/t6q11L5cGC91+Y5c46NSBrXUTjpta4t+0Y6Xld62vVrAHq7XdrieLkbzn2+gQGPbkxiLxkvHoJDTl0S5EB86qcMlezRcxKWvzGPmugK83YMMUcBVi12YHB583EJeQ2QKXIIJqOLE8FL45zudhqfVOcgXXifRs0T5mwN0hBEHgi4cc2jbbViZwodf81NM0USaolNPVcB1WbWcmCn/MUS2dp8vTDAN0cmOMVD17SMxw==
    公钥验证: true
    

附: 单点登录: Spring security + auth2+ JWT + RSA 相关局部配置

这仅仅是部分配置: 针对 Jwt 转换器 的配置

package cn.hydroxyl.sso.auth2.config;import cn.hydroxyl.sso.auth2.config.comp.JwtTokenEnhancer;
import cn.hydroxyl.sso.auth2.service.UserServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {private final UserServiceImpl userDetailsService;private final AuthenticationManager authenticationManager;private final JwtTokenEnhancer jwtTokenEnhancer;/*** 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。** 1. 密码模式下配置认证管理器 AuthenticationManager 以及 jwt* 2. 设置 AccessToken的存储介质tokenStore, 默认使用内存当做存储介质。*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {List<TokenEnhancer> delegates = new ArrayList<>();delegates.add(jwtTokenEnhancer);delegates.add(accessTokenConverter());// 密码模式下使用 authenticationManager 管理器endpoints.authenticationManager(authenticationManager)// 配置加载用户信息的服务.userDetailsService(userDetailsService).accessTokenConverter(accessTokenConverter());}/*** Jwt 转换器*/@Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();jwtAccessTokenConverter.setKeyPair(keyPair());return jwtAccessTokenConverter;}@Beanpublic KeyPair keyPair() {// rsa 密钥文件, 以及对应的 storePass, keyPass, aliasfinal ClassPathResource rsaJksRes = new ClassPathResource("rsa.jks");final String storePass = "456123";final String keyPass = "212003";final String alias = "oh";return new KeyStoreKeyFactory(rsaJksRes, storePass.toCharArray()).getKeyPair(alias, keyPass.toCharArray());}
}

参考自:

  • https://www.cnblogs.com/wrc-blog/p/14256071.html
  • https://www.jianshu.com/p/e34a579c63a0

针对JWT简介与原理,代码实例,以及oauth2+JWT+RSA的集成配置相关推荐

  1. java cas登陆实例_Java CAS基本实现原理代码实例解析

    一.前言 了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的简称.它有核心就是CAS与AQS.CAS是java.util.concurrent.a ...

  2. jwt 例子 java_spring boot 入门之security oauth2 jwt完美整合例子-java编程

    一.本文简介 本文主要讲解Java编程中spring boot框架+spring security框架+spring security oauth2框架整合的例子,并且oauth2整合使用jwt方式存 ...

  3. 「springcloud 2021 系列」Spring Cloud Gateway + OAuth2 + JWT 实现统一认证与鉴权

    通过认证服务进行统一认证,然后通过网关来统一校验认证和鉴权. 将采用 Nacos 作为注册中心,Gateway 作为网关,使用 nimbus-jose-jwt JWT 库操作 JWT 令牌 理论介绍 ...

  4. 【学习笔记】Eureka服务治理代码实例、相关配置和原理机制详解

    文章目录 代码示例 启动一个服务注册中心 注册服务提供者 高可用注册中心 服务的发现与消费 Eureka的一些配置 服务注册类配置 服务实例类配置 实例名配置 端点配置 Eureka服务治理基础架构原 ...

  5. AIGC之GPT-4:GPT-4的简介(核心原理/意义/亮点/技术点/缺点/使用建议)、使用方法、案例应用(计算能力/代码能力/看图能力等)之详细攻略

    AIGC之GPT-4:GPT-4的简介(核心原理/意义/亮点/技术点/缺点/使用建议).使用方法.案例应用(计算能力/代码能力/看图能力等)之详细攻略 解读:在2022年11月横空出世的ChatGPT ...

  6. 【编程实践】Raft 算法的原理 go代码实例

    文章目录 Raft 算法的原理 & go代码实例 Raft 算法的原理 使用 Go 语言实现的简单 Raft 算法示例 Raft 算法的原理 & go代码实例 Raft 算法的原理 R ...

  7. c语言滚动字幕的原理编程,c#中通过Graphics.DrawString实现滚动字幕的原理和代码实例...

    c#中通过Graphics.DrawString实现滚动字幕的原理和代码实例 在c#中其实滚动屏幕的实现很简单,只需要用到Graphics.DrawString方法. Graphics.DrawStr ...

  8. python遍历queryset_Django QuerySet查询集原理及代码实例

    一 概念 Django的ORM中存在查询集的概念. 查询集,也称查询结果集.QuerySet,表示从数据库中获取的对象集合. 当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表): a ...

  9. TFRecord简介,原理分析,代码实现?

    TFRecord简介,原理分析,代码实现? 在利用深度学习算法搭建完成网络之后,我们要对网络进行训练,要训练网络就要有训练数据,通常我们会直接对硬盘上存放数据进行操作,来fetch到网络中.这样直接从 ...

最新文章

  1. CG CTF CRYPTO Keyboard
  2. 虚拟光驱的开发者斟酌了很久
  3. Altium AD20焊盘样式、热焊盘与反焊盘与直接连接
  4. Oracle 痛裁程序员,阿里云坐收渔翁利?
  5. webdriver原理(自己做个记录)
  6. 2020年浙江省土地利用数据(矢量)
  7. 海康威视摄像头+OpenCV+VS2017 图像处理小结(一)
  8. 控制系统设计专题(一)——PID控制算法
  9. Vmware安装Vmware Tools工具
  10. sql 2008 R2 备份和还原
  11. 双拼输入法的原理及上手方法
  12. Linux_29_Linux-Vsftpd
  13. java 下载文件的文件名乱码_JAVA 文件下载时的文件名乱码解决
  14. 2020-12-22 ACM集训一(二维数组与结构体)
  15. Python运维开发(CMDB资产管理系统)——环境部署(下)
  16. int型整数的最小值和最大值是多少(精确值)
  17. 无锡中软国际有限公司笔试题(Java)
  18. 【算法和数据结构】模拟和暴力
  19. 解决ROS中运行launch文件报错ERROR: cannot launch node of type[xxx/xxx]:xxx的问题办法最全汇总
  20. 湖北省制造业高质量发展专项奖励申报条件,2022年揭榜挂帅项目指南

热门文章

  1. 基于迁移学习的PyTorch图像分类
  2. function函数的各种写法
  3. 应届生找嵌入式工作难吗?
  4. 5年1万亿:揭秘中行供应链金融关键词
  5. Linux【实操篇】—— 日志管理
  6. 你好,请开下门,查水表|宅客周刊
  7. 帆软报表分页预览打印,如果列数过多,打印时会将多余的列放到第二页来打印,现在需要把所有的列都放在一页来打印。并且填满整个区域
  8. Redis学习笔记~Redis事务机制与Lind.DDD.Repositories.Redis事务机制的实现
  9. STM32系统滴答_及不可不知的延时技巧 - (下)
  10. win7家庭版和旗舰版区别_WIN7_64位系统安装 MicroWIN_SP9后没有PC-PPI通讯协议怎么处理?...