赞
踩
JWT详解:讲解的十分详细
参照:JWT 介绍和使用,对称加密,非对称加密,RSA, Tocken?
服务器当中记录每一次的登录信息,从而根据客户端发送的数据来判断登录过来的用户是否合法。
缺点:
服务器当中不记录用户的登录信息,而是将登录成功后的合法用户信息以token方式保存到客户端当中
,用户在每次请求都携带token信息。
好处:
客户端第一次请求服务器,服务器端对登录用户信息进行认证。
认证通过后,对客户信息进行加密处理形成token登录凭证,然后返回给客户端。
以后客户端每一次请求都携带这个jwt信息去请求服务器。
而服务器端请求信息进行解密处理。判断登录用户是否有效。
将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。
同时生成两把密钥,公钥和私钥。私钥服务器自己保存,公钥下发到受信任的客户端
加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文
严格意义上,这种不属于加密方式。因为加密算法要既能加密也能解密。
概念:全称json wek token 是一种轻量级的身份认证 ,可实现无状态 ,分布式的web应用授权
JWT由三部分组成分别是:header+payload+签名
头部,通常由两部分信息,
声明类型:通常为jwt
签名算法:签名部分使用的算法,需要在header中进行定义。
负载
这里保存着有效的数据部分。
jwt当中实际存储数据的部分。
官方规定了7个可以选择的数据。
签名
通常是整个数据的认证信息,一般根据前面两部的数据,再加上服务的密钥,通过加密算法生成,用于验证整个数据的完成性。
如图所示,这是生成的jwt信息。
通过交互图可以观察到:用户登陆微服务后,还需要拿着jwt到鉴权中心去验证用户的登陆权限,能不能让用户就在服务端就可以完成鉴权的工作,这样就可以减少一次网络请求,加快系统的响应时间。
结论:我们可以使用jwt+rsa的方式,由鉴权中心生成私钥,公钥。在授权中心通过私钥生成jwt信息,然后公钥下发给受信任的服务。再使用公钥再服务器端进行鉴权处理。(如果通过公钥可以获取到jwt当中信息,说明该用户具有对应的权限。可以进行登陆操作。)
<!--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>
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); } }
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); } }
import lombok.Data; import java.util.Date; @Data public class Payload<T> { // jwt的id private String id; // 用户信息 private T userInfo; // 过期时间 private Date expiration; }
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; }
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()); }
解密结果
<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; }
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
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); } }
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; }
用户登录成功后,后台生成token,但是不直接把此token返回给客户端,而是使用md5对该token加密,并将MD5(token)作为key,token作为value,存入redis,这样避免用户信息被泄露(因为jwt的头部和负载仅仅是Base64加密,很容易被解析出来,因此敏感信息不能放进去,使用md5加密后,这样就解析不出来了),将解密后的结果返回给客户端(可以用cookie的形式写给客户端,让客户端自动携带cookie过来,比较方便)。
客户端访问时携带MD5(token),后台从redis中拿到真正的token,解析出来
假设我们只用一个token
生成1个token——>有效期:2小时——>每次访问就更新有效期为2小时——>2个小时没有访问的话,就要重新登录了
生成一个token—>有效期:30天——> 每次访问就更新有效期为30天——>用户一个月没有登录,没有访问,就会过期——>如果这个token长期保存在redis里,就会占用资源。这个用户一个月才访问1次,这样太浪费资源了
生成1个token——>有效期2个小时,保存在redis中——>过期了,我们就通过refreshToken来创建新的token——>redis里,如果2个小时不访问,那么redis里的token就会被干掉,这样就不占用redis的资源了。如果再访问,就通过refreshToken来创建新的token。如果该用户一个月都没有访问,就要重新登录,因为refreshToken的有效期有30天。
redis的特性–>noSQL,内存型数据库,存储大部分数据在内存里,快照会保存到磁盘里。mysql关系型数据库,数据保存在磁盘上。当然,现在的mysql也有缓存的功能。速度上来说,redis应该要更快的,所以会配合着使用。
如果网站用户有100万,我们直接把一个长期的token保存在redis里,用户不用了,那不是很占用空间吗?所以我们会生成一个2小时的token。放在redis里,生成一个30天的refreshToken放在mysql里。
平时用户访问则取用redis里的token,如果过期了,通过已失效的token找到有效时间更长的refreshToken,来创建新的token,同时也更新refreshToken。
这样子就可以减少mysql的token查询了,速度上会快一点。而且用户超过2个小时没有登录,redis里的空间就释放了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。