主流的网站项目,都是前后端完全分离。一个后端提供 restful api 服务,对应多个终端,如 ios, android, h5, web,在这种情况下,是无法使用 cookie + session 进行会话管理的,可以 token 进行用户认证鉴权,因为 token 是无状态的。
JWT 概述
JSON Web Token(JWT)是一个开放标准RFC 7519,它定义了一种紧凑且独立的方式,可以在各方之间作为 JSON 对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。
基于 token 认证的优势。基于 token 的身份验证是无状态的,我们不将用户信息存在服务器或缓存中,减轻了服务器的压力;相比原始的 cookie + session 方式,token 更适合分布式系统的用户认证,绕开了传统的分布式 session一致性等问题;安全性高,不依赖 cookie,避免了 CSRF 攻击。
一个 JWT 实际上就是一个字符串,它由三部分组成,如下所示,它们之间以点拼接在一起,如 xxxxx.yyyyy.zzzzz
- base64UrlEncode(Header)
- base64UrlEncode(Payload)
- Signature
Header 有两个部分组成,包括 token 类型和 hash 算法,对 Header 进行 Base64Url 编码,作为 JWT 的第一个部分。
{
type: 'JWT',
alg: 'HS256'
}
Payload 中可以添加一些 B/S 之间要传递的信息,如 userId,切记不要写例如密码这样的敏感信息,然后对 Payload 进行 Base64Url 编码,作为 JWT 的第二个部分。
Signature 是需要对 Base64 编码过的 Header, Payload 和密钥,进行算法加密,加密计算过程如下
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
通过签名可以验证 JWT 的有效性,如果被篡改过,就应该返回 401 未授权的错误提示
JWT 认证的实现过程,如下图所示:
JWT 可以保存在 localStorage 中,每次请求在 HTTP 请求时,在 Header 中加入 JWT 信息,格式如下:
Authorization: token
基于 Node.js 语言的 JWT 应用
首先在项目中安装所需的 jsonwebtoken 依赖包
npm i -S jsonwebtoken
签名和验证 token 语法如下
// 签名
jwt.sign(payload, secret, [options, callback])
// 验证
jwt.verify(token, secret, [options, callback])
示例代码
// 签名
exports.signToken = userId => jwt.sign({ userId }, config.secret, { expiresIn: '2h' });
// 验证
exports.verifyToken = token => new Promise((resolve, reject) => {
jwt.verify(token, config.secret, (err, decoded) => {
if(err) reject(err);
resolve(decoded.userId);
});
});
通过 token 验证中间件,保证每次 api 请求,都是带有效 token 的合法请求,并将传递的信息,如 userId 挂载在 req 对象中,然后参与到业务的处理当中。
exports.authToken = function(req, res, next){
const token = req.headers.authorization;
utils.verifyToken(token, config.secret)
.then(userId => {
req.userId = userId;
next()
})
.catch(err => next(err));
}
客户端对 token 的处理
客户端每次发出 http 请求,可以通过 axois 全局配置响应拦截器,检测到401未授权时,清空本地存储中的 token,重定向至登录页。
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response.status === 401) {
localStorage.removeItem('token');
location.href = `/login?redirect=${location.href}`;
}
return Promise.reject(error.response.data);
}
);
参考资料
如果这篇文章对您有帮助,记得给作者点个赞,谢谢!