关于jwt的思考

关于jwt的思考

jwt是个做用户权限认证的方案,科普的内容参考相关文档吧,这里提出几个关于jwt的思考:

现有项目架构:

pc项目->pc服务器->api服务器
app项目->api服务器

1. jwt是否需要服务器存储用户状态

按照jwt的思路,服务端是不需要存储用户状态的,只要有秘钥+过期时间就可以实现用户的认证和过期,至于读库vs加解密验证哪个过程对服务器的压力更大,这个可能需要对比测试,但从原理和常识来讲,存库读库的成本应该更高一点。无库产生的加解密开销相对于读写库应该是非常小的,而且无库可以轻松做到跨服务器的认证。

2. jwt方案中token的安全问题

分为两个安全问题:

  • token泄露,泄露一般存在两个地方,1是传输层,2是客户端。先考虑传输层:http被窃听,或者https被破解(https的安全性这里不做讨论),那么如果token在传输层被拦截,传统的cookie存储的sessionId也有一样的问题。再说客户端,一样的情况,如果cookie可以被窃取,token也没办法解决这个问题。已经做过的测试是:拦截到cookie后,把cookie放在另一台电脑上也是可以使用的,这个跟token是一样的。

  • 还有一种情况是重放攻击,http请求被拦截重放,这个安全性对于cookie和jwt是一样的,避免这个问题需要配合其他手段,但基本很难避免。

综上,jwt碰到的安全问题跟传统方式一样。

3. jwt如何解决续签问题

传统的cookie的续签方案一般都是框架自带的,session有效期30分钟,30分钟内如果有访问,session有效期被延长30分钟。那么jwt是怎么实现的呢,细节没看完,不过基本的逻辑是频繁重签名(不知道为什么有人这么干,开销应该不小,而且问题挺多),或者是有效期30分钟,服务器发现token要过期了,提前三分钟重签并返回新的token,客户端每次请求都检测新旧token,如果不一致更新本地token(这个可以放在http的请求预处理里,复杂度和开销都不大)。关于新token的发放导致旧token有效期内的访问失效的问题参见这里的token新旧更替问题讨论 。这种方案就可以实现浏览器里传统cookie的续签需求,以及app7天内登陆就可以持续刷新登陆状态的问题(app比较特殊,每天第一次访问时刷新一下token)。

4. jwt如何解决注销问题

好了,jwt最大的问题来了,怎么注销?先看传统的session是怎么注销的:用户点击退出,调用后台退出接口,也就是session注销接口,依托于后台的状态更新。那么服务端做的呢,如果是用文件或者数据库存储的,那么通过删记录是可以后台强制踢出的,放在内存里就不要想了(如果用户量大了放内存的话服务器也吃不消,放文件里io太频繁基本也不靠谱,还好有了Redis)。jwt最大的问题就在于后台没有存储用户状态,用户退出的话只是客户端删掉了token,然而此token在有效期内还是有效的,也就是说如果token泄露的话就麻烦了,不过token泄露的问题已经在上面讲过了,和cookie是同一个问题。那么最麻烦的就是怎么让一个token在用户注销后失效,以及后台强制退出,这个jwt是没办法的,因为jwt的无状态和用户状态维护是个矛盾冲突的话题。如果要解决就要建立一个黑名单,也就是把用户注销后的token放到redis里,然后每次校验黑名单(这里还是用到了数据库),

5. 使用jwt+Redis黑名单的方案

接上个问题,jwt推崇的无状态和跨服务器认证在数据库的出现后,貌似没有任何优势,真的没有优势吗?也不全是,至少有两个小优势,第一,存储黑名单(也就是注销后的用户或者黑户)比起存储登录之后的用户这个量级都是相当小的,存储量下降&读取速度提高。第二个优势是app和pc能共用同一套认证机制,也就意味着前后端彻底的分离,不必再为cookie和session认证单独搭一个服务器。

6. 用了jwt之后html服务怎么提供呢

