使用Springboot基于拦截器和redis的Jwt判断用户登录以及安全校验


这里简单介绍用户登录解析Jwt token,从请求的session、以及redis中获取我们想要信息,再做具体业务操作。


不熟悉Jwt概念和用法的可以参考前面两篇:
  • Jwt入门教程( 一) | 原理和用法.
  • Jwt入门教程:实战( 二) | Java/.Net/Python中的使用.

看了前两篇,下面就不多说了,直接贴代码:(代码可能较长,因为附带了redis的配置,继续往下看。)

首先,准备工作:配置redis(下方redis的代码较长,大家斟酌,需要的可以copy,放在最后)

下面是正文的开始:


1.MVC配置拦截器
/**
 * MVC配置
 */
@Configuration
public class WebMvcConfig implements  WebMvcConfigurer  {
    @Autowired
    private TokenInterceptor tokenInterceptor;//自定义Token拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");//token拦截
    }
     
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("Content-Type", "Authorization")
                .allowCredentials(false)
                .maxAge(3600);
    }
}

2.拦截器方法:(这里使用了redis)

import com.huangtu.common.util.user.JwtUtilsHelper;
import com.huangtu.config.RedisUtilsTwo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.impl.Base64Codec;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 慌途L
 */
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {

	//注入redis工具类
    @Autowired
    private RedisUtilsTwo redisUtilsTwo;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            String cookie = request.getHeader("cookie");
            System.out.println(cookie + "\n");

            /**
             * accessToken=eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MzE5OTg4MywiZXhwIjoxNTQzMjE0MjgzfQ.eyJ1c2VyX2lkIjoxMDAxLCJ5c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.-qXmphVPtcmO3PLmuQIiFL7khhKiDjwPZvGyxLbyBOkIMdtFJ7whLY0hSabbJwYH9Dcxmhf1tdPKN2jGSP5YBQ; JSESSIONID=6A769332782C0F436510214757C16063
             */
            //token最后还跟了sessionid,去除
            String[] split1 = cookie.split(";");

            /**
             * accessToken=eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MzE5OTg4MywiZXhwIjoxNTQzMjE0MjgzfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWU5OiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.-qXmphVPtcmO3PLmuQIiFL7khhKiDjwPZvGyxLbyBOkIMdtFJ7whLY0hSabbJwYH9Dcxmhf1tdPKN2jGSP5YBQ
             */
            //得到accessToken
            String[] split = split1[0].split("=");
            System.out.println("accessToken"+split[1]);

            //验证token
            String login = verificationToken(split[1]);

            return true;
        } catch (Exception e){
            e.printStackTrace();
            return false;
        }

    }
    /**
     * 验证token
     *
     * @return
     */
    public String verificationToken(String jwt) {
        String[] splitJwt = jwt.split("\\.");
        String params = "";
        if (splitJwt.length == 3) {
            //从redis里面拿到当前用户的accessToken进行对比
            String accessToken = redisUtilsTwo.get("accessToken");
            if(StringUtils.isNotBlank(accessToken)){
                if(jwt.equals(accessToken)){
                    System.out.println("token验证通过");

                    String header = splitJwt[0];
                    String payload = splitJwt[1];
                    String signature = splitJwt[2];//带过来的签名
                    System.out.println(Base64Codec.BASE64URL.decodeToString(header));
                    System.out.println(Base64Codec.BASE64URL.decodeToString(payload));

                    //得到user数据--jwt的payload
                    Claims claims = JwtUtilsHelper.accessTonkenDecodePart(jwt);
                    if (!claims.isEmpty() && claims.size()>0) {
                        Jwt jwt1 = JwtUtilsHelper.accessTonkenDecodeAll(jwt);
                        JSONObject jsonObject = JSONObject.fromObject(jwt1.getHeader());
                        long iat = Integer.parseInt(jsonObject.get("iat").toString());
                        long exp = Integer.parseInt(jsonObject.get("exp").toString());

                        //登录时间和过期时间
                        System.out.println(iat+"||||"+exp);
                        if (!JwtUtilsHelper.isTokenExpired(exp)) {//判断token有效性
                            String userId = claims.get("user_id").toString();
                            String userName = claims.get("user_name").toString();
                            String ip = claims.get("ip").toString();
                            System.out.println("userId="+userId);
                            System.out.println("userName="+userName);
                            System.out.println("ip="+ip);
                            if ("169061".equals(userId) && "admin".equals(userName) && "192.168.110.555".equals(ip)) {
                                params = "token有效";//校验成功,数据完全一致
                            } else {
                                params = "用户id或用户名或ip地址有误";
                            }
                        } else {
                            params = "token过期";
                        }
                    } else {
                        params = "token的payload无数据";
                    }
                } else {
                    params = "token被篡改";
                }
            } else{
                params = "用户为登录";
            }
        } else {
            params = "Jwt Token格式错误";
        }
        return params;
    }

}

