RFC 7519
),它定义了一种紧凑且独立的方式,用于将信息作为 JSON 对象在各方之间安全地传输。该信息可以进行验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。JWT
只是缩写,全拼则是 JSON Web Tokens
,是目前流行的跨域认证解决方案,一种基于JSON的、用于在网络上声明某种主张的令牌(token)。token
的认证方式相比传统的 session
认证方式更节约服务器资源CDN
CSRF
session
状态,因此无法在使用过程中废止某个 token
,或者更改 token
的权限。就是说,一旦 JWT
签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。JWT 是由三段字符串和两个 . 组成,每个字符串和字符串之间没有换行(类似于这样:xxxxxx.yyyyyy.zzzzzz),每个字符串代表了不同的功能,我们将这三个字符串的功能按顺序列出来并讲解:
标头通常由两部分组成:令牌的类型和正在使用的签名算法(如 HMAC SHA256 或 RSA)。例如:
{
“alg”: “HS256”,
“typ”: “JWT”
}
使用 Base64 URL 算法将该 JSON 对象转换为字符串保存,形成 JWT 的第一部分。
JWT 的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和其他数据的语句。有三种类型的声明:注册声明、公共声明和私人声明。
iss:#jwt的签发者/发行人;
sub:#主题;
aud:#接收方;
exp:#jwt过期时间;
nbf:#jwt生效时间;
iat:#签发时间
jti:#jwt唯一身份标识,可以避免重放攻击
公共声明
可以在公共声明添加任何信息,我们一般会在里面添加用户信息和业务信息,但是不建议添加敏感信息,因为公共声明部分可以在客户端解密。
私有声明
私有声明是服务器和客户端共同定义的声明,同样这里不建议添加敏感信息。
以下是有效负载的示例:
{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}
要创建签名部分,必须获取编码的标头、编码的有效负载、密钥,通过指定的算法生成哈希,以确保数据不会被篡改。
例如,如果要使用 HMAC SHA256 算法,将按以下方式创建签名:
HMACSHA256( base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
签名用于验证消息在此过程中未发生更改。并且,对于使用私钥签名的令牌,它还可以验证 JWT 的发送者是否是它所说的发件人。
将所有内容放在一起,输出是三个 Base64-URL 字符串,由点分隔,可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(如 SAML)相比更紧凑。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.5version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>springboot-jwt-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>springboot-jwt-demoname>
<description>springboot-jwt-demodescription>
<properties>
<java.version>8java.version>
<jjwt.version>0.11.2jjwt.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwt-apiartifactId>
<version>${jjwt.version}version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwt-implartifactId>
<version>${jjwt.version}version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwt-jacksonartifactId>
<version>${jjwt.version}version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
LoginUser
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser {
/**
* 账户
*/
private Long accoutNo;
/**
* 用户名
*/
private String userName;
/**
* 头像
*/
private String headImg;
}
import com.example.springbootjwtdemo.model.LoginUser;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.Objects;
@Slf4j
public class JWTUtil {
/**
* token 过期时间 正常是7天
*/
private static final long EXPIRE = 1000 * 60 * 60 * 24 * 7;
/**
* 加密秘钥 UUID.randomUUID().toString().replaceAll("-","") 生成
*/
private static final String SECRET = "ff9b84d01c0940c6a5723a744d0d66a8";
/**
* token前缀
*/
private static final String TOKEN_PREFIX = "org";
/**
* subject
*/
private static final String SUBJECT = "my";
/**
* 加密秘钥
*/
private static final Key key = Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
/**
* 使用系统默认token
*/
// private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
/**
* 生成token
*
* @param loginUser 登录用户
* @return {@link String}
*/
public static String geneJsonWebToken(LoginUser loginUser) {
if (Objects.isNull(loginUser)) {
throw new NullPointerException("LoginUser对象不能为空");
}
String token = Jwts.builder().setSubject(SUBJECT)
.claim("accout_no", loginUser.getAccoutNo())
.claim("head_img", loginUser.getHeadImg())
.claim("user_name", loginUser.getUserName())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(key, SignatureAlgorithm.HS256).compact();
token = TOKEN_PREFIX + token;
return token;
}
/**
* 检查 token
*
* @param token 令牌
* @return {@link Claims}
*/
public static Claims checkJWT(String token) {
try {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
} catch (Exception e) {
log.info("jwt token解密系统异常:{}", e);
return null;
}
}
}
import com.example.springbootjwtdemo.model.LoginUser;
import com.example.springbootjwtdemo.utils.JWTUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class LoginController {
@GetMapping("login")
public String login(LoginUser loginUser) {
// 生成token
LoginUser userInfo = new LoginUser();
userInfo.setUserName("展释");
userInfo.setAccoutNo(123213L);
userInfo.setHeadImg("");
return JWTUtil.geneJsonWebToken(loginUser);
}
}