您的位置:首页 >品牌 >

jwt 学习笔记

2023-03-25 21:43:11    来源:博客园
概述

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

JWT 的作用如下:


(资料图片仅供参考)

授权:一旦用户登录,每个后续请求将包括 JWT,从而允许用户访问该令牌允许的路由,服务和资源信息交换:JSON Web Token 是在各方之间安全地传输信息的好方法,因为可以对 JWT 进行签名,此外,由于签名是使用标头和有效负载计算的,因此还可以验证内容是否篡改传统的 Session 认证1. 认证方式

http 协议本身是一种无状态协议,这就意味着如果用户向我们的应用提供了用户名和密码进行认证,那么下次请求时还需再作一次认证。因为根据 http 协议,我们并不知道是哪个用户发出的请求,所以为了让应用能识别是哪个用户发出的请求,我们只能在服务存储一份用户登录的信息,这份登录信息会在响应传递给浏览器,告诉其保存为 cookie,以便下次请求时发送回来,这样我们就能识别请求来自哪个用户了

2. 缺点每个用户经过认证后,都要在服务端做一次记录,以方便下次鉴别。通常而言,session 都是保存在内存中的,随着认证用户的增多,服务端的开销也会明显增大用户认证之后,服务端做认证记录,如果认证的记录被保存到内存中,就意味着下次用户请求还得访问该服务器,才能拿到授权的资源,在分布式应用上,相应的限制了负载均衡器的能力,也就意味着限制了应用的扩展能力基于 cookie 进行用户识别,cookie 一旦被捕获,用户就会很容易受到跨站请求伪造的攻击在前后端分离时增加了部署的复杂性JWT 认证1. 认证方式前端通过 Web 表单将自己的用户名和密码发送给后端的接口,这一过程一般是 http post 请求,建议的方式是通过 SSL 加密的传输(https),从而避免敏感信息被嗅探后端核对用户名和密码成功后,将用户的 id 等其他信息作为 JWT Payload(负载),将其与头部分别进行 Base64 编码,拼接后签名,形成一个 JWT Token后端将 JWT 字符串作为登录成功的返回结果返回,前端可以将返回的结果保存在本地缓存上,退出登录时前端删除保存的 JWT 即可前端在每次请求后端带回 JWT后端检查是否存在,如验证 JWT 的有效性,检查签名是否正确,检查 Token 是否过期,检查 Token 接收方是否是自己验证通过后,后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果2. 优点简洁,可以通过 URL、POST 参数或者在 HTTP Header 发送,因为数据量小,传输速度快自包含,负载中包含了所有用户需要的信息,避免多次查询数据库Token 以加密形式保存在客户端,原则上任何 web 形式都支持不需要在服务端保存会话信息,特别适用于分布式微服务JWT 结构

通常 JWT 如下所示:xxxxx.yyyyy.zzzzz

标头(Header):标头一般由两部分组成:令牌的类型和所使用的签名算法,标头使用 Base64 编码

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

有效负载(payload):令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。同样使用 Base64 编码。不建议放入用户的敏感信息

{    "sub":"12345678",    "name":"john",    "admin":true,    ...}

签名(Signature):Signature 需要使用编码后的 Header 和 Payload 以及我们提供的一个密钥,然后使用 Header 中指定签名算法,进行签名。签名的作用是保证 JWT 没有被篡改过,如:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

客户端收到服务端发送的 token 后,再次请求服务端需要带上 token,此时 token 包含三部分:经过 Base64 编码的 Header、经过 Base64 编码的 Payload 和加密后的签名,服务端用自己保存的 secret 与客户端发送的 Header、Payload 运算,如果结果和客户端带回来的签名不一致,则验证失败

JWT 使用

引入依赖

    com.auth0    java-jwt    3.4.0

生成 token

Calendar instance = Calendar.getInstance();instance.add(Calendar.SECOND, 20);Map map = new HashMap<>();String token = JWT.create().withHeader(map)    // header    .withClaim("userId", 21)    // payload    .withClaim("username", "yeeq")    .withExpiresAt(instance.getTime())    // 指定令牌的过期时间    .sign(Algorithm.HMAC256("FAWF2#!F@"));  // 签名,并指定密钥

根据令牌和签名解析数据

// 创建验证对JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("FAWF2#!F@")).build();// 解码后的信息DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDAyNTQ4NzIsInVzZXJJZCI6MjEsInVzZXJuYW1lIjoieWVlcSJ9.jo_6gKThSXUcEfH1e9bu7at9lm2zmdupwiYvMUWopls");System.out.println(verify.getClaim("userId").asInt());System.out.println(verify.getClaim("username").asString());

常见异常信息:

SignatureVerificationException:签名不一致异常TokenExpiredException:令牌过期异常AlgorithmMismatchException:算法不匹配异常InvalidClaimException:失效的 payload 异常JWT 封装工具类

一般结合拦截器或者网关完成认证

public class JWTUtils {    private static final String SIGN = "FAWF2#!F@";    /**         * 生成 token         * @param map payload 的信息         * @return token         */    public static String getToken(Map map) {        Calendar instance = Calendar.getInstance();        instance.add(Calendar.DATE, 7);        JWTCreator.Builder builder = JWT.create();        // 创建 payload        map.forEach((k, v) -> {            builder.withClaim(k, v);        });        String token = builder.withExpiresAt(instance.getTime())    // 指定令牌的过期时间            .sign(Algorithm.HMAC256(SIGN));  // 签名,并指定密钥        return token;    }    /**         * 验证 token 的合法性         * @param token token         */    public static void verifyJWT(String token) {        JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);    }    /**         * 获取 token 的信息         * @return token 的信息         */    public static DecodedJWT getTokenInfo(String token) {        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);        return verify;    }}

关键词:

相关阅读

精彩放送