学习链接
jwt官网:https://jwt.io
JWT详解:讲解的十分详细
参照:JWT 介绍和使用,对称加密,非对称加密,RSA, Tocken?
1.引入jwt的概念
1.1 有状态登录,无状态登录
有状态登录
服务器当中记录每一次的登录信息,从而根据客户端发送的数据来判断登录过来的用户是否合法。
缺点:
- 服务器当中需要保存大量的session信息,从而增加了服务器的压力。
- 客户端请求依赖登录服务器,多个请求过来访问同一个服务器。
- 服务器拓展困难,需要将session存储到其他服务器当中。
无状态登录
服务器当中不记录用户的登录信息,而是将登录成功后的合法用户信息以token方式保存到客户端当中
,用户在每次请求都携带token信息。
好处:
- 减少服务器存储session信息的压力。
- 客户端请求不依赖服务器。
1.2 如何实现无状态登录?
流程
-
客户端第一次请求服务器,服务器端对登录用户信息进行认证。
-
认证通过后,对客户信息进行加密处理形成token登录凭证,然后返回给客户端。
-
以后客户端每一次请求都携带这个jwt信息去请求服务器。
而服务器端请求信息进行解密处理。判断登录用户是否有效。
授权-鉴权流程图
2.加密技术的引入
2.1对称加密,非对称加密,不可逆加密方式。
对称加密
将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。
- 优势:加密速度快,加密效率高。
- 缺点:双方都使用同样的密钥,安全性得不到保证。
非对称加密方式
同时生成两把密钥,公钥和私钥。私钥服务器自己保存,公钥下发到受信任的客户端
- 优势:安全性高,只要私钥不暴露出去,信息就不会泄露。
- 缺点:加密效率低
不可逆加密方式:MD5
加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文
严格意义上,这种不属于加密方式。因为加密算法要既能加密也能解密。
3.JWT
概念:全称json wek token 是一种轻量级的身份认证 ,可实现无状态 ,分布式的web应用授权
3.1组成:
JWT由三部分组成分别是:header+payload+签名
header
头部,通常由两部分信息,
-
声明类型:通常为jwt
-
签名算法:签名部分使用的算法,需要在header中进行定义。
payload
负载
这里保存着有效的数据部分。
-
jwt当中实际存储数据的部分。
-
官方规定了7个可以选择的数据。
- iss:发行人
- exp:到期时间
- sub:主题
- aud:用户
- nbf:在此之前不可用
- iat:发布时间
- jti:JWT ID用于标识该JWT
signature
签名
通常是整个数据的认证信息,一般根据前面两部的数据,再加上服务的密钥,通过加密算法生成,用于验证整个数据的完成性。
如图所示,这是生成的jwt信息。
3.2jwt系统交互流程
流程图
交互流程析
-
通过交互图可以观察到:用户登陆微服务后,还需要拿着jwt到鉴权中心去验证用户的登陆权限,能不能让用户就在服务端就可以完成鉴权的工作,这样就可以减少一次网络请求,加快系统的响应时间。
-
结论:我们可以使用jwt+rsa的方式,由鉴权中心生成私钥,公钥。在授权中心通过私钥生成jwt信息,然后公钥下发给受信任的服务。再使用公钥再服务器端进行鉴权处理。(如果通过公钥可以获取到jwt当中信息,说明该用户具有对应的权限。可以进行登陆操作。)
3.3 使用jwt+rsa方式的授权+鉴权方式
4.JWT实例
4.1使用jwt所依赖的maven依赖都有哪些
<!--json web token相关坐标-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.10</version>
</dependency>
4.2RSA工具类:用于生成私钥和公钥
RsaUtils
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* Rsa工具类
*/
public class RsaUtils {
private static final int DEFAULT_KEY_SIZE = 2048;
/**
* 从文件中读取公钥
*
* @param filename 公钥保存路径,相对于classpath
* @return PublicKey 公钥对象
* @throws Exception
*/
public static PublicKey getPublicKey(String filename) throws Exception {
byte[] bytes = readFile (filename);
return getPublicKey (bytes);
}
/**
* 从文件中读取密钥
*
* @param filename 私钥保存路径,相对于classpath
* @return PrivateKey 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
byte[] bytes = readFile (filename);
return getPrivateKey (bytes);
}
/**
* 获取公钥
* 公钥的字节形式。
* @param bytes 公钥的字节形式
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(byte[] bytes) throws Exception {
bytes = Base64.getDecoder ( ).decode (bytes);
X509EncodedKeySpec spec = new X509EncodedKeySpec (bytes);
KeyFactory factory = KeyFactory.getInstance ("RSA");
return factory.generatePublic (spec);
}
/**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
bytes = Base64.getDecoder ( ).decode (bytes);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec (bytes);
KeyFactory factory = KeyFactory.getInstance ("RSA");
return factory.generatePrivate (spec);
}
/**
* 根据密文,生存rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance ("RSA");
SecureRandom secureRandom = new SecureRandom (secret.getBytes ( ));
keyPairGenerator.initialize (Math.max (keySize, DEFAULT_KEY_SIZE), secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair ( );
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic ( ).getEncoded ( );
publicKeyBytes = Base64.getEncoder ( ).encode (publicKeyBytes);
writeFile (publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate ( ).getEncoded ( );
privateKeyBytes = Base64.getEncoder ( ).encode (privateKeyBytes);
writeFile (privateKeyFilename, privateKeyBytes);
}
private static byte[] readFile(String fileName) throws Exception {
return Files.readAllBytes (new File (fileName).toPath ( ));
}
private static void writeFile(String destPath, byte[] bytes) throws IOException {
File dest = new File (destPath);
if (!dest.exists ( )) {
dest.createNewFile ( );
}
Files.write (dest.toPath ( ), bytes);
}
}
RsaUtilsTest
import org.junit.Test;
import java.security.PrivateKey;
import java.security.PublicKey;
public class RsaUtilsTest {
//生成私钥,公钥地址
private String privateFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa";
private String publicFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa_pub";
@Test
public void testRSA() throws Exception {
// 生成密钥对
RsaUtils.generateKey(publicFilePath, privateFilePath, "hello", 2048);
// 获取私钥
PrivateKey privateKey = RsaUtils.getPrivateKey(privateFilePath);
System.out.println("privateKey = " + privateKey);
// 获取公钥
PublicKey publicKey = RsaUtils.getPublicKey(publicFilePath);
System.out.println("publicKey = " + publicKey);
}
}
4.3 jwt当中的载荷信息部分
Payload
import lombok.Data;
import java.util.Date;
@Data
public class Payload<T> {
// jwt的id
private String id;
// 用户信息
private T userInfo;
// 过期时间
private Date expiration;
}
UserInfo
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
/**
* 载荷 :UserInfo
*/
public class UserInfo {
private Long id;
private String username;
private String role;
}
4.4 jwtutils工具类,用于生成jwt信息,解析jwt信息
JwtUtils
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.UUID;
public class JwtUtils {
private static final String JWT_PAYLOAD_USER_KEY = "user";
/**
*
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位分钟
* @return JWT
*/
public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusMinutes(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位秒
* @return JWT
*/
public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusSeconds(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* 公钥解析token
*
* @param token 用户请求中的token
* @param publicKey 公钥
* @return Jws<Claims>
*/
private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
}
private static String createJTI() {
return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
}
/**
* 获取token中的用户信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload<T> claims = new Payload<>();
claims.setId(body.getId());
claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
claims.setExpiration(body.getExpiration());
return claims;
}
/**
* 获取token中的载荷信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload<T> claims = new Payload<>();
claims.setId(body.getId());
claims.setExpiration(body.getExpiration());
return claims;
}
}
测试
public static void main(String[] args) throws Exception {
String privateFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa";
String publicFilePath = "D:\\Projects\\demo-springboot222\\src\\main\\resources\\id_rsa_pub";
PrivateKey privateKey = RsaUtils.getPrivateKey(privateFilePath);
PublicKey publicKey = RsaUtils.getPublicKey(publicFilePath);
UserInfo userInfo = new UserInfo();
userInfo.setId(1L);
userInfo.setRole("admin");
userInfo.setUsername("zzhua");
ObjectMapper mapper = new ObjectMapper();
String token = Jwts.builder()
.claim("user", mapper.writeValueAsString((userInfo)))
.setId(createJTI())
.setExpiration(DateTime.now().plusMinutes(10).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256) // 私钥加密
// 注意这里的算法只能选RS开头的,比如还可以选SignatureAlgorithm.RS384
.compact();
Jws<Claims> claimsJws = Jwts.parser()
.setSigningKey(publicKey) // 公钥解密
.parseClaimsJws(token); // token
Claims body = claimsJws.getBody();
System.out.println(body.get("user").toString());
}
解密结果
5. jwt一些简单demo
jjwt
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
Base64加密、解密
@Test
public void testGenJwt() {
JwtBuilder jwtBuilder = Jwts.builder()
.setSubject("zzhua")
.setId("001")
.signWith(SignatureAlgorithm.HS256, "YOUR_secret");
String token = jwtBuilder.compact();
System.out.println(token);
// eyJhbGciOiJIUzI1NiJ9.
// eyJzdWIiOiJ6emh1YSIsImp0aSI6IjAwMSJ9.
// yipfa-gsDGg-5KYqeCVo_oiOnUx2D0Sp589gCUXCap8
}
@Test
public void testParseToken() throws IOException {
BASE64Decoder decoder = new BASE64Decoder();
// 使用Base64解析jwtToken的负载部分(头部和负载部分都是使用Base64加密的,所以可以解密)
String data = new String(decoder.decodeBuffer("eyJzdWIiOiJ6emh1YSIsImp0aSI6IjAwMSJ9"));
System.out.println(data); // 解析结果:{"sub":"zzhua","jti":"001"}
BASE64Encoder encoder = new BASE64Encoder();
// 使用Base64加密
String encode = encoder.encode("{\"sub\":\"zzhua\",\"jti\":\"001\"}".getBytes());
System.out.println(encode); // 与上面完全符合:eyJzdWIiOiJ6emh1YSIsImp0aSI6IjAwMSJ9
}
用法
//生成token
public class CreateJwtTest {
public static void main(String[] args) {
JwtBuilder builder= Jwts.builder().setId("888")
.setSubject("小白")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256,"itcast");
System.out.println( builder.compact() );
}
}
// 解析token
public class ParseJwtTest {
public static void main(String[] args) {
String token ="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiO"+
"jE1MjM0MTM0NTh9.gq0J-cOM_qCNqU_s-d_IrRytaNenesPmqAIhQpYXHZk";
Claims claims = Jwts.parser().setSigningKey("itcast").parseClaimsJws(token).getBody();
System.out.println("id:"+claims.getId());
System.out.println("subject:"+claims.getSubject());
System.out.println("IssuedAt:"+claims.getIssuedAt());
}
}
// 自定义claims数据
public class CreateJwtTest3 {
public static void main(String[] args) {
//为了方便测试,我们将过期时间设置为1分钟
long now = System.currentTimeMillis();//当前时间
long exp = now + 1000*60;//过期时间为1分钟
new HashMap<String,Object>()
JwtBuilder builder= Jwts.builder().setId("888")
.setSubject("小白")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256,"itcast")
.setExpiration(new Date(exp))
.claim("roles","admin") //自定义claims存储数据
.claim("logo","logo.png");
System.out.println( builder.compact() );
}
}
对称签名
public class JwtUtils {
// token时效:24小时
public static final long EXPIRE = 1000 * 60 * 60 * 24;
// 签名哈希的密钥,对于不同的加密算法来说含义不同
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
/**
* 根据用户id和昵称生成token
* @param id 用户id
* @param nickname 用户昵称
* @return JWT规则生成的token
*/
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("baobao-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
// HS256算法实际上就是MD5加盐值,此时APP_SECRET就代表盐值
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken token字符串
* @return 如果token有效返回true,否则返回false
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request Http请求对象
* @return 如果token有效返回true,否则返回false
*/
public static boolean checkToken(HttpServletRequest request) {
try {
// 从http请求头中获取token字符串
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request Http请求对象
* @return 解析token后获得的用户id
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
非对称签名
生成jwt串的时候需要指定私钥,解析jwt串的时候需要指定公钥
private static final String RSA_PRIVATE_KEY = "...";
private static final String RSA_PUBLIC_KEY = "...";
/**
* 根据用户id和昵称生成token
* @param id 用户id
* @param nickname 用户昵称
* @return JWT规则生成的token
*/
public static String getJwtTokenRsa(String id, String nickname){
// 利用hutool创建RSA
RSA rsa = new RSA(RSA_PRIVATE_KEY, null);
RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
String JwtToken = Jwts.builder()
.setSubject("baobao-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
// 签名指定私钥
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken token字符串
* @return 如果token有效返回true,否则返回false
*/
public static Jws<Claims> decodeRsa(String jwtToken) {
RSA rsa = new RSA(null, RSA_PUBLIC_KEY);
RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
// 验签指定公钥
Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(jwtToken);
return claimsJws;
}
java-jwt
引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
示例工具类1
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
public class JWTUtil {
/**
* 过期时间3小时
*/
private static final long EXPIRE_TIME = 3 * 60 * 60 * 1000;
/**
* 校验token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 获取当前用户
*
* @param token jwt加密信息
* @return 解析的当前用户信息
*/
public static Principal getPrincipal(String token) {
try {
Principal principal = new Principal();
DecodedJWT jwt = JWT.decode(token);
principal.setUserId(jwt.getClaim("userId").asString());
principal.setUserName(jwt.getClaim("username").asString());
String[] roleArr = jwt.getClaim("roles").asArray(String.class);
if (roleArr != null) {
principal.setRoles(Arrays.asList(roleArr));
}
return principal;
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 获取角色组
*
* @param token
* @return
*/
public static String[] getRoles(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("roles").asArray(String.class);
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名
*
* @param username 用户名
* @param userId 用户id
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String username, String userId, List<String> roles, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
String[] roleArr = new String[roles.size()];
roleArr = roles.toArray(roleArr);
// 附带username信息
return JWT.create()
.withClaim("userId", userId)
.withClaim("username", username)
.withArrayClaim("roles", roleArr)
.withExpiresAt(date)
.sign(algorithm);
}
}
示例工具类2
public final class JwtUtil {
private JwtUtil() {
}
/**
* 生成jwt字符串 JWT(json web token)
*
* @param userId 用户id
* @param info 用户细腻系
* @param expireIn 过期时间
* @param secret 秘钥
* @return token
*/
public static String sign(Long userId, String info, long expireIn, String secret) {
try {
Date date = new Date(System.currentTimeMillis() + expireIn * 1000);
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create()
//将userId保存到token里面
.withAudience(userId.toString())
//存放自定义数据
.withClaim("info", info)
//过期时间
.withExpiresAt(date)
//token的密钥
.sign(algorithm);
} catch (Exception e) {
return null;
}
}
/**
* 根据token获取userId
*
* @param token 登录token
* @return 用户id
*/
public static Long getUserId(String token) {
try {
String userId = JWT.decode(token).getAudience().get(0);
return Long.parseLong(userId);
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 根据token获取用户数据
*
* @param token 用户登录token
* @return 用户数据
*/
public static String getInfo(String token) {
try {
return JWT.decode(token).getClaim("info").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 校验token
*
* @param token 用户登录token
* @param secret 秘钥
* @return true/false
*/
public static Boolean checkSign(String token, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
} catch (JWTVerificationException e) {
return false;
}
}
}
对称签名
public class JWTUtils {
// 签名密钥
private static final String SECRET = "!DAR$";
/**
* 生成token
* @param payload token携带的信息
* @return token字符串
*/
public static String getToken(Map<String,String> payload){
// 指定token过期时间为7天
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 7);
JWTCreator.Builder builder = JWT.create();
// 构建payload
payload.forEach((k,v) -> builder.withClaim(k,v));
// 指定过期时间和签名算法
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET));
return token;
}
/**
* 解析token
* @param token token字符串
* @return 解析后的token
*/
public static DecodedJWT decode(String token){
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT;
}
}
非对称签名
生成jwt串的时候需要指定私钥,解析jwt串的时候需要指定公钥。
private static final String RSA_PRIVATE_KEY = "..."; // 需要生成
private static final String RSA_PUBLIC_KEY = "..."; // 需要生成
/**
* 生成token
* @param payload token携带的信息
* @return token字符串
*/
public static String getTokenRsa(Map<String,String> payload){
// 指定token过期时间为7天
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 7);
JWTCreator.Builder builder = JWT.create();
// 构建payload
payload.forEach((k,v) -> builder.withClaim(k,v));
// 利用hutool创建RSA
RSA rsa = new RSA(RSA_PRIVATE_KEY, null);
// 获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
// 签名时传入私钥
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));
return token;
}
/**
* 解析token
* @param token token字符串
* @return 解析后的token
*/
public static DecodedJWT decodeRsa(String token){
// 利用hutool创建RSA
RSA rsa = new RSA(null, RSA_PUBLIC_KEY);
// 获取RSA公钥
RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
// 验签时传入公钥
JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT;
}
6. Double Token解决方案
流程图
-
用户登录成功后,后台生成token,但是不直接把此token返回给客户端,而是使用md5对该token加密,并将MD5(token)作为key,token作为value,存入redis,这样避免用户信息被泄露(因为jwt的头部和负载仅仅是Base64加密,很容易被解析出来,因此敏感信息不能放进去,使用md5加密后,这样就解析不出来了),将解密后的结果返回给客户端(可以用cookie的形式写给客户端,让客户端自动携带cookie过来,比较方便)。
-
客户端访问时携带MD5(token),后台从redis中拿到真正的token,解析出来