一、我们新建一个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、拦截器的打印效果展示到控制台,说明拦截器正常运行