——————————已实际运用在项目中,可放心使用————————————————
本篇文章使用的是JWT+Redis,即 使用JWT生成令牌,用于后期用户登录验证;使用Redis保存用户信息,并记录用户的登录时间和令牌到期时间;
刷新令牌有效期也是刷新Redis数据有效期的方法来实现的,所以本篇文章并没有给JWT设置有效期的地方。这样就避免了JWT过期后,需重新返回新生成的令牌的问题。
目录
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>包括生成token,刷新token时长,验证token
package com.yc.util;
import com.yc.config.Global;
import com.yc.domain.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author mclt2017
* @date 2021年06月24日 13:35
*/
@Component
public class JwtUtils {
/**
* 自定义秘钥
* */
private static final String sign = "123123" ;
/**
* jwtToken的默认有效时间 单位分钟
* */
private static final int expireTime = 30 ;
/**
* 生成jwt token
* @param map 要存放负载信息
* */
public static String createJwtToken(Map<String,Object> map){
return Jwts.builder()
.setClaims(map) //放入payLoad部分的信息
.signWith(SignatureAlgorithm.HS512,sign)
.compact();
}
/**
* 从令牌中获取数据,就是payLoad部分存放的数据。如果jwt被改,该函数会直接抛出异常
* @param token 令牌
* */
public static Claims parseToken(String token){
return Jwts.parser()
.setSigningKey(sign)
.parseClaimsJws(token)
.getBody() ;
}
/**
* 验证用户信息
* @param token jwtToken
* */
public static User verifyJwtToken(String token){
Claims claims = parseToken(replaceTokenPrefix(token));
String id = String.valueOf(claims.get("id"));
//从redis中获取用户信息
User user = JSON.parseObject(JSON.toJSONString(RedisUtils.getValue(id)),User.class);
return user ;
}
/**
* 刷新令牌时间,刷新redis缓存时间
* @param user 用户信息
* */
public static void refreshToken(User user){
//重新设置User对象的过期时间,再刷新缓存
user.setExpireTime(System.currentTimeMillis()+1000L * 60 * expireTime);
RedisUtils.saveValue(user.getId(),user,expireTime,TimeUnit.MINUTES);
}
/**
* 删除 token 的前缀
* 前端的安全规则会在token前自动生成 Bearer 字符串前缀,共7个字符,需要删掉
* */
public static String replaceTokenPrefix(String token){
return token.substring(7);
}
}
package com.yc.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.config.Global;
import com.yc.domain.User;
import com.yc.util.JwtUtils;
import io.jsonwebtoken.SignatureException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author mclt2017
* @date 2021年06月24日 17:16
* Jwt拦截器
*/
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
Map<Object, Object> map = new HashMap<>();
//如果是OPTIONS请求 直接放行
String method = request.getMethod();
try {
if(method.equals("OPTIONS")){
return true;
}
//从请求中获取令牌
String jwtToken = request.getHeader("Authorization");
if(String.valueOf(jwtToken).equals("null")){
throw new SignatureException("令牌不合法");
}
//验证token
User user = JwtUtils.verifyJwtToken(jwtToken);
//验证成功后,如果令牌有效时间<=5分钟,则签发新的令牌,刷新令牌时间
if(user != null){
if(user.getExpireTime() - System.currentTimeMillis() <= 1000L * 60 * 5){
JwtUtils.refreshToken(user);
}
return true ;
}else{
map.put("success",false);
map.put("code",401);
map.put("message","令牌已失效,请重新登录");
}
}catch(SignatureException e){
e.printStackTrace();
map.put("message","令牌不合法");
map.put("code",401);
map.put("success",false);
}catch (Exception e) {
e.printStackTrace();
map.put("message","令牌验签失败:"+e.getMessage());
map.put("success",false);
}
String jsonMap = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(jsonMap);
return false;
}
}
import com.yc.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author mclt2017
* @date 2021年06月24日 17:45
* 拦截器配置文件
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 注册拦截器
* */
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**") //拦截的地址
.excludePathPatterns("/loginController/login"); //不需要拦截的地址,如登录接口
}
}
package com.yc.util;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
/**
* @author mclt2017
* @date 2021年06月28日 15:22
*/
@Component
public class RedisUtils {
@Autowired
private RedisTemplate redisTemplate;
private static RedisUtils redisUtils ;
@PostConstruct
public void init(){
redisUtils = this ;
redisUtils.redisTemplate = this.redisTemplate ;
}
/**
* redis存入数据
* @param key 键名
* @param value 值
* @param time 保存时间
* @param timeUnit 时间单位
* */
public static void saveValue(String key, Object value, int time, TimeUnit timeUnit){
redisUtils.redisTemplate.opsForValue().set(key,value,time,timeUnit);
}
/**
* 获取redis中的值
* @param key 键名
* */
public static <T> T getValue(String key){
ValueOperations<String,T> valueOperations = redisUtils.redisTemplate.opsForValue();
return valueOperations.get(key);
}
/**
* 删除单个对象
* @param key 键名
* */
public static boolean deleteValue(String key){
return redisUtils.redisTemplate.delete(key);
}
}
/**
* 用户登录,验证成功,生成并返回token,
* */
@Override
public ResultData getToken(User user) {
try{
ResultData resultData = userService.getUser(user);
if(resultData.getSuccess()){
User info = (User) resultData.getData();
//需要存放的负载信息
Map<String,Object> map = new HashMap<>();
map.put("id",info.getId());
//生成jwtToken
String jwtToken = JwtUtils.createJwtToken(map);
//设置用户的登录时间和过期时间,并存入redis
info.setLoginTime(System.currentTimeMillis());
info.setExpireTime(info.getLoginTime()+1000L * 60 * 30);
RedisUtils.saveValue(info.getId(),info,30,TimeUnit.MINUTES);
//将token返回
map.put("Authorization",jwtToken);
resultData.setData(map);
}
return resultData;
}catch (Exception e){
e.printStackTrace();
return null;
}
}public class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 登录时间
* */
private Long loginTime ;
/**
* 令牌过期时间
* */
private Long expireTime ;
/**
* id
* */
private string id ;
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id ;
}
//其他属性。。。。。。
}SpringBoot整合Redis
请参考我的另一篇文章: springboot整合redis_mclt2017的博客-CSDN博客
版权声明:本文为weixin_42488402原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。