zoukankan      html  css  js  c++  java
  • JWT了解一下

    简介

    JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于将信息作为JSON对象在各方之间安全地传输。可以对该信息进行验证和信任,因为它是数字签名的。JWT可以使用密钥(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

    JWT优点
    1. 简洁(Compact):可以通过URL、POST参数或在HTTP标头内发送。由于它的大小,它的传输速度也很快。

    2. 自包含(Self-contained):有效负载包含有关用户的所有必需信息,可以避免多次查询数据库。

    3. JWT是跨语言的。

    4. 不需要在服务端保存会话信息,特别适用于分布式微服务。

    JWT使用场景

    1.授权

    这是JWT的典型使用场景,一旦用户登录,每个后续请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用JWT的一个特性,因为它的开销很小,并且能够在不同域的系统之间轻松使用。

    具体流程如下图:

    a.用户通过登录将用户名和密码发送给服务端。

    b.服务端验证用户名和密码,验证通过则生成JWT,将生成的JWT返回给客户端。

    c.客户端收到JWT后,将它保存在本地,当退出登录时再清除保存的JWT。

    d.当客户端访问服务端受保护的资源时,需要带上JWT,一般将JWT放入HTTP Header的Authorization标头中(使用Bearer模式)。

    e.服务端接收到请求时,首先会验证JWT的有效性,验证通过则正常执行对应逻辑返回对应资源,验证失败则拒绝访问对应资源。

    2.信息交换

    JWT是在各方之间安全传输信息的好方法,因为JWT可以被签名(例如:使用公钥/私钥对),您可以确定发送者就是他们所说的那个人。此外,由于使用标头和有效负载计算签名,因此还可以验证内容是否被篡改。

    JWT结构

    JWT由用点(.)分隔的以下三个部分组成:

    1. 标头(Header)

    2. 有效载荷(Payload)

    3. 签名(Signature)

    因此,JWT通常如下所示:

    Header.Payload.Signature
    
    标头(Header)

    Header通常由两部分组成:令牌的类型,即JWT,以及签名算法,如HMAC SHA256或RSA。如下所示:

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    

    这个JSON将被Base64编码,组成JWT的第一部分。

    有效载荷(Payload)

    Payload包含有关实体和其他数据的声明。如下所示:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
    

    同样这个JSON将被base64编码,组成JWT的第二部分。

    关于Payload应该注意两点,由于只是使用Base64编码并没有加密,所以不应该存储敏感数据。其次是应该设置到期时间。

    签名(Signature)

    Signature需要使用编码后的header和Payload以及我们提供的一个密钥,然后使用header中指定的签名算法进行签名,该签名字符串将作为JWT中的第三部分。如下所示:

    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)
    

    Java中使用JWT

    maven依赖:

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.16.0</version>
    </dependency>
    

    JwtUtils:

    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTCreator;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import java.util.Calendar;
    import java.util.Map;
    
    public class JwtUtils {
        //加密密钥
        private static final String SECRET = "5b6u5L+h5YWs5LyX5Y+377ya5oiR55yf55qE5LiN5LyaSmF2YeWVig==";
    
        //Token过期时间
        private static final int EXP_TIME_MINUTE = 30;
    
        /**
         * 创建Token
         * @param payloadDataMap
         * @return
         */
        public static String createToken(Map<String, String> payloadDataMap) {
            JWTCreator.Builder builder = JWT.create();
    
            //设置自定义的数据
            payloadDataMap.forEach((key, value) -> builder.withClaim(key, value));
    
            //设置token有效期 60s
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.MINUTE, EXP_TIME_MINUTE);
            builder.withExpiresAt(calendar.getTime());
    
            //生成Token
            return builder.sign(Algorithm.HMAC256(SECRET));
        }
    
        /**
         * 验证Token
         * @param token
         * @return
         */
        public static DecodedJWT verifyToken(String token) {
            return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        }
    }
    
    
    创建Token:
    @Test
    public void testCreateToken(){
    	Map<String, String> dataMap = new HashMap<>();
    	dataMap.put("id", "7");
    	dataMap.put("username", "buhe");
    	dataMap.put("wechat", "tryjava");
    	System.out.println(JwtUtils.createToken(dataMap));
    }
    

    输出:

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3ZWNoYXQiOiJ0cnlqYXZhIiwiaWQiOiI3IiwiZXhwIjoxNjI0ODY2MTE2LCJ1c2VybmFtZSI6ImJ1aGUifQ._hELqy_DpBL4IlE5N-ZtUNFia72MOxHK220ISuzSfKE
    
    验证Token,并输出Payload中的数据:
    @Test
    public void testVerifyToken(){
    	String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3ZWNoYXQiOiJ0cnlqYXZhIiwiaWQiOiI3IiwiZXhwIjoxNjI0ODY2MTE2LCJ1c2VybmFtZSI6ImJ1aGUifQ._hELqy_DpBL4IlE5N-ZtUNFia72MOxHK220ISuzSfKE";
    	DecodedJWT decodedJWT = JwtUtils.verifyToken(token);
    	decodedJWT.getClaims().forEach((key, value) -> System.out.println(key + " : " + value));
    }
    

    输出:

    wechat : "tryjava"
    id : "7"
    exp : 1624866116
    username : "buhe"
    
    验证失败的情况

    token验证成功时会返回DecodedJWT对象,从DecodedJWT中,我们能获取到Token相关的一些信息,token验证失败时则会抛出对应异常。

    token验证异常分类如下图:

  • 相关阅读:
    Java API学习
    Java接口类学习笔记
    TCP/IP协议族
    Web服务器
    Redis笔记
    Python知识总汇
    GIL锁
    day8
    day7
    day6
  • 原文地址:https://www.cnblogs.com/seve/p/14945263.html
Copyright © 2011-2022 走看看