3.使用Postman请求:(用的是我项目的请求格式,大家随意),这里我放在请求头里面:使用名字cookie

jwt的jti存redis jwt和redis_token


jwt的jti存redis jwt和redis_token_02


4.到这里,基本上就结束了,大家可以在拦截器里面debugger一步步走。

配置文件:

spring:
    redis:
        database: 0
        host: localhost
        port: 6379
        password: 123456     # 密码()
        timeout: 6000  # 连接超时时长(毫秒)
        pool:
            max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
            max-wait: -1      # 连接池最大阻塞等待时间(使用负值表示没有限制)
            max-idle: 10      # 连接池中的最大空闲连接
            min-idle: 5       # 连接池中的最小空闲连接
    cache:
        type: none

JedisPool:

/**
 * @author 慌途L
 */
@Configuration
@EnableCaching
public class RedisConfigTwo extends CachingConfigurerSupport {
    private Logger logger = Logger.getLogger(getClass());
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.pool.max-wait}")
    private long maxWaitMillis;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.database}")
    private int dataBase;

    @Bean
    public JedisPool redisPoolFactory() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);

        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password,dataBase);
        return jedisPool;
    }
}

redis工具类:

import org.springframework.stereotype.Component;
import redis.clients.jedis.BinaryClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author 慌途L
 */
@Component
public class RedisUtilsTwo{
    @Resource
    private JedisPool jedisPool;

    /**
     * 获取数据
     * @param key
     * @return
     */
    public  String get(String key){
        String value=null;
        Jedis jedis=null;
        try{
            jedis=jedisPool.getResource();
            value=jedis.get(key);
        }catch (Exception e){
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        }finally {
            close(jedis);
        }
        return value;
    }


    public  void del(String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
    }

    public  void set(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
    }

    public  void set(String key, String value, int time) {

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
            jedis.expire(key, time);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
    }

    public  void hset(String key, String field, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hset(key, field, value);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
    }

    /**
     * 获取数据
     *
     * @param key
     * @return
     */
    public  String hget(String key, String field) {

        String value = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            value = jedis.hget(key, field);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }

