用户登录的详细流程(二)JWT生成token登录

JWT生成token登录

1.jwt的构成
(1)header
(2)payload
(3)signature
2.token的登陆原理
3.在实际中如何应用token
(1)设置token的生成代码
(2)如何从token中获取有用的信息
(3)验证token
(4)实践得到的结果
4.附录
(1)创建token所用的源码
(2)获取token信息的源码

1.JWT的构成

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

2.token登录的原理

首先用户输入账号和密码进行登录,后端根据数据库进行验证用户,验证通过生成token。然后前端将后端返回的token保存起来,每次用户访问请求都带着token,在后端经过拦截器的拦截,然后再进行判断,如果token验证通过放行,否则不能让用户访问界面。

3.在实际中如何应用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)实践得到的结果
代码
用户登录的详细流程(二)JWT生成token登录_第1张图片
这上面只是测试,可以看到详细的结果。和上面的实际代码有些冲突。

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();
}

你可能感兴趣的:(java,jwt,spring,shiro)