首页 » Web前端 » phpjwt技巧_不懂就学什么是JWT

phpjwt技巧_不懂就学什么是JWT

访客 2024-10-26 0

扫一扫用手机浏览

文章目录 [+]

session认证的缺陷实在很明显,由于session是保存在做事器里,以是如果分布式支配运用的话,会涌现session不能共享的问题,很难扩展。
于是乎为理解决session共享的问题,又引入了redis,接着往下看。

token认证

这种办法跟session的办法流程差不多,不同的地方在于保存的是一个token值到redis,token一样平常是一串随机的字符(比如UUID),value一样平常是用户ID,并且设置一个过期韶光。
每次要求做事的时候带上token在要求头,后端吸收到token则根据token查一下redis是否存在,如果存在则表示用户已认证,如果token不存在则跳到登录界面让用户重新登录,登录成功后返回一个token值给客户端。

phpjwt技巧_不懂就学什么是JWT

优点是多台做事器都是利用redis来存取token,不存在不共享的问题,以是随意马虎扩展。
缺陷是每次要求都须要查一下redis,会造成redis的压力,还有增加了要求的耗时,每个已登录的用户都要保存一个token在redis,也会花费redis的存储空间。

phpjwt技巧_不懂就学什么是JWT
(图片来自网络侵删)

有没有更好的办法呢?接着往下看。

什么是JWT