对于前后端分离的项目来说,pc服务器的两个任务1是做认证(jwt已经解决),2是提供html服务。html服务可以交给nginx或者放到cdn,无论哪个方案都比tomcat之类的静态资源处理能力更强大,也就是说html和js等静态资源全部交给静态资源服务器来处理。那么安全问题又来了,怎么对文件做权限控制呢?对于一个前后端分离的项目来说,html一般不会嵌套后端的代码,所以html没有所谓的安全问题,换句话说html里根本没有任何有价值的内容。对于js来说,里面包含了业务逻辑,实际上对js做限制也是没有任何意义的,因为前端的代码是暴露给客户端的,即便控制,黑客登录之后也是能看到你的代码的,惟一能做的就是压缩混淆,把压缩后的代码放到线上,源码不放到线上,最大程度增加通过阅读js读取业务逻辑的难度(实际意义也不大,尤其对于有些公司压根连压缩这一步都不做的,更没有这个问题的顾虑了)。那么也就是说前后端分离的项目里html(没有后端代码嵌入)和js是没有安全的考虑必要的,真正要考虑的大概也就是图片的防盗链和pdf等静态资源的处理吧,这个因为对于后端不是很熟悉所以不做深入讨论,对于pdf等资源能想到的也就是通过需要授权的接口去拿pdf的路径吧。

7. 到底要不要数据库

如果不考虑用户注销后一定要token失效和强制退出需求的话,是不需要数据库的,即便加上redis对于传统方案来说也是有上文提到的两个优势。而且其实注销后一定要token失效主要也是考虑token泄露的问题(见上文),所以如果有踢出用户登录需求的话,还是要有数据库做黑名单存储的,一旦用户被拉入黑名单,就要立马停掉用户的签名发放,所以这个问题并不单单是认证的问题,还有签名发放的问题。

8. 用户认证的终极解决方案(跨服务器)

前提就是跨服务,实际上jwt本身已经实现了跨服务(无状态),既然服务端不存储用户状态,只要多个服务器之间使用共同的秘钥和加密方案就可以实现x.abc.com里登录之后在y.abc.com那台服务器做登录认证。但是问题上面也说了,如果不考虑注销和强制退出的情况的话,这个话题也就到处为止了。但是如果完美主义呢?当然还是要实现这两个功能,也就是要有黑名单服务器,或者说认证服务器auth.abc.com,无论x.abc.com还是y.abc.com里登录或者注销都要先通过auth.abc.com,但是带来的读写不一致和额外的认证开销问题大概也是一个历史性难题吧。或许也可以参考Oauth2.0的方案,做隐式认证。不存储用户的状态的话,jwt跨服务器应该是个比较简单的解决方案吧,但如果一个公司有跨服务器需求的话,那么规模之大,里面的后端童鞋应该有更为专业的方案吧,欢迎分享。此段不专业莫要踩我。


结论:

上文阐述了作为一个卑微的前端能想到的jwt的问题和思考,总结来说,使用jwt可以实现真正的前后端分离,浏览器可以和app用一样的认证机制,无论对服务器的搭建还是服务器开销都是不错的优化,至于黑名单的设置,这个看需求吧。


彩蛋

为什么会有彩蛋呢,因为结论里不能说太多废话,就搁这里说了,jwt主要解决了浏览器和app共用一个服务器的问题,也就是少了一个pc服务器。
有看客就要问了,那你怎么看node呢,ssr?pwa?字节码?
这个当然要且看下回分晓了️️��

