拦截器和JWT的使用

一、我们新建一个Controller文件(编写一段测试代码)

1.2、在设计一个interceptor文件夹,在文件夹下生成一个类TestInterceptor(主要需要继承WebMvcConfigurerAdapter)                                

package com.example.spring_interceptors.interceptor;

import com.example.spring_interceptors.Controller.utils.JwtOperator;
import com.example.spring_interceptors.Controller.utils.RedisHelper;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
@Slf4j
public class Testinterceptor extends WebMvcConfigurerAdapter {

    @Autowired
    private JwtOperator jwtOperator;
    @Autowired
    private RedisHelper redisHelper;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        HandlerInterceptor handlerInterceptor=new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                log.info("preHandle,在请求Controller之前触发,返回值为true代表放行,false代表拦截");

                boolean isOk=false;

                String serVletPath = request.getServletPath();//获取请求的路径
                if (serVletPath.contains("/user/login")){
                    isOk=true;//放行
                }else {
                    try {
                        String token = request.getHeader("Authorization");
                        boolean isExpire = jwtOperator.validateTokenTime(token);
                        if(isExpire){//验证token是否被篡改,是否过期
                            Claims info = jwtOperator.getClaimsFromToken(token);
                            String[] str =info.toString().replace("{","").replace("}","").split(",");
                            String userId=str[0].replace("id=","");

                            //从redis中获取token,通过userId
                            String tokenFromRedis = redisHelper.getValue(userId);
                            if(tokenFromRedis.equals(token)){
                                isOk=true;
                            }
                        }
                    } catch (Exception e) {
                        response.getWriter().write("token is disabled");
                    }
                }
                return isOk;
            }

            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                log.info("postHandle,在请求Controller之后执行");
            }

            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                log.info("afterCompletion,在整个Request请求完成后执行");
            }
        };
        registry.addInterceptor(handlerInterceptor);
    }
}

 1.3、然后我们通过访问页面就可以看到打印的日志会输出到控制台上(返回值为true就代表着放行,不需要拦截,false代表拦截)

 二、JWT的使用

2.1、我们使用JWT需要先导入pom.xml文件的依赖(需要导入JWT和Redis的依赖,刷新Maven)

<!--JWT的依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.10.7</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.7</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.7</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 添加jedis:Begin -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

2.2、设置application.yml的配置条件

spring:
  redis:
    #redis服务器IP
    host: 127.0.0.1
    #redis服务器端口号
    port: 6379
    #redis数据库密码
    password:
    #redis连接池配置
    jedis:
      pool:
        #设置连接池中最大连接数
        max-active: 10000
        #设置连接池中最大空闲连接数
        max-idle: 50
jwt:
  secret: aaaaaaabbbbbbcccccdddddaaaaaaabbbbbbcccccdddddaaaaaaabbbbbbcccccddddd
  # 有效期,单位秒,默认2周
  expire-time-in-second: 1209600

2.3、需要在Controller文件中中新建一个Utils文件夹,并新建两个文件

        2.3.1、第一个文件(JwtOperator)

package com.example.spring_interceptors.Controller.utils;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("WeakerAccess")
@Component
public class JwtOperator {
    /**
     * 秘钥
     * - 默认aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt
     */
    @Value("${jwt.secret:aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt}")
    private String secret;
    /**
     * token有效期,单位秒
     * - 默认30分钟
     */
    @Value("${jwt.expire-time-in-second:1209600}")
    private Long expirationTimeInSecond;

    /**
     * 按要求生成token和refresh_token
     *
     * @param claims
     * @return token
     */
    public String generateTokens(Map<String, Object> claims){

        long currentTime = System.currentTimeMillis();
        Date tokenExpirationTime = new Date(currentTime + this.expirationTimeInSecond * 1000);

        String token = generateToken(claims, tokenExpirationTime);//生成token

        return token;
    }

    /**
     * 判断token是否过期
     *
     * @param token token
     * @return 未过期返回true,否则返回false
     */
    public Boolean validateTokenTime(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.after(new Date()); //获取时间的毫秒数 > 当前时间的毫秒数 = true
    }

    /**
     * 从token中获取claim
     *
     * @param token token
     * @return claim
     */
    public Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(this.secret.getBytes())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {
            log.error("token无效");
            throw new IllegalArgumentException("Token invalided.");
        }
    }

    /**
     * 生成token
     *
     * @param claims 用户信息
     * @param expirationTime 过期时间
     * @return token
     */
    private String generateToken(Map<String, Object> claims, Date expirationTime) {
        Date createdTime = new Date();

        byte[] keyBytes = secret.getBytes();
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(createdTime)
                .setExpiration(expirationTime)
                // 你也可以改用你喜欢的算法
                // 支持的算法详见:https://github.com/jwtk/jjwt#features
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 获取token的过期时间
     *
     * @param token token
     * @return 过期时间
     */
    private Date getExpirationDateFromToken(String token) {
        return getClaimsFromToken(token).getExpiration();
    }

}

         2.3.2、第二个文件(RedisHelper)

package com.example.spring_interceptors.Controller.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

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

@Slf4j
@Component
public class RedisHelper {

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

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

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

