JSON Web Token
JWT 结构
- 头部:解密算法、令牌类型
- 直接 base64 编码
- 负载:数据
- 直接 base64 编码
- 签名:验证令牌是否有效
- 将前两部分拼接之后,加密处理,再base64编码
所以令牌的结构为:
头部.负载.签名
官网 可解析令牌,如果签名【第三部分】无效是无法获得令牌的内容的
注意的是,负载的数据虽然是经过base64编码,但也不能存放敏感数据,令牌存储的是用户的唯一标识:name、id、uuid等
什么是 base64 编码:用a-z A-Z 0-9 + - 等64个可打印字符来表示二进制数据 ,可编码、可解码,文本编码之后大小会变成原来的4/3,所以文本一般不用 base64 编码。
生成密钥
可以通过 postman 测试一下接口
@RequestMapping("/key")
@RestController
public class KeyController {
@GetMapping("/rsa")
public HashMap<String, String> rsa(){
// // 椭圆曲线的加密算法
// SignatureAlgorithm.ES256;
// 2、非对称密钥加密 RSA
// 获得密钥对
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
// 公钥:开放出去,用于用户加密
PublicKey publicKey = keyPair.getPublic();
byte[] pub = publicKey.getEncoded();// 公钥本身
String pubText = Base64.getEncoder().encodeToString(pub);
System.out.println("公钥:");
System.out.println(pubText);
System.out.println(pubText.length());
// 私钥:保留,用于解密
PrivateKey privateKey = keyPair.getPrivate();
byte[] pri = privateKey.getEncoded();// 私钥本身
String priText = Base64.getEncoder().encodeToString(pri);
System.out.println("私钥:");
System.out.println(priText);
System.out.println(priText.length());
HashMap<String, String> data = new HashMap<String, String>();
data.put("pub",Base64.getEncoder().encodeToString(pub));
data.put("pri", Base64.getEncoder().encodeToString(pri));
return data;
}
@GetMapping
public String sha() {
// 2、安全散列加密算法
// 密钥
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);//HS512
byte[] data = key.getEncoded();// 密钥本身
System.out.println("原始:");
System.out.println(key.getAlgorithm());
System.out.println(Arrays.toString(data));
System.out.println(data.length);
System.out.println("十六进制:");
String hex = new BigInteger(1, data).toString(16);
System.out.println(hex);
System.out.println(hex.length());
// Base64 密钥编码
System.out.println("Base 64 编码后:");
String text = Base64.getEncoder().encodeToString(data);
System.out.println(text);
System.out.println(text.length());
return text;
}
}
生成 token ,解析 token
可以将上面得到的密钥写入配置信息,全局可用
下面写的接口都可以通过postman测试一下
import java.util.Enumeration;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@RequestMapping("/jwt")
@RestController
public class TokenController {
// 自定义的密钥
@Value("${key.base64}")
String base64key;
//1、JS提交、客户端:application/json
@PostMapping("signup2")
public ResponseEntity<String> create2(@RequestBody User user){
if(!user.getName().equals(user.getPassword())) {
return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
}
System.out.println("用户名密码一致!!验证通过,可发放令牌");
// 负载
Claims claims=Jwts.claims();
claims.put("role", "vip");
claims.put("num", "9");
claims.setId(UUID.randomUUID().toString());
claims.setSubject("主体");
claims.setIssuer("签发者");
claims.setAudience(user.getName());
// 生成令牌
String jws=Jwts.builder()
.signWith(SignatureAlgorithm.HS256,base64key)
.setClaims(claims)
.compact();
// HTTP 响应头
HttpHeaders headers=new HttpHeaders();
// 响应头存放令牌
headers.setBearerAuth(jws);
return new ResponseEntity<String>(jws,headers,HttpStatus.OK);
}
//2、表单提交 content-type: x-application-form-encodingurl
@PostMapping("signup")
public ResponseEntity<String> create1(String name,String password){
//第三方,可直接 使用 微博、QQ、微信 登录
//第三方只能获得:用户名、头像、令牌(标识)
if(!name.equals(password)) {
return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
}
System.out.println("用户名密码一致,可发放令牌");
Claims claims=Jwts.claims();
claims.put("role", "vip");
claims.put("num", "9");
claims.setId(UUID.randomUUID().toString());
claims.setSubject("主体");
claims.setIssuer("签发者");
claims.setAudience(name);
String jws=Jwts.builder()
.signWith(SignatureAlgorithm.HS256,base64key)
.setClaims(claims)
.compact();
// 服务器保存令牌
// HTTP 响应头
HttpHeaders headers=new HttpHeaders();
// 保存令牌
headers.setBearerAuth(jws);
return new ResponseEntity<String>(jws,headers,HttpStatus.OK);
}
// 解析 token
@GetMapping("/foo")
public String parse(HttpServletRequest request) {
// 令牌,在请求头
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
System.out.println("jwt:" + token);
// 获得的令牌格式:前面7个字符多余,要截掉
// Bearer jwt
// 解析
String name=Jwts.parserBuilder()
.setSigningKey(base64key)
.build()
.parseClaimsJws(token.substring(7))
.getBody()
.getId();
return name;
}
}
user
package com.newer.jwt;
public class User {
String name;
String password;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [name=" + name + ", password=" + password + "]";
}
}
postman 测试
登录时生成 token
解析token
复制上面生成的token
查看 Headers
去官网解析一下,通过就会可查看数据