前言

简单了解一下原理,重点知道怎么用

Token:简单来说就是一个用户在一段时间内的标识,只有登录成功才能获得Token,一般情况下只有携带Token的请求才能被Interceptor放行。

Interceptor:这是一个过滤器技术,可以将Interceptor理解为一个筛子,在请求到达控制层前进行拦截,根据具体的业务需求,可以对请求进行一定操作,比如拦截、放行等等。


Jwt快速入门

JwtToken的组成

  • Header

标头,一般用来标识令牌类型和所使用的签名算法。

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
  • Payload

有效载荷,保存自定义信息。

1
2
3
4
5
6
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
  • Signature

签名,融合了Header和Payload,根据输入的secret基于签名算法生成而来,是保证token不被篡改。

上面的信息都是json格式的字符串,标头和载荷部分是Base64编码后直接构成token的前两部分,第三部分由标签算法、密钥和输入的信息(Header、Payload)决定。

Token的创建与验证

使用jwt提供的工具包快速生成令牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//生成jwt令牌
@Test
public void jwtCreate(){
//自定义信息
Map<String, Object> claims = new HashMap<>();
claims.put("username", "m1kasaz");
claims.put("id", 1);
String token = Jwts.builder()
//将自定义信息加入token
.addClaims(claims)
//将签名算法和密钥(基于base64编码)加入token
.signWith(SignatureAlgorithm.HS256, "bTFrYXNheg==")
//设置过期时间为 当前时间+1h
.setExpiration(new Date(System.currentTimeMillis()+3600*60))
//生成
.compact();
System.out.println(token);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//解析jwt令牌
@Test
public void jwtParse(){
//拿到更改生成的token
String token = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJtMWthc2F6IiwiZXhwIjoxNzUxMzgyMDQ0fQ.vuyp1XEZv3ncI-F6U63QlRHymG_du_IlETE0ZiVarfE";
//获取自定义信息
Claims claims = Jwts.parser()
//设置密钥
.setSigningKey("bTFrYXNheg==")
//设置需要解析的令牌
.parseClaimsJws(token)
//获取自定义信息
.getBody();
System.out.println(claims);
}

一般将jwt创建和验证封装为工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class jwtUtils {
//密钥
private static final String Key = "bTFrYXNheg==";
//持续时间
private static final Long EXP = 3600*60L;
//使用的签名算法
private static final SignatureAlgorithm SIGN = SignatureAlgorithm.HS256;
//创建token
public String createToken(Map<String, Object> claims){
String token = Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis()+EXP))
.signWith(SIGN, Key)
.compact();
return token;
}

//解析token
public Claims parseToken(String token){
Claims claims = Jwts.parser()
.setSigningKey(Key)
.parseClaimsJws(token)
.getBody();
return claims;
}
}

Interceptor快速入门

Interceptor由springboot提供,基于AOP(面向切面编程)思想的过滤技术。

自定义Interceptor

想要自定义 Interceptor,必须实现HandlerInterceptor或HandlerInterceptorAdapter类中的一个。并需要重写三个方法中至少一个:

这三个方法都是默认方法,既可以实现也可以不实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//在请求到达Controller方法之前进行操作,返回true表示放行,返回false表示拒绝
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return HandlerInterceptor.super.preHandle(request, response, handler);
}

/**
* 在Controller层方法执行完后该方法会被调用,可以用这个方法实现一些与前端交互的过程
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}

/**
* 一般只用于作资源回收,在前端渲染了相应资源后执行
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}

下面是一个用于控制登录请求的过滤器示例

主要实现的功能是解析jwt令牌,如果令牌合法就放行,否则拒绝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {
@Autowired
private JwtProperties jwtProperties;//jwt的配置类,用于记录密钥等个人信息

/**
* 校验jwt
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}

//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getUserTokenName());

//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
BaseContext.setCurrentId(userId);
log.info("当前用户id:", userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}

注册过滤器

ok,在知道怎样创建inteceptor过滤器后,我们怎么使用呢?

其实AOP将自动帮我们完成调用过程,我们只需要知道怎么注册一个inteceptor过滤器就好了。

注册过程可以借助Springboot的WebMvcConfigurer配置类,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

//将我们刚刚创建的类自动注入
@Autowired
private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

/**
* 注册自定义拦截器
*/
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
registry.addInterceptor(jwtTokenAdminInterceptor)
.addPathPatterns("/admin/**") //这个指定了过滤器的作用范围
.excludePathPatterns("/admin/employee/login"); //这个指定过滤器将排除的请求路径
}

jwt官方文档

csdn