    @Value("${spring.redis.jedis.pool.max-active}")
    private String redisMaxActive;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private String redisMaxIdle;

    private JedisPool jedisPool; //连接池

    /**
     * 向Redis服务器中添加数据
     *
     * @param key
     * @param value
     * @return
     */
    public String setValue(String key, String value) {
        return getJedis().set(key, value);
    }

    /**
     * 根据Key获取Redis中保存的数据
     *
     * @param key
     * @return
     */
    public String getValue(String key) {
        return getJedis().get(key);
    }

    /**
     * 根据Key删除Redis中保存的数据
     *
     * @param key
     * @return
     */
    public Long removeValue(String key) {
        return getJedis().del(key);
    }

    /**
     * 获得Jedis对象
     *
     * @return
     */
    private Jedis getJedis() {
//		log.info("redisHost:"+redisHost);
//		log.info("redisPort:"+redisPort);
//		log.info("redisPassword:"+redisPassword);
        if(jedisPool == null) {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxTotal(Integer.parseInt(redisMaxActive)); //设置最大连接数
            jedisPoolConfig.setMaxIdle(Integer.parseInt(redisMaxIdle)); //设置最大空闲连接数
            jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort); //ip+端口号
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if(!"".equals(redisPassword)){
                jedis.auth(redisPassword);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(jedis != null) {
                jedis.close(); //关闭jedis连接
            }
            if(jedisPool != null) {
                jedisPool.close(); //关闭jedisPool连接
                jedisPool = null;
            }
        }
        return jedis;
    }
}

三、我们通过测试来实现拦截,新建一个entity的实体类包,新建一个user文件,属性有id,username,password三个参数(使用Data注解来自动生成get和set方法)

 3.2、在设计一个Service文件夹,新建一个UserService文件,然后在Comtroller文件夹中新建一个UserController文件

        3.2.1、UserService

package com.example.spring_interceptors.service;

import com.example.spring_interceptors.Controller.utils.JwtOperator;
import com.example.spring_interceptors.Controller.utils.RedisHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class UserService {

    @Autowired
    private JwtOperator jwtOperator;
    @Autowired
    private RedisHelper redisHelper;
    //校验用户登录
    public String validUser(String username,String password){
        String token=null;
        if("admin".equals(username)&&"123".equals(password)){
            String id="aaaaaaaa";
            //生成token
            Map<String,Object> param=new HashMap<>();
            param.put("id",id);
            token=jwtOperator.generateTokens(param);
            log.info("生成的token是:"+token);

            //将token保存到redis中
            redisHelper.setValue(id,token);
        }
        return token;
    }
}

        3.2.2、UserController

 

package com.example.spring_interceptors.Controller;

import com.example.spring_interceptors.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/login/{username}/{password}")
    public String login(@PathVariable String username,@PathVariable String password){
        String token = userService.validUser(username,password);
        if(token!=null){
            return "登录成功";
        }else {
            return "登录失败";
        }
    }

    @GetMapping("/list")
    public String[] userList(){
        String[] users={"admin","tom","mike"};
        return users;
    }
}

3.3、我们修改TestInterceptor文件(在当前文件进行判断校验)

package com.example.spring_interceptors.interceptor;

import com.example.spring_interceptors.Controller.utils.JwtOperator;
import com.example.spring_interceptors.Controller.utils.RedisHelper;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
@Slf4j
public class Testinterceptor extends WebMvcConfigurerAdapter {

    @Autowired
    private JwtOperator jwtOperator;
    @Autowired
    private RedisHelper redisHelper;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        HandlerInterceptor handlerInterceptor=new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                log.info("preHandle,在请求Controller之前触发,返回值为true代表放行,false代表拦截");

                boolean isOk=false;

                String serVletPath = request.getServletPath();//获取请求的路径
                if (serVletPath.contains("/user/login")){
                    isOk=true;//放行
                }else {
                    try {
                        String token = request.getHeader("Authorization");
                        boolean isExpire = jwtOperator.validateTokenTime(token);
                        if(isExpire){//验证token是否被篡改,是否过期
                            Claims info = jwtOperator.getClaimsFromToken(token);
                            String[] str =info.toString().replace("{","").replace("}","").split(",");
                            String userId=str[0].replace("id=","");

                            //从redis中获取token,通过userId
                            String tokenFromRedis = redisHelper.getValue(userId);
                            if(tokenFromRedis.equals(token)){
                                isOk=true;
                            }
                        }
                    } catch (Exception e) {
                        response.getWriter().write("token is disabled");
                    }
                }
                return isOk;
            }

            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                log.info("postHandle,在请求Controller之后执行");
            }

            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                log.info("afterCompletion,在整个Request请求完成后执行");
            }
        };
        registry.addInterceptor(handlerInterceptor);
    }
}

3.4、我们启动项目来进行测试

        3.4.1、通过页面访问

         3.4.2、访问之后我们就可以获得Token的加密代码,我们将其复制

 

 3.5、修改Http头字段

         3.5.1、使用token密钥进行访问

        3.5.2、拦截器的打印效果展示到控制台,说明拦截器正常运行

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值