初识JWT
JWT
1. 简介
- 介绍
JWT:JSON Web Token,通过数字签名的方式以JSON对象为载体,在不同的服务终端之间安全的传输信息
- 作用
JWT最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在没错处理用户的请求之前,都要先进行JWT安全校验,通过之后在进行处理
- 组成
JWT由三部分组成,以"."进行拼接
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
三部分:
-
Header
jwt的头部承载两部分信息:
-
声明类型,这里是jwt
-
声明加密的算法 通常直接使用 HMAC SHA256
{ 'typ': 'JWT', 'alg': 'HS256' }
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 -
-
Payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
声明(claims)
-
标准中注册的声明
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
java代码对应的声明 setIssuer: sets the iss (Issuer) Claim setSubject: sets the sub (Subject) Claim setAudience: sets the aud (Audience) Claim setExpiration: sets the exp (Expiration Time) Claim setNotBefore: sets the nbf (Not Before) Claim setIssuedAt: sets the iat (Issued At) Claim setId: sets the jti (JWT ID) Claim -
公共的声明
-
私有的声明
{ "sub": "1234567890", "name": "John Doe", "admin": true }
-
-
Signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用
.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。// javascript var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
2. 使用
-
依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> -
测试
public class Test { private long time = 1000 * 60 * 60 * 24; // 签名 private String signatrue = "admin"; // 加密 @org.junit.Test public void jwt() { // 获取jwtbuilder对象 JwtBuilder builder = Jwts.builder(); // 设置Header String token = builder.setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") // 设置 payload .claim("username", "tzeao") .claim("role", "admin") // 主题 .setSubject("admin-test") // 过期时间 .setExpiration(new Date(System.currentTimeMillis() + time)) // id .setId(UUID.randomUUID().toString()) // 设置Signature 两个参数分别是签名算法和自定义的签名Key(盐) .signWith(SignatureAlgorithm.HS256, signatrue) // 全部拼接起来 .compact(); System.out.println(token); } // 解密 @org.junit.Test public void parse() { String tonke = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InR6ZWFvIiwicm9sZSI6ImFkbWluIiwic3ViIjoiYWRtaW4tdGVzdCIsImV4cCI6MTYyODcwMjc3OSwianRpIjoiOTA2MWQ4MGQtYjY0Ni00NzUyLWEyODctN2MzYTBjYzUyYmYyIn0.vCSbCfDrnduxHxViOOLZxs98ZjhMhm6hwvq_9MEKlFI"; JwtParser parser = Jwts.parser(); // 通过签名来解密 Jws<Claims> jws = parser.setSigningKey(signatrue).parseClaimsJws(tonke); // 这个 === payload Claims body = jws.getBody(); Object o = body.get("username"); JwsHeader header = jws.getHeader(); String signature = jws.getSignature(); System.out.println(signature); System.out.println(header+o.toString()); } } -
可以使用这些实例直接添加Header header = Jwts.header();Claims claims = Jwts.claims();
3. SpringBoot 整合JWT
-
创建实体类
@Datapublic class User { private String username; private String password; private String token;} -
控制器
@RestControllerpublic class UserController { private final String USERNAME = "admin"; private final String PASSWORD = "123123";// 返回给前端的token @GetMapping("/login") public User login(User user) { if (USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())) {// 添加token user.setToken(JWTUtil.craterToken()); return user; } return null; }} -
JWT工具类
public class JWTUtil { private static final long time = 1000 * 60 * 60 * 24; // 签名(盐) private static final String signature = "!@#$#$%^%&$@#$@#$!#hjhh$!$!#$"; public static String craterToken() { JwtBuilder builder = Jwts.builder(); String token = builder.setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") .claim("username", "admin") .claim("role", "admin") .setSubject("admin-test") .setExpiration(new Date(System.currentTimeMillis() + time)) .setId(UUID.randomUUID().toString()) .signWith(SignatureAlgorithm.HS256, signature) .compact(); return token; } // 校验token public static boolean checkToken(String token) { if (token == null) { return false; } try { Jws<Claims> jws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token); } catch (ExpiredJwtException e) { return false; } return true; }} -
解决跨域问题
@Configurationpublic class CrosConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600) .allowedHeaders("*"); }} -
再创建一个项目编写前端
链接: https://pan.baidu.com/s/1U91Y3MGbykBJxCdWY0FIyg 提取码: mhwh
-
注意 — 密钥的长度是有要求的16字节以上,不够就会报65509
-
控制器不使用@Controller,要使用@RestController 否则会报
Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
-
前端传token过来,进行验证
@GetMapping("/checkToken") public Boolean checkToken(HttpServletRequest request) { // 通过前端传过来的请求头,取出token String token = request.getHeader("token"); return JWTUtil.checkToken(token); }
重新登录会重新创建token

浙公网安备 33010602011771号