api基于token认证机制

背景

通常我们登录权限认证可以有两种方法,一种是基于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是由<header>.<payload>.<signature>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');
  })
}