1.jwt的构成
(1)header
(2)payload
(3)signature
2.token的登陆原理
3.在实际中如何应用token
(1)设置token的生成代码
(2)如何从token中获取有用的信息
(3)验证token
(4)实践得到的结果
4.附录
(1)创建token所用的源码
(2)获取token信息的源码
header(头部) payload(载荷) signature(签名)
(1)header 承接两部分信息
声明类型: jwt
声明加密算法: 一般使用的是HMAC SHA256
完整的头部就像下面这样的JSON:
{
"typ": "JWT",
"alg": "HS256"
}
(2)payload 存放有效信息的地方
可以大致分为三部分
标注中的注册声明: 可以再token中存放自定义的字段,但是要注意不能存放一些关键信息
公共声明:公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
标注中的注册声明:
iss:jwt的签发者
sub:jwt所面向的用户
aud:接收jwt的一方
exp:jwt的过期时间,就是token的有限期限
nbf:定义在什么时间之前jwt都不可以用
iat:jwt的签发时间
jti:jwt唯一身份标识,主要用来生成一次性的token
(3)signature(签证)
是根据加密算法得到的数据,其中加密的密钥是存在在后端服务器中,然后这三个部分连接起来就是一个token
首先用户输入账号和密码进行登录,后端根据数据库进行验证用户,验证通过生成token。然后前端将后端返回的token保存起来,每次用户访问请求都带着token,在后端经过拦截器的拦截,然后再进行判断,如果token验证通过放行,否则不能让用户访问界面。
(1)设置token的生成代码
/**
* token携带id和name
*/
private static final String CLAIM_KEY_ID = "sub";//系统自带的
private static final String CLAIM_KEY_NAME = "name";//自定义的
/**
* token生成时间
*/
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expire}")
private int expire;
public String createToken(Long id,String name) {
Date date = DateUtil.offset(new Date(), DateField.DAY_OF_YEAR, expire);//设置过期时间,其中这个expire是自己设置的时间,可以定义一个常量,也可以在通过注入的方式,将其配置在xml中。
Algorithm algorithm = Algorithm.HMAC256(secret);//使用jwt类的Algorithm 对象,去对密钥(第三部分的签证)进行加密
JWTCreator.Builder builder = JWT.create();//使用jwt的对应的类的方法将jwt的三部分组成结合起来,形成一个token。
String token = builder
.withClaim(CLAIM_KEY_ID, id)
.withClaim(CLAIM_KEY_NAME, name)
.withClaim(CLAIM_KEY_CREATED, new Date())
.withExpiresAt(date)
.sign(algorithm);
//以上都是Builder的方法,我已放在后面。
return token;
}
/**
* 获取指定日期偏移指定时间后的时间,生成的偏移日期不影响原日期
*
* @param date 基准日期
* @param dateField 偏移的粒度大小(小时、天、月等){@link DateField}
* @param offset 偏移量,正数为向后偏移,负数为向前偏移
* @return 偏移后的日期
*/
public static DateTime offset(Date date, DateField dateField, int offset) {
return dateNew(date).offset(dateField, offset);
}
//这是他的简介
/**
* The Algorithm class represents an algorithm to be used in the Signing or Verification process of a Token.
*/
/** * Algorithm 类表示在令牌的签名或验证过程中使用的算法。 */
/**
* Creates a new Algorithm instance using HmacSHA256. Tokens specify this as "HS256".
*
* @param secret the secret to use in the verify or signing instance.
* @return a valid HMAC256 Algorithm.
* @throws IllegalArgumentException if the provided Secret is null.
*/
/** * 使用 HmacSHA256 创建一个新的算法实例。令牌将其指定为“HS256”。 * * @param secret 在验证或签名实例中使用的秘密。 * @return 有效的 HMAC256 算法。 * @throws IllegalArgumentException 如果提供的 Secret 为空。 */
public static Algorithm HMAC256(String secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
}
/**
* The JWTCreator class holds the sign method to generate a complete JWT (with Signature) from a given Header and Payload content.
*/
/** * JWTCreator 类包含 sign 方法,用于从给定的 Header 和 Payload 内容生成完整的 JWT(带签名)。 */
@SuppressWarnings("WeakerAccess")
public final class JWTCreator {
private final Algorithm algorithm;
private final String headerJson;
private final String payloadJson;
//截取部分源码
(2)如何从token中获取有用的信息
/**
* 从token中获取id
*
*
* @param token token
* @return 用户id
*/
public Long getIdFromToken(String token) {
DecodedJWT jwt = JWT.decode(token);// DecodedJWT是用来解码token
Long id = jwt.getClaim(CLAIM_KEY_ID).asLong();
return accountId;
}
/**
* 从token中获取name
*
* @param token token
* @return 用户name
*/
public String getNameFromToken(String token) {
DecodedJWT jwt = JWT.decode(token);
String name=jwt.getClaim(CLAIM_KEY_NAME).asString();
return accountId;
}
(3)验证token
/**
* 验证token
* 验证成功什么也不返回,验证失败抛出JWTVerificationException异常
*
* @param token token
*/
//固定写法就对了
public void verifierToken(String token) {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
}
(4)实践得到的结果
这上面只是测试,可以看到详细的结果。和上面的实际代码有些冲突。
(1)创建token所用的源码
/**
* The Builder class holds the Claims that defines the JWT to be created.
*/
/** * Builder 类包含定义要创建的 JWT 的 Claims。 */
public static class Builder {
private final Map<String, Object> payloadClaims;
private Map<String, Object> headerClaims;
Builder() {
this.payloadClaims = new HashMap<>();
this.headerClaims = new HashMap<>();
}
/**
* Add specific Claims to set as the Header.
* If provided map is null then nothing is changed
* If provided map contains a claim with null value then that claim will be removed from the header
*
* @param headerClaims the values to use as Claims in the token's Header.
* @return this same Builder instance.
*/
public Builder withHeader(Map<String, Object> headerClaims) {
if (headerClaims == null) {
return this;
}
for (Map.Entry<String, Object> entry : headerClaims.entrySet()) {
if (entry.getValue() == null) {
this.headerClaims.remove(entry.getKey());
} else {
this.headerClaims.put(entry.getKey(), entry.getValue());
}
}
return this;
}
/**
* Add a specific Key Id ("kid") claim to the Header.
* If the {@link Algorithm} used to sign this token was instantiated with a KeyProvider, the 'kid' value will be taken from that provider and this one will be ignored.
*
* @param keyId the Key Id value.
* @return this same Builder instance.
*/
public Builder withKeyId(String keyId) {
this.headerClaims.put(PublicClaims.KEY_ID, keyId);
return this;
}
/**
* Add a specific Issuer ("iss") claim to the Payload.
*
* @param issuer the Issuer value.
* @return this same Builder instance.
*/
public Builder withIssuer(String issuer) {
addClaim(PublicClaims.ISSUER, issuer);
return this;
}
/**
* Add a specific Subject ("sub") claim to the Payload.
*
* @param subject the Subject value.
* @return this same Builder instance.
*/
public Builder withSubject(String subject) {
addClaim(PublicClaims.SUBJECT, subject);
return this;
}
/**
* Add a specific Audience ("aud") claim to the Payload.
*
* @param audience the Audience value.
* @return this same Builder instance.
*/
public Builder withAudience(String... audience) {
addClaim(PublicClaims.AUDIENCE, audience);
return this;
}
/**
* Add a specific Expires At ("exp") claim to the Payload.
*
* @param expiresAt the Expires At value.
* @return this same Builder instance.
*/
public Builder withExpiresAt(Date expiresAt) {
addClaim(PublicClaims.EXPIRES_AT, expiresAt);
return this;
}
/**
* Add a specific Not Before ("nbf") claim to the Payload.
*
* @param notBefore the Not Before value.
* @return this same Builder instance.
*/
public Builder withNotBefore(Date notBefore) {
addClaim(PublicClaims.NOT_BEFORE, notBefore);
return this;
}
/**
* Add a specific Issued At ("iat") claim to the Payload.
*
* @param issuedAt the Issued At value.
* @return this same Builder instance.
*/
public Builder withIssuedAt(Date issuedAt) {
addClaim(PublicClaims.ISSUED_AT, issuedAt);
return this;
}
/**
* Add a specific JWT Id ("jti") claim to the Payload.
*
* @param jwtId the Token Id value.
* @return this same Builder instance.
*/
public Builder withJWTId(String jwtId) {
addClaim(PublicClaims.JWT_ID, jwtId);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Boolean value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Integer value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Long value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Double value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, String value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Claim value.
*
* @param name the Claim's name.
* @param value the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withClaim(String name, Date value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
/**
* Add a custom Array Claim with the given items.
*
* @param name the Claim's name.
* @param items the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withArrayClaim(String name, String[] items) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, items);
return this;
}
/**
* Add a custom Array Claim with the given items.
*
* @param name the Claim's name.
* @param items the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null.
*/
public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, items);
return this;
}
/**
* Add a custom Array Claim with the given items.
*
* @param name the Claim's name.
* @param items the Claim's value.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null
*/
public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, items);
return this;
}
/**
* Add a custom Map Claim with the given items.
*
* Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types
* {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double},
* {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values.
* {@linkplain List}s can contain null elements.
*
* @param name the Claim's name.
* @param map the Claim's key-values.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null, or if the map contents does not validate.
*/
public Builder withClaim(String name, Map<String, ?> map) throws IllegalArgumentException {
assertNonNull(name);
// validate map contents
if (map != null && !validateClaim(map)) {
throw new IllegalArgumentException("Expected map containing Map, List, Boolean, Integer, Long, Double, String and Date");
}
addClaim(name, map);
return this;
}
/**
* Add a custom List Claim with the given items.
*
* Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types
* {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double},
* {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values.
* {@linkplain List}s can contain null elements.
*
* @param name the Claim's name.
* @param list the Claim's list of values.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null, or if the list contents does not validate.
*/
public Builder withClaim(String name, List<?> list) throws IllegalArgumentException {
assertNonNull(name);
// validate list contents
if (list != null && !validateClaim(list)) {
throw new IllegalArgumentException("Expected list containing Map, List, Boolean, Integer, Long, Double, String and Date");
}
addClaim(name, list);
return this;
}
private static boolean validateClaim(Map<?, ?> map) {
// do not accept null values in maps
for (Entry<?, ?> entry : map.entrySet()) {
Object value = entry.getValue();
if (value == null || !isSupportedType(value)) {
return false;
}
if (entry.getKey() == null || !(entry.getKey() instanceof String)) {
return false;
}
}
return true;
}
private static boolean validateClaim(List<?> list) {
// accept null values in list
for (Object object : list) {
if (object != null && !isSupportedType(object)) {
return false;
}
}
return true;
}
private static boolean isSupportedType(Object value) {
if (value instanceof List) {
return validateClaim((List<?>) value);
} else if (value instanceof Map) {
return validateClaim((Map<?, ?>) value);
} else {
return isBasicType(value);
}
}
private static boolean isBasicType(Object value) {
Class<?> c = value.getClass();
if (c.isArray()) {
return c == Integer[].class || c == Long[].class || c == String[].class;
}
return c == String.class || c == Integer.class || c == Long.class || c == Double.class || c == Date.class || c == Boolean.class;
}
/**
* Creates a new JWT and signs is with the given algorithm
*
* @param algorithm used to sign the JWT
* @return a new JWT token
* @throws IllegalArgumentException if the provided algorithm is null.
* @throws JWTCreationException if the claims could not be converted to a valid JSON or there was a problem with the signing key.
*/
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
}
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
if (!headerClaims.containsKey(PublicClaims.TYPE)) {
headerClaims.put(PublicClaims.TYPE, "JWT");
}
String signingKeyId = algorithm.getSigningKeyId();
if (signingKeyId != null) {
withKeyId(signingKeyId);
}
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
}
private void assertNonNull(String name) {
if (name == null) {
throw new IllegalArgumentException("The Custom Claim's name can't be null.");
}
}
private void addClaim(String name, Object value) {
if (value == null) {
payloadClaims.remove(name);
return;
}
payloadClaims.put(name, value);
}
}
(2)获取token信息的源码
package com.auth0.jwt.interfaces;
/**
* Class that represents a Json Web Token that was decoded from it's string representation.
*/
public interface DecodedJWT extends Payload, Header {
/**
* Getter for the String Token used to create this JWT instance.
*
* @return the String Token.
*/
String getToken();
/**
* Getter for the Header contained in the JWT as a Base64 encoded String.
* This represents the first part of the token.
*
* @return the Header of the JWT.
*/
String getHeader();
/**
* Getter for the Payload contained in the JWT as a Base64 encoded String.
* This represents the second part of the token.
*
* @return the Payload of the JWT.
*/
String getPayload();
/**
* Getter for the Signature contained in the JWT as a Base64 encoded String.
* This represents the third part of the token.
*
* @return the Signature of the JWT.
*/
String getSignature();
}