背景
通常我们登录权限认证可以有两种方法,一种是基于 cookie 认证,客户端获取服务器端的 cookie,每次请求携带 cookie 服务端校验,另一种是基于 token,每次请求头部携带 token 进行验证。如下图
前后端分离是主要趋势,后端提供接口,前端实现单页面开发,使用 token 认证的好处:
- 跨域请求,cookie 认证的话,服务器设置 Access-Control-Allow-Credentials: true 来允许客户端携带 cookie,设置对应的请求源 Access-Control-Allow-Origin 来指定域名,客户端 xmlhttprequest 请求 withCredentials=true 来传输,流程比较繁琐,而 token 是通过 header 来传输认证相关的信息
- 不使用 cookie 认证可以有效防止 csrf 攻击,因为无法利用 cookie 来进行请求伪造
- 服务器不需要去数据库存储 session,免了了数据库查询的时间,对分布式也是同一套验证机制
JWT Token
Json web token (JWT)是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519)。该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。
JWT Token 是由
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'); });}