背景

通常我们登录权限认证可以有两种方法,一种是基于 cookie 认证,客户端获取服务器端的 cookie,每次请求携带 cookie 服务端校验,另一种是基于 token,每次请求头部携带 token 进行验证。如下图

前后端分离是主要趋势,后端提供接口,前端实现单页面开发,使用 token 认证的好处:

  1. 跨域请求,cookie 认证的话,服务器设置 Access-Control-Allow-Credentials: true 来允许客户端携带 cookie,设置对应的请求源 Access-Control-Allow-Origin 来指定域名,客户端 xmlhttprequest 请求 withCredentials=true 来传输,流程比较繁琐,而 token 是通过 header 来传输认证相关的信息
  2. 不使用 cookie 认证可以有效防止 csrf 攻击,因为无法利用 cookie 来进行请求伪造
  3. 服务器不需要去数据库存储 session,免了了数据库查询的时间,对分布式也是同一套验证机制

JWT Token

Json web token (JWT)是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519)。该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

JWT Token 是由

..3 部分组成的,分别为
header:base64 编码字符串,存储使用的加密算法(HS256,RSA)等等
payload:base64 编码字符串,存储用户信息
signature:由 header+payload+'secret 字符串'生成,使用 header 的加密算法,服务端定义的 secret 字符串,secret 是保存在服务器端的,jwt 的签发生成也是在服务器端的

express+JWT

安装对应的依赖包

npm install express-jwt jsonwebtoken

使用 expressJwt 中间件

var expressJwt = require('express-jwt');
var jwt = require('jsonwebtoken');
// api路由进行jwt认证
app.use('/api', expressJwt({ secret: secret }));
app.use(express.json());
app.use(express.urlencoded());

登录认证的时候生成 token 返回给客户端

app.post('/authenticate', function (req, res) {
if (!(req.body.username === 'john' && req.body.password === '123456')) {
res.send(401, '账号或者密码错误');
return;
}
var profile = {
id: 123,
username: 'jon',
email: '123456@qq.com',
};
// 利用jwt生成token
var token = jwt.sign(profile, secret, { expiresInMinutes: 60 * 5 });
res.json({ token: token });
});

访问/api/post的时候,expressJwt 中间件会进行认证

app.get('/api/post', function (req, res) {
// 可以通过req.user获取对应的用户信息
console.log('user ' + req.user.email + ' is calling /api/restricted');
res.json({
name: 'foo',
});
});

客户端请求

客户端把 token 存储在 sessionStorage 或者 localStorage,每次请求把 token 取出放到 header 进行传输即可,如下

export function getByCors(opts) {
let token = localStore.getItem(authKey);
return fetch(opts.url, {
headers: {
authorization: token,
},
credentials: 'include',
})
.then((body) => {
if (body.status == 401) {
throw 'unlogin';
} else {
return body.json();
}
})
.catch((err) => {
browserHistory.push('/login');
});
}