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


相关文章
|
2月前
|
JSON 安全 程序员
[JavaWeb]——JWT令牌技术,如何从获取JWT令牌
[JavaWeb]——JWT令牌技术,如何从获取JWT令牌
|
7天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
ava从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
|
2月前
|
JSON 前端开发 Java
|
2月前
|
JSON 安全 Java
JWT令牌技术
JSON Web Token (JWT) 是一种安全的、自包含的信息传输格式,常用于身份验证和信息交换。它由Header、Payload和Signature三部分组成,其中Signature用于验证消息完整性和发送者身份。JWT包含用户信息,服务器登录后发送给客户端,客户端使用JWT证明身份访问受保护资源。在Java项目中,可以使用`java-jwt`库进行JWT的生成和解析。要开始使用JWT,需在Maven或Gradle中添加相关依赖,并实现生成和解析JWT的方法。此外,文中还提供了一个简单的Java Web应用示例,展示如何在用户登录和访问受保护资源时使用JWT。
40 0
|
2月前
|
前端开发 Java Spring
SpringBoot通过拦截器和JWT令牌实现登录验证
该文介绍了JWT工具类、匿名访问注解、JWT验证拦截器的实现以及拦截器注册。使用`java-jwt`库生成和验证JWT,JwtUtil类包含generateToken和verifyToken方法。自定义注解`@AllowAnon`允许接口匿名访问。JwtInterceptor在Spring MVC中拦截请求,检查JWT令牌有效性。InterceptorConfig配置拦截器,注册并设定拦截与排除规则。UserController示例展示了注册、登录(允许匿名)和需要验证的用户详情接口。
180 1
|
3月前
|
开发框架 安全 Java
【Java专题_01】springboot+Shiro+Jwt整合方案
【Java专题_01】springboot+Shiro+Jwt整合方案
|
4月前
JWT令牌的使用
JWT令牌的使用
55 0
|
4月前
|
存储 JSON 算法
登录认证-登录校验-会话技术方案选择和对比(cookie、session和JWT令牌)
登录认证-登录校验-会话技术方案选择和对比(cookie、session和JWT令牌)
|
22天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
2月前
|
存储 JSON Java
spring boot3登录开发-1(整合jwt)
spring boot3登录开发-1(整合jwt)
59 1