JWT
JWT: Json Web Token
JWS Json Web Signature
JWE: Json Web Encryption
JWT是一種在雙方之間傳輸?shù)木o湊的,URL安全的令牌表示方式
JWT 表示為編碼的 JSON 對(duì)象沼死, 可用于 JWS 的的負(fù)載章钾, 或者 JWE 的明文
簽過名的 JWT 稱 JWS
加過密的 JWT 稱 JWE
最終的 JWS 形為 ${Header}.${Payload}.${Signature}
它包含三部分
1. Header 頭部
表示基本算法和格式的 JSON 對(duì)象, 例如
{
"alg": "HS256", //簽名算法是 HMAC SHA256
"typ": "JWT" // token 格式是 JWT
}
2. Payload 正文
以一個(gè) JSON 對(duì)象表示 JWT 中所包含的內(nèi)容, 字段可自由定義, 以下 7 個(gè)字段是官方定義的
- iss (issuer):簽發(fā)人
- exp (expiration time):過期時(shí)間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時(shí)間
- iat (Issued At):簽發(fā)時(shí)間
- jti (JWT ID):編號(hào)
我們也可以加上自己定義的內(nèi)容, 例如
{
"orgId": "$orgId",
"userId": "$userId",
"roles": ["admin"],
"scopes": ["create", "write"]
}
3. Signature 簽名
以頭部所聲明的算法將頭部和正文部分的內(nèi)容進(jìn)行簽名, 防止被人篡改.
HMACSHA256(base64UrlEncode(header) + base64UrlEncode(payload), secret)
實(shí)例
寫一個(gè)簡(jiǎn)單的例子來演示一下 JWT的編碼和解碼
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @Author: Walter Fan
* @Date: 16/6/2019, Sun
**/
@Slf4j
public class JwtUtil {
private static final Long ONE_HOUR_MS = 60 * 60 * 100L;
public static String createJws(String subject, Map<String, Object> claims, long liveSeconds, String apiKey) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
long expMillis = System.currentTimeMillis() + liveSeconds * 1000;
Date expireDate = new Date(expMillis);
JwtBuilder jwtBuilder = Jwts.builder()
.setId(UUID.randomUUID().toString())
.setSubject(subject)
.setIssuedAt(new Date())
.setExpiration(expireDate)
.signWith(signingKey);
if(!Collections.isEmpty(claims)) {
claims.entrySet().stream().forEach( x -> jwtBuilder.claim(x.getKey(), x.getValue()));
}
return jwtBuilder.compact();
}
public static Claims parseJws(String compactJws, String apiKey) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
Claims ret = Jwts.parser()
.setSigningKey(signingKey)
.parseClaimsJws(compactJws)
.getBody();
return ret;
}
public static void main(String[] args) {
String proverb = "A journey of a thousand miles begins with a single step";
String apiKey = Encoders.BASE64.encode(proverb.getBytes());
Map<String, Object> map = new HashMap<>();
map.put("orgId", UUID.randomUUID());
map.put("userId", UUID.randomUUID());
map.put("roles", Arrays.asList("admin"));
map.put("scopes", Arrays.asList("read", "write"));
String jws= createJws("potato", map, 300, apiKey);
log.info("jws = {}", jws);
Claims claims = parseJws(jws, apiKey);
claims.entrySet().stream().forEach(x -> log.info("{} = {}", x.getKey(), x.getValue()));
}
}
輸出如下
# 編碼過的內(nèi)容
JwtUtil - jws = eyJhbGciOiJIUzM4NCJ9.eyJqdGkiOiJkOTJkNGRlZC00MjVjLTRhMGItYjVkZi05OGYzN2FiNDVmMzkiLCJzdWIiOiJwb3RhdG8iLCJpYXQiOjE1NjA2NjgwMjIsImV4cCI6MTU2MDY2ODMyMiwicm9sZXMiOlsiYWRtaW4iXSwic2NvcGVzIjpbInJlYWQiLCJ3cml0ZSJdLCJ1c2VySWQiOiIyNzYwMWE1Zi0zZDllLTQ3NWMtOGQzNS0wYTdlZGNlMzZhOGYiLCJvcmdJZCI6IjQ2ZDZhYjM1LTM5MmUtNDE4OS1iMGJjLTk3YzhkMzM2MzA2YSJ9.XydcyqdZD18Kgt0YqpOD_vD_NreHtNh_kyW8ZqxfWbhT4iNYOquQxsxyE8b2Xrul
# 解碼過的內(nèi)容
JwtUtil - jti = d92d4ded-425c-4a0b-b5df-98f37ab45f39
JwtUtil - sub = potato
JwtUtil - iat = 1560668022
JwtUtil - exp = 1560668322
JwtUtil - roles = [admin]
JwtUtil - scopes = [read, write]
JwtUtil - userId = 27601a5f-3d9e-475c-8d35-0a7edce36a8f
JwtUtil - orgId = 46d6ab35-392e-4189-b0bc-97c8d336306a
在 Web Service 實(shí)際應(yīng)用中, 經(jīng)常把 JWT 放到 HTTP 的 Authorization 頭域中, 客戶端從認(rèn)證服務(wù)器那里申請(qǐng)一個(gè) token , 此 token 也就是認(rèn)證服務(wù)器所簽發(fā)的 JWT, 用這個(gè) token 來訪問資源.
資源服務(wù)器會(huì)校驗(yàn)這個(gè) token , 也會(huì)到認(rèn)證服務(wù)器那里會(huì)驗(yàn)證步绸。