JWT解密:探秘令牌魔法与Java的完美交互

简介: JWT解密:探秘令牌魔法与Java的完美交互

JWT 简介

JWT 简称 JSON Web Token,也就是通过 JSON 形式作为 Web 应用中的令牌,用于各方之间安全地将信息作为 JSON 对象传输,在数据传输的过程中还可以完成数据加密、签名等相关处理。

注意:JWT 的三个部分的 Header 和 Payload都是明文存储!只不过内容通过 Base64 转码了!所以不要将重要信息存储在 JWT 中!

认证流程

  1. 首先,前端通过 Web 表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个 HTTP POST 请求。建议的方式是通过 SSL 加密的传输(HTTPS),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的 ID 等其他信息作为 JWT Payload(负载),将其与头部分别进行 Base64 编码拼接后签名,形成一个 Token。形成的 Token 就是一个形同 head.payload.singurater 的字符串。
  3. 后端将 Token 字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在 localStorage 或 sessionStorage 上,退出登录时前端删除保存的 Token 即可。
  4. 前端在每次请求时将 Token 放入 HTTP Header 中的 Authorization 位。
  5. 后端检查是否存在,如存在验证 Token 的有效性。例如,检查签名是否正确;检查 Token 是否过期;检查 Token 的接收方是否是自己(可选)。
  6. 证通过后后端使用 Token 中包含的用户信息进行其他逻辑操作,返回相应结果。

JWT 结构

JWT 的组成有三部分:标头(Header)、有效载荷(Payload)、签名(Signature)。

Header(Base64Url).Payload(Base64Url).Secret(Header(Base64Url)+ Payload(Base64Url))

标头 Header

标头通常由两部分组成:令牌的类型和所使用的签名算法,例如 HMAC SHA256(默认)或 RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。

注意:Base64 是一种编码,也就是说,它是可以被翻译回原来的样子的,它并不是一种加密过程。

有效载荷 Payload

将能用到的用户信息放在 Payload 中。官方建议不要放特别敏感的信息,例如密码。

签名 Signature

签证由三部分组成,header 和 payload 分别经 Base64Url(一种在 Base64 上做了一点改变的编码)编码后由 . 连接,服务器生成秘钥(secret),连接之后的字符串在经 Header 中声明的加密方式和秘钥加密,再用 . 和加密前的字符串连接。服务器验证 Token 时只会验证第三部分。

使用JWT

导入依赖

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

创建工具类

package world.xuewei;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
public class JwtUtils {
    /**
     * 密钥
     */
    private static final String SECRET = "xuewei";
    /**
     * 获取Token,默认过期时间为1天
     */
    public static String getToken(Map<String, String> map) {
        return getToken(map, Calendar.DATE, 1);
    }
    /**
     * 获取Token,指定过期时间
     */
    public static String getToken(Map<String, String> map, Integer calenderType, Integer count) {
        // 创建令牌的过期时间
        Calendar instance = Calendar.getInstance();
        instance.add(calenderType, count);
        // 创建令牌建造者,封装请求参数
        JWTCreator.Builder builder = JWT.create();
        map.forEach(builder::withClaim);
        // 设置过期时间
        builder.withExpiresAt(instance.getTime());
        // 设置加密算法和密钥
        return builder.sign(Algorithm.HMAC256(SECRET));
    }
    /**
     * 验证Token的合法性
     *
     * @throws TokenExpiredException          Token过期
     * @throws SignatureVerificationException 密钥不匹配
     * @throws AlgorithmMismatchException     算法不匹配
     * @throws JWTDecodeException             Token异常
     */
    public static DecodedJWT validate(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
    }
    /**
     * 获取Token中包含的信息
     *
     * @throws TokenExpiredException          Token过期
     * @throws SignatureVerificationException 密钥不匹配
     * @throws AlgorithmMismatchException     算法不匹配
     * @throws JWTDecodeException             Token异常
     */
    public static Map<String, String> getPayloadMap(String token) {
        DecodedJWT verify = validate(token);
        Map<String, Claim> claims = verify.getClaims();
        // 转换为标准的Map
        Map<String, String> result = new HashMap<>();
        claims.forEach((k, v) -> {
            result.put(k, v.asString());
        });
        return result;
    }
}

测试代码

