jwt

jwt

xxx.yyy.zzz -> header.payload.signature

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

header标记了使用的算法和token的类型

payload

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}

payload中存放了一些数据,比如用户id、用户名等,还有一个iat字段,表示token的签发时间

ps. payload中的数据不要存放敏感信息,因为payload是可以解码的

signature

1
2
3
4
5
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)

signature是对header和payload的签名,防止数据被篡改,当服务端接收到token后,会重新计算一次签名,如果签名不一致,说明数据被篡改

两种使用方式

  1. 使用java-jwt
1
2
3
4
5
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.14.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
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
/**
* 生成token header.payload.singature
*/
private static final String SING = "!Q@W#E$R";

public static String getToken(Map<String, String> map) {
Calendar instance = Calendar.getInstance();

// 默认7天过期
// instance.add(Calendar.DATE, 7);
// 120秒过期
instance.add(Calendar.SECOND, 120);

//创建jwt builder
JWTCreator.Builder builder = JWT.create();

// payload
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
String token = builder.withExpiresAt(instance.getTime()) //指定令牌过期时间
.sign(Algorithm.HMAC256(SING)); // sign
return token;
}

/**
* 验证token 合法性
*/
public static DecodedJWT verify(String token) {
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import com.dunjia.jwtdemo.utils.JWTUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.HashMap;
import java.util.Map;

@Component
public class JWTInterceptors implements HandlerInterceptor {

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Map<String, Object> map = new HashMap<>();
// 获取请求头中令牌
String token = request.getHeader("token");
if (token != null && JWTUtils.verify(token) != null) {
return true;
} else {
map.put("msg", "token无效,请重新登录!");
map.put("state", false);
return false;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.dunjia.jwtdemo.interceptor.JWTInterceptors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

@Autowired
private JWTInterceptors jwtInterceptors;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptors)
.addPathPatterns("/user/**") // 其他接口token验证
.excludePathPatterns("/user/login"); // 所有用户都放行
}
}
  1. 使用jjwt
1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</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
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.UUID;

public class JwtUtil {

private static long time = 1000 * 60 * 60 * 24; // 24 hour
private static String signature = "#@!$%^&*()";

/**
* 生成token
* @return
*/
public static String createToken() {
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken = jwtBuilder
// header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
// payload
.claim("username", "admin")
.claim("password", "admin")
.setSubject("jwt-token")
.setExpiration(new Date(System.currentTimeMillis() + time))
.setId(UUID.randomUUID().toString())
// signature
.signWith(SignatureAlgorithm.HS256, signature)
.compact();
return jwtToken;
}

/**
* 校验token
* @param token
* @return
*/
public static boolean verifyToken(String token) {
if (token == null) {
return false;
}
try {
Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
} catch (Exception e) {
return false;
}
return 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
27
28
29
30
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 放行OPTIONS请求
if ("OPTIONS".equals(request.getMethod())) {
return true;
}

// 获取请求头中的token
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
// 截取token
token = token.substring(7);
// 验证token
if (JwtUtil.verifyToken(token)) {
return true;
}
}

// token无效,返回401
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.dunjia.back.utils.JwtInterceptor;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/api/**") // 拦截所有/api/**的请求
.excludePathPatterns( // 不拦截以下路径
"/api/login", // 登录接口
"/api/register", // 注册接口
"/api/upload", // 上传图片接口
"/image/**" // 静态资源
);
}
}