Spring Boot JWT 快速入门
本章节讨论 jwt 在 spring boot 中的应用。意在快速入门 jwt。
- java jdk1.8
- maven 3.2+
- spring boot 2.0+
JSON Web Token(JWT) 他是一个用于 Web 身份验证的令牌。
1 JWT 概述
1.1 什么是 JWT
直观的理解 JWT 就是一串字符串,如下(来自于 JWT.IO):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
细心的你可能会发现字符串是有 3 个独立的字符串使用.
号组合而成,前两个字符串是 Base64 编码,最后一个字符串是一个加密后的字符串。
序号 | 字符串 |
---|---|
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 |
2 | eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ |
3 | SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c |
JWT 使用以上 3 个字符串组成一个字符串,以 xxxxx.yyyyyy.zzzzz
的形式传输给认证服务器
xxxxx.yyyyyy.zzzzz
表示为 Header.Payload.Signature
Header=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload=eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature=SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
我们使用 JWT Debuger 把信息解码可以看出:
1.1.1 Header
Header 头信息,以 json 字符串实现,并压缩成 Base64URL
编码字符串传输
{
"alg": "HS256",
"typ": "JWT"
}
上面代码中,alg 属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ 属性表示这个令牌(token)的类型(type),JWT 令牌统一写为 JWT。
1.1.2 Payload
Payload 负载,以 json 字符串实现,并压缩成 Base64 编码字符串传输
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
官方对 json 内容节点给出了一些建议,我个人觉得意义不大,况且那个建议字段完全是看不出任何含义的缩写。比如 iss、exp、sub、aud、nbf、iat、jti
。当然如果大家都能共同遵守,也不存在这些问题。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
1.1.3 Signature
VERIFY SIGNATURE 签名,在服务端指定一个秘钥,把签名两个 json 字符串使用下面方法加密而成的
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
only-save-in-server-secret)
值得注意的是,使用 JWT 推荐使用 HTTPS 加密协议
1.1.4 Base64URL
BASE64URL 是一种在 BASE64 的基础上编码形成新的加密方式,为了编码能在网络中安全顺畅传输,需要对 BASE64 进行的编码,特别是互联网中。
1.2 JWT 是如何工作的
为了能够支持跨域,通常 JWT 的字符串 xxxxx.yyyyyy.zzzzz
通过 Http 的 Head 发送
Authorization: Bearer <token>
也可以放在 Post 数据体重。
2 Spring Boot JWT 示例
在 JWT 官方可以看到,maven: io.jsonwebtoken / jjwt / 0.9.0
这个库是支持最全的。
2.1 新建 Spring Boot Maven 示例工程项目
- File > New > Project,如下图选择
Spring Initializr
然后点击 【Next】下一步 - 填写
GroupId
(包名)、Artifact
(项目名) 即可。点击 下一步
groupId=com.fishpro
artifactId=jwt - 选择依赖
Spring Web Starter
前面打钩。 - 项目名设置为
spring-boot-study-jwt
.
2.2 依赖引入 Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.fishpro</groupId>
<artifactId>jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jwt</name>
<description>Demo project for Spring Boot</description>
<span class="hljs-tag"><<span class="hljs-name">properties</span>></span>
<span class="hljs-tag"><<span class="hljs-name">java.version</span>></span>1.8<span class="hljs-tag"></<span class="hljs-name">java.version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">properties</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependencies</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-web<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.auth0<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>java-jwt<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.4.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-test<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>test<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span>
<span class="hljs-tag"><<span class="hljs-name">build</span>></span>
<span class="hljs-tag"><<span class="hljs-name">plugins</span>></span>
<span class="hljs-tag"><<span class="hljs-name">plugin</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-maven-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"></<span class="hljs-name">plugin</span>></span>
<span class="hljs-tag"></<span class="hljs-name">plugins</span>></span>
<span class="hljs-tag"></<span class="hljs-name">build</span>></span>
</project>
2.4 编写 JWT 生成与验证代码
本章节知识学习 JWT,不涉及其他知识点,故我们之间在 JwtApplication.java 中来编写代码
package com.fishpro.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.*;
@SpringBootApplication
public class JwtApplication {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
SpringApplication.run(JwtApplication.class, args);
String token=createJWTToken();<span class="hljs-comment">//获取生成的Token</span>
verifyJWTToken(token);<span class="hljs-comment">//验证生成的Token</span>
}
<span class="hljs-comment">/**
* 生成 JWT Token
* */</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">createJWTToken</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> JWTCreationException {
String secret=<span class="hljs-string">"secret"</span>;<span class="hljs-comment">//假设服务端秘钥</span>
<span class="hljs-type">Algorithm</span> <span class="hljs-variable">algorithm</span> <span class="hljs-operator">=</span> Algorithm.HMAC256(secret);
<span class="hljs-comment">//jwt 头部信息</span>
Map<String,Object> map=<span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span><>();
map.put(<span class="hljs-string">"alg"</span>,<span class="hljs-string">"HS256"</span>);
map.put(<span class="hljs-string">"typ"</span>,<span class="hljs-string">"JWT"</span>);
<span class="hljs-type">Date</span> <span class="hljs-variable">nowDate</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();
<span class="hljs-type">Date</span> <span class="hljs-variable">expireDate</span> <span class="hljs-operator">=</span> AddDate(nowDate,<span class="hljs-number">2</span>*<span class="hljs-number">60</span>);<span class="hljs-comment">//120 分钟过期</span>
String token= JWT.create()
.withHeader(map)
.withIssuer(<span class="hljs-string">"SERVICE"</span>) <span class="hljs-comment">//对应 paylaod iss 节点:签发人</span>
.withClaim(<span class="hljs-string">"loginName"</span>,<span class="hljs-string">"fishpro"</span>)
.withSubject(<span class="hljs-string">"this is a token demo"</span>)<span class="hljs-comment">//对应 paylaod sub 节点:主题</span>
.withAudience(<span class="hljs-string">"Client"</span>)<span class="hljs-comment">//对应 paylaod aud 节点:受众</span>
.withIssuedAt(nowDate)<span class="hljs-comment">//对应 paylaod iat 节点:生效时间</span>
.withExpiresAt(expireDate) <span class="hljs-comment">//对应 paylaod exp 签发人 节点:过期时间</span>
.sign(algorithm);
<span class="hljs-keyword">return</span> token;
}
<span class="hljs-comment">/**
* 验证 token
* */</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">verifyJWTToken</span><span class="hljs-params">(String token)</span> <span class="hljs-keyword">throws</span> JWTVerificationException {
Algorithm algorithm=Algorithm.HMAC256(<span class="hljs-string">"secret"</span>);
<span class="hljs-type">JWTVerifier</span> <span class="hljs-variable">verifier</span> <span class="hljs-operator">=</span> JWT.require(algorithm)
.withIssuer(<span class="hljs-string">"SERVICE"</span>)
.build();
<span class="hljs-type">DecodedJWT</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span>verifier.verify(token);
String subject=jwt.getSubject();
Map<String,Claim> claims=jwt.getClaims();
<span class="hljs-type">Claim</span> <span class="hljs-variable">claim</span> <span class="hljs-operator">=</span> claims.get(<span class="hljs-string">"loginName"</span>);
System.out.println(<span class="hljs-string">"自定义 claim:"</span>+claim.asString());
List<String> audience = jwt.getAudience();
System.out.println(<span class="hljs-string">"subject 值:"</span>+subject);
System.out.println(<span class="hljs-string">"audience 值:"</span>+audience.get(<span class="hljs-number">0</span>));
}
<span class="hljs-comment">/**
* 时间加减法
* */</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Date <span class="hljs-title function_">AddDate</span><span class="hljs-params">(Date date,Integer minute)</span>{
<span class="hljs-keyword">if</span>(<span class="hljs-literal">null</span>==date)
date=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>();
Calendar cal=<span class="hljs-keyword">new</span> <span class="hljs-title class_">GregorianCalendar</span>();
cal.setTime(date);
cal.add(Calendar.MINUTE, minute);
<span class="hljs-keyword">return</span> cal.getTime();
}
}
2.5 演示效果
右键 JwtApplication 选择 Run JwtApplication ,观察 console 窗口