package world.xuewei;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.junit.Test;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
public class JwtTest {
    /**
     * 创建 Token
     */
    @Test
    public void testCreateToken() {
        // Payload Map
        Map<String, String> payload = new HashMap<>();
        payload.put("userId", "1001");
        payload.put("roleCode", "admin");
        // 创建默认时效 Token 1天
        String defaultToken = JwtUtils.getToken(payload);
        System.out.println("defaultToken = " + defaultToken);
        // 创建自定义时效 Token 1 分钟
        String customToken = JwtUtils.getToken(payload, Calendar.MINUTE, 1);
        System.out.println("customToken = " + customToken);
    }
    /**
     * 验证 Token
     */
    @Test
    public void testValidateToken() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlQ29kZSI6ImFkbWluIiwiZXhwIjoxNjkyMTczMDk4LCJ1c2VySWQiOiIxMDAxIn0.P7Qc0agYIkuB3XuLs76tuxb9Z10a75srffwATzM45Nw";
        try {
            JwtUtils.validate(token);
        } catch (TokenExpiredException e) {
            throw new RuntimeException("Token 已过期");
        } catch (SignatureVerificationException e) {
            throw new RuntimeException("Token 密钥不匹配");
        } catch (AlgorithmMismatchException e) {
            throw new RuntimeException("Token 算法不匹配");
        } catch (JWTDecodeException e) {
            throw new RuntimeException("Token 异常");
        }
        System.out.println("Token 验证通过");
    }
    /**
     * 解析 Token
     */
    @Test
    public void testParseToken() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlQ29kZSI6ImFkbWluIiwiZXhwIjoxNjkyMTczMDk4LCJ1c2VySWQiOiIxMDAxIn0.P7Qc0agYIkuB3XuLs76tuxb9Z10a75srffwATzM45Nw";
        // 此方法中存在 Token 有效性验证的逻辑,故也需要异常捕获处理
        Map<String, String> payloadMap;
        try {
            payloadMap = JwtUtils.getPayloadMap(token);
        } catch (TokenExpiredException e) {
            throw new RuntimeException("Token 已过期");
        } catch (SignatureVerificationException e) {
            throw new RuntimeException("Token 密钥不匹配");
        } catch (AlgorithmMismatchException e) {
            throw new RuntimeException("Token 算法不匹配");
        } catch (JWTDecodeException e) {
            throw new RuntimeException("Token 异常");
        }
        System.out.println("payloadMap = " + payloadMap);
    }
}


相关文章
|
3月前
|
存储 JSON 安全
JWT令牌详解
JWT令牌详解
|
11天前
|
前端开发 JavaScript Java
Ajax进行异步交互:提升Java Web应用的用户体验
Ajax 技术允许在不重载整个页面的情况下与服务器异步交换数据,通过局部更新页面内容,极大提升了 Java Web 应用的响应速度和用户体验。本文介绍 Ajax 的基本原理及其实现方式,包括使用 XMLHttpRequest 对象发送请求、处理响应数据,并在 Java Web 应用中集成 Ajax。此外,还探讨了 Ajax 如何通过减少页面刷新、实时数据更新等功能改善用户体验。
27 3
|
7天前
|
JSON Java API
【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?
【Azure Developer】如何验证 Azure AD的JWT Token (JSON Web 令牌)?
|
18天前
|
NoSQL 安全 Java
Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权
Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权
|
2月前
|
缓存 Java 编译器
Java演进问题之JVMCI JIT编译器与JVM的交互如何解决
Java演进问题之JVMCI JIT编译器与JVM的交互如何解决
|
2月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
57 1
|
2月前
|
算法 Java 调度
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
|
2月前
|
监控 算法 Java
详解 Java 限流接口实现问题之避免令牌桶限流算法可能导致的过载问题如何解决
详解 Java 限流接口实现问题之避免令牌桶限流算法可能导致的过载问题如何解决
|
2月前
|
存储 设计模式 监控
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
33 0
JWT令牌,JWT令牌的后续使用,在其他端口中使用的注意事项?如果你编写了JWT令牌的话,在下一次请求当中,都需要添加的,如果你已经配置好了WebConfig和Inter 就不用配了,添加了拦截器之后
JWT令牌,JWT令牌的后续使用,在其他端口中使用的注意事项?如果你编写了JWT令牌的话,在下一次请求当中,都需要添加的,如果你已经配置好了WebConfig和Inter 就不用配了,添加了拦截器之后
下一篇
云函数