SpringBoot整合jwt


在商业项目中,后端并非只完成http接口就可以,更要对安全问题进行验证,主流springsecurity,shiro,jwt

整合步骤

maven依赖

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

配置拦截器

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package com.obox.obox.model.token;

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

import com.obox.obox.Utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import java.lang.reflect.Method;

@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {


public static final String USER_KEY = "oboxusrid";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String servletPath = request.getServletPath();
System.out.println("ServletPath: " + servletPath);

// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod() ;

// 检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(NoToken.class)) {
NoToken passToken = method.getAnnotation(NoToken.class);
if (passToken.required()) {
return true;
}
}

// 需要验证
String token = getToken(request);


if (StringUtils.isBlank(token)) {
throw new Exception("失效,请重新登录");
}

// 获取签名信息
Claims claims = JwtUtils.checkToken(token);
System.out.println("TOKEN: " + claims);
// 判断签名是否存在或过期
boolean b = claims == null || claims.isEmpty() || JwtUtils.checkToken(token) == null;
if (b) {
throw new Exception("失效,请重新登录");
}
// 将签名中获取的用户信息放入request中;
request.setAttribute(USER_KEY, claims.getSubject());
return true;
}


/**
* 获取请求Token
*/
private String getToken(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
token = request.getParameter("Authorization");
}
return token;
}

/**
* 不用拦截的页面路径(也可存入数据库中), 不要以 / 结尾
*/
private static final String[] NOT_CHECK_URL = {"/test/**", "/login/**"};

/**
* 根据URL判断当前请求是否不需要校验, true:需要校验
*/
public boolean isNotCheck(String servletPath) {
// 若 请求接口 以 / 结尾, 则去掉 /
servletPath = servletPath.endsWith("/")
? servletPath.substring(0,servletPath.lastIndexOf("/"))
: servletPath;
System.out.println("servletPath = " + servletPath);
for (String path : NOT_CHECK_URL) {
System.out.println("path = " + path);
// path 以 /** 结尾, servletPath 以 path 前缀开头
if (path.endsWith("/**")) {
String pathStart = path.substring(0, path.lastIndexOf("/")+1);
System.out.println("pathStart = " + pathStart);
if (servletPath.startsWith(pathStart)) {
return true;
}
String pathStart2 = path.substring(0, path.lastIndexOf("/"));
System.out.println("pathStart2 = " + pathStart2);
if (servletPath.equals(pathStart2)) {
return true;
}
}
// servletPath == path
if (servletPath.equals(path)) {
return true;
}
}
return false;
}
}

设置无需验证注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.obox.obox.model.token;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 无需token验证注解
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NoToken {
boolean required() default true;
}

设置拦截器生效

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
package com.obox.obox.configuration;

import com.obox.obox.model.token.JwtInterceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private JwtInterceptor jwtInterceptor;

/**
* APP接口拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor).addPathPatterns("/**");
}

}

设置JWTUtil

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.obox.obox.Utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

/**
* jwt 工具类
*
*/
public class JwtUtils {

public static String SUBJECT = "oboxweb" ;
public static final long EXPIRE = 1000*60*60 ;
public static final String APPSECRET = "oboxsecret" ;

/**
* Token 生成
* @param info
* @return
*/
public static String geneJsonWebToken(String info){

if (info != null) {
String token = Jwts.builder().setSubject(SUBJECT)
.claim("info", info) // 加密信息
.setIssuedAt(new Date()) // 发行时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) // 过期时间
.signWith(SignatureAlgorithm.HS256, APPSECRET).compact(); // 设置加密算法和加密串

return token ;
}else {
return null ;
}

}

/**
* token 校验
* @param token
* @return
*/
public static Claims checkToken(String token){

try {

final Claims claims = Jwts.parser().setSigningKey(APPSECRET).parseClaimsJws(token).getBody() ;
return claims ;
}catch (Exception e){

return null ;
}

}

/**
* token 解密
* @return
*/
public static String decipheringToken(String token){
try {

final Claims claims = Jwts.parser().setSigningKey(APPSECRET).parseClaimsJws(token).getBody() ;
return (String) claims.get("info") ;
}catch (Exception e){

return null ;
}

}



}