springboot整合JWT+Redis

——————————已实际运用在项目中,可放心使用————————————————

本篇文章使用的是JWT+Redis,即  使用JWT生成令牌,用于后期用户登录验证;使用Redis保存用户信息,并记录用户的登录时间和令牌到期时间;

刷新令牌有效期也是刷新Redis数据有效期的方法来实现的,所以本篇文章并没有给JWT设置有效期的地方。这样就避免了JWT过期后,需重新返回新生成的令牌的问题。

目录

引入依赖

 <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

创建JWT工具类

包括生成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);
    }
}

创建JWT拦截器

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;
    }
}

注册JWT拦截器 

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"); //不需要拦截的地址,如登录接口
    }
}

创建Redis工具类

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;
        }
    }

User实体类

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版权协议,转载请附上原文出处链接和本声明。