JWT(全称:Json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的办法,用于作为JSON工具在各方之间安全地传输信息。
该信息可以被验证和信赖,由于它是数字署名的。

上面说法比较文绉绉,大略点说便是一种认证机制,让后台知道该要求是来自于受信的客户端。

首先我们先看一个流程图:

流程描述一下:

用户利用账号、密码登录运用,登录的要求发送到Authentication Server。
Authentication Server进行用户验证,然后创建JWT字符串返回给客户端。
客户端要求接口时,在要求头带上JWT。
Application Server验证JWT合法性,如果合法则连续调用运用接口返回结果。

可以看出与token办法有一些不同的地方,便是不须要依赖redis,用户信息存储在客户端。
以是关键在于天生JWT,和解析JWT这两个地方。

JWT的数据构造

JWT一样平常是这样一个字符串,分为三个部分,以"."隔开:

xxxxx.yyyyy.zzzzz

Header

JWT第一部分是头部分,它是一个描述JWT元数据的Json工具,常日如下所示。

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

alg属性表示署名利用的算法,默认为HMAC SHA256(写为HS256),typ属性表示令牌的类型,JWT令牌统一写为JWT。

末了,利用Base64 URL算法将上述JSON工具转换为字符串保存。

Payload

JWT第二部分是Payload,也是一个Json工具,除了包含须要通报的数据,还有七个默认的字段供选择。

分别是,iss:发行人、exp:到期韶光、sub:主题、aud:用户、nbf:在此之前不可用、iat:发布韶光、jti:JWT ID用于标识该JWT。

如果自定义字段,可以这样定义:

{ //默认字段 "sub":"主题123", //自定义字段 "name":"java技能爱好者", "isAdmin":"true", "loginTime":"2021-12-05 12:00:03"}

须要把稳的是,默认情形下JWT是未加密的,任何人都可以解读其内容,因此如果一些敏感信息不要存放在此,以防信息透露。

JSON工具也利用Base64 URL算法转换为字符串保存。

Signature

JWT第三部分是署名。
是这样天生的,首先须要指定一个secret,该secret仅仅保存在做事器中,担保不能让其他用户知道。
然后利用Header指定的算法对Header和Payload进行打算,然后就得出一个署名哈希。
也便是Signature。

那么Application Server如何进行验证呢?可以利用JWT前两段,用同一套哈希算法和同一个secret打算一个署名值,然后把打算出来的署名值和收到的JWT第三段比较,如果相同则认证通过。

JWT的优点json格式的通用性,以是JWT可以跨措辞支持,比如Java、JavaScript、PHP、Node等等。
可以利用Payload存储一些非敏感的信息。
便于传输,JWT构造大略,字节占用小。
不须要在做事端保存会话信息,易于运用的扩展。
怎么利用JWT

首先引入Maven依赖。

<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version></dependency>

创建工具类,用于创建jwt字符串和解析jwt。

@Componentpublic class JwtUtil { @Value("${jwt.secretKey}") private String secretKey; public String createJWT(String id, String subject, long ttlMillis, Map<String, Object> map) throws Exception { JwtBuilder builder = Jwts.builder() .setSubject(null) // 发行者 .setId(id) .setSubject(subject) .setIssuedAt(new Date()) // 发行韶光 .signWith(SignatureAlgorithm.HS256, secretKey) // 署名类型 与 密钥 .compressWith(CompressionCodecs.DEFLATE);// 对载荷进行压缩 if (!CollectionUtils.isEmpty(map)) { builder.setClaims(map); } if (ttlMillis > 0) { builder.setExpiration(new Date(System.currentTimeMillis() + ttlMillis)); } return builder.compact(); } public Claims parseJWT(String jwtString) { return Jwts.parser().setSigningKey(secretKey) .parseClaimsJws(jwtString) .getBody(); }}

接着在application.yml配置文件配置jwt.secretKey。

## 用户天生jwt字符串的secretKeyjwt: secretKey: ak47

接着创建一个相应体。

public class BaseResponse { private String code; private String msg; public static BaseResponse success() { return new BaseResponse("0", "成功"); } public static BaseResponse fail() { return new BaseResponse("1", "失落败"); } //布局器、getter、setter方法}public class JwtResponse extends BaseResponse { private String jwtData; public static JwtResponse success(String jwtData) { BaseResponse success = BaseResponse.success(); return new JwtResponse(success.getCode(), success.getMsg(), jwtData); } public static JwtResponse fail(String jwtData) { BaseResponse fail = BaseResponse.fail(); return new JwtResponse(fail.getCode(), fail.getMsg(), jwtData); } //布局器、getter、setter方法}

接着创建一个UserController:

@RestController@RequestMapping("/user")public class UserController { @Resource private UserService userService; @RequestMapping(value = "/login", method = RequestMethod.POST) public JwtResponse login(@RequestParam(name = "userName") String userName, @RequestParam(name = "passWord") String passWord){ String jwt = ""; try { jwt = userService.login(userName, passWord); return JwtResponse.success(jwt); } catch (Exception e) { e.printStackTrace(); return JwtResponse.fail(jwt); } }}

还有UserService:

@Servicepublic class UserServiceImpl implements UserService { @Resource private JwtUtil jwtUtil; @Resource private UserMapper userMapper; @Override public String login(String userName, String passWord) throws Exception { //登录验证 User user = userMapper.findByUserNameAndPassword(userName, passWord); if (user == null) { return null; } //如果能查出,则表示账号密码精确,天生jwt返回 String uuid = UUID.randomUUID().toString().replace("-", ""); HashMap<String, Object> map = new HashMap<>(); map.put("name", user.getName()); map.put("age", user.getAge()); return jwtUtil.createJWT(uuid, "login subject", 0L, map); }}

还有UserMapper.xml:

@Mapperpublic interface UserMapper { User findByUserNameAndPassword(@Param("userName") String userName, @Param("passWord") String passWord);}

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="io.github.yehongzhi.jwtdemo.mapper.UserMapper"> <select id="findByUserNameAndPassword" resultType="io.github.yehongzhi.jwtdemo.model.User"> select from user where user_name = #{userName} and pass_word = #{passWord} </select></mapper>

user表构造如下:

启动项目,然后用POSTMAN要求login接口。

返回的jwt字符串如下:

eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqVspLzE1VslJ6OnHFsxnzX67coKSjlJgOFDEzqAUAAAD__w.qib2DrjRKcFnY77Cuh_b1zSzXfISOpCA-g8PlAZCWoU

接着我们写一个接口吸收这个jwt,并做验证。

@RestController@RequestMapping("/jwt")public class TestController { @Resource private JwtUtil jwtUtil; @RequestMapping("/test") public Map<String, Object> test(@RequestParam("jwt") String jwt) { //这个步骤可以利用自定义表明+AOP编程做解析jwt的逻辑,这里为了简便就直接写在controller里 Claims claims = jwtUtil.parseJWT(jwt); String name = claims.get("name", String.class); String age = claims.get("age", String.class); HashMap<String, Object> map = new HashMap<>(); map.put("name", name); map.put("age", age); map.put("code", "0"); map.put("msg", "要求成功"); return map; }}

像这样能正常解析成功的话,就表示该用户登录未过期,并且已认证成功,以是可以正常调用做事。
那么有人会问了,这个jwt字符串能不能被假造呢?

除非你知道secretKey,否则是不能假造的。
比如客户端随便猜一个secretKey的值,然后假造一个jwt:

eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqVspLzE1VslJ6OnHFsxnzX67coKSjlJgOFDEzqAUAAAD__w.bHr9p3-t2qR4R50vifRVyaYYImm2viZqiTlDdZHmF5Y

然后传进去解析,会报以下缺点:

还记得事理吧,是根据前面两部分(Header、Payload)加上secretKey利用Header指定的哈希算法打算出第三部分(Signature),以是可以看出最关键便是secretKey。
secretKey只有做事端自己知道,以是客户端不知道secretKey的值是假造不了jwt字符串的。

总结

末了讲讲JWT的缺陷,任何技能都不是完美的,以是我们得用辩证思维去看待任何一项技能。

安全性没法担保,以是jwt里不能存储敏感数据。
由于jwt的payload并没有加密,只是用Base64编码而已。
无法中途废弃。
由于一旦签发了一个jwt,在到期之前始终都是有效的,如果用户信息发生更新了,只能等旧的jwt过期后重新签发新的jwt。
续签问题。
当签发的jwt保存在客户端,客户端一贯在操作页面,按道理该当一贯为客户端续长有效韶光,否则当jwt有效期到了就会导致用户须要重新登录。
那么怎么为jwt续签呢?最大略粗暴便是每次签发新的jwt,但是由于过于暴力,会影响性能。
如果要优雅一点,又要引入Redis办理,但是这又把无状态的jwt硬生生变成了有状态的,违背了初衷。

以是印证了那句话,没有最好的技能,只有适宜的技能。
感谢大家的阅读,希望看完之后能对你有所收成。

以为有用就点个赞吧,你的点赞是我创作的最大动力~

我是一个努力让大家记住的程序员。
我们下期再见!


能力有限,如果有什么缺点或者不当之处,请大家批评示正,一起学习互换!

标签:

相关文章

今日头条算法如何实现个化推荐与精准传播

信息传播方式发生了翻天覆地的变化。今日头条作为国内领先的信息分发平台,凭借其强大的算法推荐系统,吸引了海量用户。今日头条的算法究竟...

Web前端 2025-01-31 阅读1 评论0

今日头条算法关闭之谜内容分发新格局

今日头条作为一款备受瞩目的新闻资讯平台,凭借其独特的算法推荐机制,吸引了大量用户。近期有关今日头条算法关闭的消息引发了广泛关注。本...

Web前端 2025-01-31 阅读1 评论0

今日头条算法智能推荐背后的科技魅力

信息爆炸的时代已经到来。人们每天在互联网上接触到海量的信息,如何从中筛选出有价值的内容,成为了人们关注的焦点。今日头条作为一款智能...

Web前端 2025-01-31 阅读1 评论0

今日头条算法专利申请个化推荐的秘密武器

信息爆炸的时代已经来临。在众多信息中,如何快速找到自己感兴趣的内容成为了一个难题。今日头条作为中国领先的资讯平台,凭借其独特的算法...

Web前端 2025-01-31 阅读1 评论0

今日头条算法机器推荐模式的秘密与挑战

大数据、人工智能等新兴技术的应用已经渗透到我们生活的方方面面。在信息爆炸的时代,人们获取信息的渠道越来越丰富,如何在海量信息中找到...

Web前端 2025-01-31 阅读1 评论0