传统的服务端验证使用的技术是 session 来实现,就是把登录信息存在服务端,每次通过访问 sessionid 来进行获取信息,但这样会出现一些问题,比如每当用户发起请求时,服务端都需要创建一个对象用于存储这些信息,这样当越来越多的用户访问,那么服务端创建的 session 也就越来越多,从而导致内存的开销也越来越大。
基于 token 的身份验证是无状态的,可以不用将信息存储在服务器端或者 session 中,token 依赖于请求头传输,当客户端发送请求时,只需将 token 信息放到 header 请求头一并发给服务端,服务端对 token 做解析认证即可。
基于 token 验证的步骤如下:
JWT(JSON Web Token) 编码解码模块
安装
npm install --save jwt-simple
生成token & 解析token
const tokenExpiresTime = 1000 * 60 * 60 * 24 * 7
//秘钥
const jstSecret = 'jstSecret'
//需要加密的对象
const payload = {
user:'wang',
environment:'web',
expires: Date.now() + tokenExpiresTime
}
//encode
var token = jwt.encode(payload, jstSecret)
console.log('token: ', token)
//decoded
var decoded = jwt.decode(token, jstSecret)
console.log('decoded: ', decoded)
执行结果:
token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoid2FuZyIsImVudmlyb25tZW50Ijoid2ViIiwiZXhwaXJlcyI6MTUzNTUyMDk1MTcxOX0.4rmP6UeeJ3mQbHfplruH15PvuUxPzFTxAfVnPgA7sgo
decoded: { user: ‘wang’, environment: ‘web’, expires: 1535520951719 }
整个token 分为三部分:头部(header)、载荷(payload, )、签证(signature),上方截图中的token字符串是由此三部分经过base64加密得到的
Header
{
'typ': 'JWT',
'alg': 'HS256'
}
algorithm 默认的为HS256,源码中可以看到其他可选
/**
- support algorithm mapping
*/
var algorithmMap = {
HS256: 'sha256',
HS384: 'sha384',
HS512: 'sha512',
RS256: 'RSA-SHA256'
};
token 的核心,一般主要包括:
iss(Issuer): token 签发者
sub(Subject): jwt所面向的用户
aud(Audience): token 收件人
exp(Expiration Time): token 的过期时间,一般当前时间加期限
nbf(Not Before): 验证该时间之前token 无效
iat(Issued At): token 签发时间
jti(JWT ID): jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
签名创建,使用编码后的header 和payload,通过指定的算法加秘钥得到的,秘钥必须保存在服务器端。例如:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
jwt_encode(payload, key, algorithm, options)
jwt_decode(token, key, noVerify, algorithm)
服务器对用户提交的信息(一般用户名密码)验证成功后,服务器根据提交的公共信息(用户名或ID,不能使用敏感信息如密码,因为token在客户端也是可以被解密的)外加服务器持有的秘钥生成token并返回给客户端,客户端在随后需要验证的请求中携带该token,服务器每次处理请求之前先通过该token验证用户的合法性(签发者是不是服务器自己,token是否过期,是否在有效期内),通过后再交由其它模块处理请求。
token可以作为请求链接的参数、请求主体(body)的参数,或者请求头(Header)的参数
可以通过规定携带方式,也可以同时通过多种方式获取
var token = (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];