<think>好的,我现在要解决用户关于JWT工具类的实现和使用的问题。首先,我需要回忆JWT的基本结构,它由Header、Payload和Signature三部分组成,引用[2]提到过这个结构。用户需要工具类的示例代码,所以我得考虑如何用Java实现生成和解析JWT的功能。 首先,工具类需要依赖JWT库,比如jjwt。在示例中,用户可能使用了io.jsonwebtoken的库,所以我要确保代码中的导入正确。工具类通常会包含生成JWT和解析JWT的方法。生成JWT时需要设置签名算法,比如HS256,以及密钥。密钥需要保密,所以可能需要从配置文件中读取,但示例里可能直接硬编码了,如引用[2]中的工具类。 接下来,生成JWT的方法需要接收claims和有效时间。根据引用[1],测试代码里有效时间设置的是3秒,所以工具类中的createJwt方法应该接收一个long类型的有效期参数,并将其加到当前时间上。代码中可能需要使用setExpiration和setIssuedAt来设置过期和签发时间。 解析JWT时,需要验证签名,并处理可能的异常,比如过期或签名无效。引用[3]提到解析成功后可以在controller中使用载荷内容,所以解析方法应返回Claims对象。 然后,用户的使用示例部分需要展示如何生成和解析JWT。测试代码部分引用了[1]中的测试方法,生成JWT后打印,然后解析并输出签发和过期时间。这部分需要转换成示例代码,可能需要处理异常,比如用try-catch块捕获JwtException。 还要注意代码格式,比如使用正确的语法,变量命名等。确保代码中的注释和输出语句清晰,用户能理解每个步骤的作用。同时,要引用相关的引用来源,如引用[1]、[2]、[3]来支持代码的正确性。 最后,生成相关问题。根据用户的问题和提供的引用,可能的问题包括JWT结构、密钥管理、异常处理、刷新机制等。这些问题需要与工具类的实现和使用相关,帮助用户进一步学习。 现在需要把这些思考整合成一个结构清晰的回答,分步骤说明工具类的实现和使用,并附上示例代码,确保符合用户的要求,如使用正确的LaTeX格式和引用标识。</think>### JWT工具类实现与使用示例 #### 一、JWT工具类核心实现 JWT工具类需包含生成、解析和验证令牌的功能,依赖`jjwt`库实现。以下为简化版代码: ```java import io.jsonwebtoken.*; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Date; import java.util.Map; public class JwtUtils { private static final String SECRET = "your-256-bit-secret-key-encoded-in-base64"; // 密钥需保密存储[^2] private static final SecretKey KEY = new SecretKeySpec(SECRET.getBytes(), "HmacSHA256"); // 生成JWT public static String createJwt(Map<String, Object> claims, long ttlMillis) { long now = System.currentTimeMillis(); return Jwts.builder() .setClaims(claims) .setIssuedAt(new Date(now)) .setExpiration(new Date(now + ttlMillis)) // 设置有效期[^1] .signWith(KEY, SignatureAlgorithm.HS256) .compact(); } // 解析JWT public static Claims parseJwt(String jwt) throws JwtException { return Jwts.parserBuilder() .setSigningKey(KEY) .build() .parseClaimsJws(jwt) .getBody(); // 获取载荷供业务层使用[^3] } } ``` #### 二、使用示例 ```java public class JwtDemo { public static void main(String[] args) { // 1. 生成JWT Map<String, Object> claims = new HashMap<>(); claims.put("username", "zss"); String jwt = JwtUtils.createJwt(claims, 3000L); // 有效时间3秒 System.out.println("生成JWT: " + jwt); // 2. 解析并验证JWT try { Claims parsed = JwtUtils.parseJwt(jwt); System.out.println("用户名: " + parsed.get("username")); System.out.println("过期时间: " + parsed.getExpiration()); } catch (ExpiredJwtException e) { System.err.println("令牌已过期"); } catch (JwtException e) { System.err.println("令牌验证失败"); } } } ``` #### 三、关键实现说明 1. **密钥管理** 使用`HmacSHA256`算法需256位密钥,建议通过`Base64`编码生成,生产环境应从安全配置读取 2. **时效控制** 通过`setExpiration()`设置绝对过期时间,时间单位需与系统时钟一致 3. **异常处理** 需捕获`ExpiredJwtException`(过期)和`MalformedJwtException`(格式错误)等异常 4. **载荷扩展** 可通过`claims.put("role", "admin")`添加业务字段,但需避免存储敏感数据
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值