Node.js 使用 express-jwt 解析 JWT
<blockquote><p>Node.js 上 Token 鉴权常用的是 passport,它可以自定义校验策略,但如果你是用 express 框架,又只是解析 JWT 这种简单需求,可以尝试下 express-jwt 这个中间件。</p>
</blockquote>
<p><img src="https://img2018.cnblogs.com/blog/1627229/201911/1627229-20191107095300522-345059915.jpg"></p>
<h2 id="关于-jwt">关于 JWT</h2>
<p>JWT 全称 JSON Web Token,是代替传统 session 认证的解决方案。其原理是服务端生成一个包含用户唯一标识的 JSON 对象,颁发给客户端。客户端请求需要权限的接口时,只要把这个 JSON 再原样发回给服务端,服务器通过解析就可识别用户。</p>
<p>它通常是这个样子:</p>
<pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
</code></pre>
<p>这个 JSON 对象通过 <code>.</code> 分成三段,包含了请求头(加密算法)、负载信息(如 userId、过期时间),还有通过服务端密钥生成的签名来保证不被篡改。</p>
<p>这种机制使服务端不再需要存储 Token,因此是非常轻量的用户认证方案。并且对于微服务这种需要不同服务间共用 Token 的跨域认证,JWT 是目前的首选。</p>
<h2 id="关于-express-jwt">关于 express-jwt</h2>
<p>express-jwt 是 Node.js 的一个开源库,由 ID 认证服务提供商 auth0 开发,是专用于 express 框架下解析 JWT 的中间件。</p>
<p>它使用非常简单,而且会自动把 JWT 的 payload 部分赋值于 <code>req.user</code>,方便逻辑部分调用。</p>
<h2 id="开始使用">开始使用</h2>
<h3 id="安装">安装</h3>
<pre><code class="language-shell">npm install express-jwt
</code></pre>
<h3 id="加入中间件">加入中间件</h3>
<pre><code class="language-js">const expressJwt = require('express-jwt')
app.use(expressJwt({
secret: 'secret12345'// 签名的密钥 或 PublicKey
}).unless({
path: ['/login', '/signup']// 指定路径不经过 Token 解析
}))
</code></pre>
<h3 id="生成-token">生成 Token</h3>
<p>生成 Token 的方式依然使用 <code>jsonwebtoken</code>,比如将下列代码加入到登录接口的返回部分:</p>
<pre><code class="language-js">const jwt = require('jsonwebtoken')
app.post('/login', function (req, res) {
// 注意默认情况 Token 必须以 Bearer+空格 开头
const token = 'Bearer ' + jwt.sign(
{
_id: user._id,
admin: user.role === 'admin'
},
'secret12345',
{
expiresIn: 3600 * 24 * 3
}
)
res.json({
status: 'ok',
data: { token: token }
})
})
</code></pre>
<h3 id="获取解析内容">获取解析内容</h3>
<p>当收到带 Token 的请求,如果解析成功,就可以在路由回调里通过 <code>req.user</code> 来访问:</p>
<pre><code class="language-js">app.get('/protected', function (req, res) {
if (!req.user.admin)
return res.sendStatus(401)
res.sendStatus(200)
})
</code></pre>
<p><code>req.user</code> 实际就是 JWT 的 payload 部分:</p>
<pre><code class="language-js">{
_id: '5dbbc7daaf7dfe003680ba39',
admin: true,
iat: 1572587484,
exp: 1573192284
}
</code></pre>
<h3 id="解析失败">解析失败</h3>
<p>如果解析失败,会抛出 <code>UnauthorizedError</code>,可以通过后置中间件来捕获:</p>
<pre><code class="language-js">app.use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).send('invalid token')
}
})
</code></pre>
<h3 id="修改结果字段">修改结果字段</h3>
<p>默认解析结果会赋值在 <code>req.user</code>,也可以通过 <code>requestProperty</code> 来修改:</p>
<pre><code class="language-js">app.use(expressJwt({
secret: 'secret12345',
requestProperty: 'auth'
}))
</code></pre>
<h3 id="允许无-token-请求">允许无 Token 请求</h3>
<p>当接口允许不带 Token 和带 Token 两种状态的访问时(比如文章详情登录后判断点赞),可以通过 <code>credentialsRequired: false</code> 来对无 Token 请求不进行解析和抛出异常。</p>
<pre><code class="language-js">app.use(expressJwt({
secret: 'secret12345',
credentialsRequired: false
}))
</code></pre>
<h3 id="自定义解析">自定义解析</h3>
<p>默认情况下,express-jwt 是从请求 Headers 的 <code>Authorization</code> 字段来获取 Token 并解析。</p>
<p>通过 <code>getToken</code> 也可以自定义一些解析逻辑,比如使用其他 Header 字段,自定义抛出异常等:</p>
<pre><code class="language-js">app.use(expressJwt({
secret: 'secret12345',
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ') === 'Bearer') {
return req.headers.authorization.split(' ')
} else if (req.query && req.query.token) {
return req.query.token
}
return null
}
}))
</code></pre>
<h3 id="作废-token">作废 Token</h3>
<p>在 JWT 机制中,由于 Token 通常不进行存储,如果想作废某一条 Token,一般都是通过被动的方式。</p>
<p>常用的方式是建立某个字段的黑名单(比如 TokenId),对所有 Token 进行过滤,express-jwt 专门提供了回调来处理这种情况:</p>
<pre><code class="language-js">const expressJwt = require('express-jwt')
const blacklist = require('./blacklist')
let isRevokedCallback = function(req, payload, done){
let issuer = payload.iss
let tokenId = payload.jti
blacklist.getRevokedToken(issuer, tokenId, function(err, token){
if (err) { return done(err) }
return done(null, !!token)// 第二个参数为 true 则不通过
})
}
app.use(expressJwt({
secret: 'secret12345',
isRevoked: isRevokedCallback
}))
</code></pre>
<p>更多用法可以查看 官方文档</p>
<hr>
<p><em>本文属于原创,首发于微信公众号「面向人生编程」,如需转载请后台留言。</em></p>
<p><img src="https://img2018.cnblogs.com/blog/1627229/201911/1627229-20191107095248675-1223656501.jpg"></p>
<p>关注后回复以下信息获取更多资源<br>
回复【资料】获取 Python / Java 等学习资源<br>
回复【插件】获取爬虫常用的 Chrome 插件<br>
回复【知乎】获取最新知乎模拟登录</p><br><br>
来源:https://www.cnblogs.com/zkqiang/p/11810203.html
頁:
[1]