        return value;
    }

    public  void hdel(String key, String field) {

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hdel(key, field);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
    }
    /**
     * 存储REDIS队列 顺序存储
     * @param  key reids键名
     * @param  value 键值
     */
    public  void lpush(String key, String value) {

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.lpush(key, value);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
    }

    /**
     * 移除队列中出现第一个数据
     * @param key
     * @param value
     * @return
     */
    public long lrem(String key, String value){
        Jedis jedis = null;
        long l =0 ;
        try{
            jedis = jedisPool.getResource();
            l = jedis.lrem(key,1,value);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return l;
    }

    /**
     * 存储REDIS队列 反向存储
     * @param  key reids键名
     * @param  value 键值
     */
    public  long rpush(String key, String value) {
        Jedis jedis = null;
        long flage=0;
        try {
            jedis = jedisPool.getResource();
            flage = jedis.rpush(key, value);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return flage;
    }

    /**
     * 存储REDIS队列 反向存储
     * @param  key reids键名
     * @param  value 键值
     */
    public  long rpush(String key, String value,int seconds) {
        Jedis jedis = null;
        long flage=0;
        try {
            jedis = jedisPool.getResource();
            flage = jedis.rpush(key, value);
            jedis.expire(key, seconds);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return flage;
    }

    /**
     * 在列表的元素前或者后插入元素,返回List的长度
     * @param key
     * @param where LIST_POSITION    LIST_POSITION.BEFORE之前添加;LIST_POSITION.AFTER之后添加
     * @param pivot 以该元素作为参照物,是在它之前,还是之后
     * @param value
     * @return Long
     */
    public long linsert(String key, BinaryClient.LIST_POSITION where, String pivot, String value){
        Jedis jedis = null;
        long flage=0;
        try {
            jedis = jedisPool.getResource();
            flage = jedis.linsert(key, where, pivot, value);
        } catch (Exception e) {
            flage = -1;
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return flage;
    }

    /**
     * 将一个或多个值插入到已存在的列表头部,当成功时,返回List的长度;当不成功(即key不存在时,返回0)
     * @param key
     * @param value String
     * @return Long
     */
    public long lpushx(String key, String value){
        Jedis jedis = null;
        long flage=0;
        try {
            jedis = jedisPool.getResource();
            flage = jedis.lpushx(key, value);
        } catch (Exception e) {
            flage = -1;
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return flage;
    }

    /**
     * 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端
     * @param  key reids键名
     * @param  destination 键值
     */
    public  void rpoplpush(String key, String destination) {

        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            jedis.rpoplpush(key, destination);

        } catch (Exception e) {

            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {

            //返还到连接池
            close(jedis);

        }
    }

    /**
     * 获取队列数据
     * @param  key 键名
     * @return
     */
    public  List lpopList(String key) {

        List list = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            list = jedis.lrange(key, 0, -1);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return list;
    }
    /**
     * 获取队列数据,返回并删除列表的第一个(LPOP)或最后一个(RPOP)元素。
     * @param  key 键名
     * @return
     */
    public String rpop(String key) {
        String bytes = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            bytes = jedis.rpop(key);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return bytes;
    }

    public  void hmset(Object key, Map hash) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hmset(key.toString(), hash);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);

        }
    }
    public  void hmset(Object key, Map hash, int time) {
        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            jedis.hmset(key.toString(), hash);
            jedis.expire(key.toString(), time);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);

        }
    }
    public  List hmget(Object key, String... fields) {
        List result = null;
        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            result = jedis.hmget(key.toString(), fields);

        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);

        }
        return result;
    }

    public  Set hkeys(String key) {
        Set result = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            result = jedis.hkeys(key);

        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);

        }
        return result;
    }
    public  List lrange(String key, int from, int to) {
        List result = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            result = jedis.lrange(key, from, to);

        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);

        }
        return result;
    }
    public  Map hgetAll(String key) {
        Map result = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            result = jedis.hgetAll(key);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);
        }
        return result;
    }

    public  long llen(String key) {

        long len = 0;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.llen(key);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally {
            //返还到连接池
            close(jedis);
        }
        return len;
    }

    /**
     * 修改list中指定位置的值
     * @param key
     * @param index
     * @param value
     * @return  正确返回ok
     */
    public String lset(String key,long index,String value){
        Jedis jedis = null;
        String s = null;
        try {
            jedis = jedisPool.getResource();
            s = jedis.lset(key,index,value);
        } catch (Exception e) {
            //释放redis对象
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();

        } finally {
            //返还到连接池
            close(jedis);
        }
        return s;
    }

    private  void close(Jedis jedis) {
        try{
            jedisPool.returnResource(jedis);
        }catch (Exception e){
            if(jedis.isConnected()){
                jedis.quit();
                jedis.disconnect();
            }
        }
    }
}

结语:目前使用的就是这些啦,希望对大家有所帮助。后续会有在shiro中的使用,敬请期待!