JWT全称是jsonwebtoken,JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
如何应用
一般是在请求头里加入Authorization,并加上Bearer标注:
服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:
JWT 的定义及其组成
JWT(JSON Web Token) 是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
我们先将用户认证的操作描述成一个JSON对象。其中添加了一些其他的信息,帮助今后收到这个JWT的服务器理解这个JWT。
这里面的前6个字段都是由JWT的标准所定义的。
这些定义都可以在标准中找到。
将上面的JSON对象进行base64编码可以得到下面的字符串:
这个字符串我们将它称作JWT的Payload(载荷)。
如果你使用Node.js,可以用Node.js的包base64url来得到这个字符串:
注:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
头部(Header)
JWT还需要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象:
在这里,我们说明了这是一个JWT,并且我们所用的签名算法(后面会提到)是HS256算法。
对它也要进行Base64编码,之后的字符串就成了JWT的Header(头部):
将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了:
最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret):
这样就可以得到我们加密后的内容:
这一部分又叫做签名。
最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT:
看文章之前,强烈建议先把项目拉取下来!案例来自小弟的开源项目 「项目Github」
文章内容只是个人学习的一些总结经验,不具有权威性,这是 Node 服务端的实现,后面会写前端的实现
常见的 Token 验证方式种:
推荐阅读:
JWT 超详细分析
说一说几种常用的登录认证方式,你用的哪种
推荐阅读:
JSON Web Token 入门教程
JSON Web Token - 在Web应用间安全地传递信息
首先我们先安装 jsonwebtoken 和 express-jwt 这两个中间件
jsonwebtoken : 用于生成 Token 。它也有解析 Token 的功能
express-jwt : 用于解析 Token(比 jsonwebtoken 解决方便) , 它把解析之后的数据,存放到 requset.user 中
如果你看了上面 JWT 介绍的文章,就知道 JWT 是由三部分组成的,分别是 载荷(Payload) 、 头部(Header) 、 签名(Signature) 。
jsonwebtoken 给我们提供了 sign(payload, secretOrPrivateKey, [options, callback]) 方法。sign 方法对应的其实就是 JWT 签名(Signature) 的动作
payload:荷载 ,参数类型:对象secretOrPrivateKey:自定义的密钥,密钥属于敏感信息。参数类型:字符串options:可以配置 header 、荷载、指定算法类型。参数类型:对象callback:回调
眼尖的朋友应该发现, payload 和 options 两个 参数 都可以配置荷,下面有例子。根据自己的习惯选择就好
Payload 部分 JWT 规定了7个官方字段,这些字段都是可选字段。可直接以对象的形式传给 payload 参数。
options 中也可以接受以上七个字段,不过字段名称有所区别。
除此之后 options 提供了 algorithm 和 header ,分别对应使用的加密算法和 JWT 的 Header 部分,其实 algorithm 应该也是属于 Header 部分的。
说了这么多,其实我们一般常用的只有 exp(expiresIn) 和 algorithm 这两个字段,
例子一:
token 的有效时间是配置在 option 里
例子二:
我们也可以在 payload 里配置有效时间
jsonwebtoken 除了生成 token 外,还提供了解析验证 token 的方法, jwt.verify(token, secretOrPublicKey, [options, callback]) 。
这里就不演示了, 感兴趣的朋友可以参考文档: 「JsonWebToken」
express-jwt 是针对 express 框架开发的 JWT Token 验证中间件。我先来简单说以下它的用法。
主要有两种方式,一种是哪些请求需要验证就往哪里加验证;另外一种是先给全部请求加上验证,再给不需要验证的请求配置 白名单 。
方式一:
看完上面的例子,很显然不符合我们的逾期,一个正常的项目有个几十个 api 是分分钟的事。我们不可能一个个给他加上检验
方式二:
这种方式是不是方便很多,而且更美观,维护起来也更方便
Token 解析出来的用户信息,默认存放在 req.user , 可以直接 req.user.userId 来使用生成 Token 时填进去的用户id
你也通过 requestProperty 和 resultProperty 来设置用户信息存放的对象。
这里就不展开,详细文档参考: express-jwt
可以使用 app.use() 来注册处理验证不通过的情况
到这里 Token 的生成、验证、检验不通过错误处理就完成了。 Token 生成一般是在登录之后生成,并返回给前端,前端拿到 Token ,并在每次请求 api 的时候携带上 Token , Token 就相当于这个用户的身份,不要轻易泄露。
Token一旦签发,不能主动让它失效,只能等待它有效期过才能失效。也就是说就算你修改了密码,之前的 Token 也还是有效的。你可以修改后端生成 Token 时使用的密钥,不让之前的 Token 检验通过,但是这就表示之前所有生成 Token 都失效了,做不到针对某个用户进行注销。这显然也不合适的。 所以用户修改密码时,前端一般都要清除之前保存的 Token,再重获取新的 Token
有朋友应该会想到在后端把 Token 储存起来,每一个用户对应一个 token。修改账号时,再生成一个新的 Token 覆盖之前的 Token,但这就违背了使用 Token 的目的,Token 的使用很大程度就为了减少服务器的压力。把尽可能多的信息存储在客户端而不是服务端。
使用 Token 可以防御 CSRF 攻击,之前写过一篇关于网络安全的文章,感兴趣的朋友可以看一下 「XSS 攻击、CSRF 攻击、SQL 注入、流量劫持(DNS 劫持、HTTP 劫持)—— 浏览器安全」