json-web-token

json-web-token

json-web-token,简称jwt。是服务端开发的认证的一种方式,是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体/载荷(payload)和签名(signature)。

header中的内容指定的是jwt的签名算法

1
2
3
4
{
"typ": "JWT", // 类型
"alg": "HS256" // 算法,默认HS256算法。
}

payload这一部分为实体的状态,每个claim包含传输的信息,以及用于服务验证的信息。即里面可以存我们想要自定义验证的信息(如我们可以用户的id等需要使用的数据),可以包含规范定义的claim的信息。

1
2
3
4
5
6
7
{
"iss": "MRLYJ",
"iat": 1416794645,
"exp": 1448338978,
"aud": "gopeak.cn",
"sub": "jwt",
}
  • iss: jwt的发行者(可选)
  • sub: 主题(可选)
  • aud: jwt的接收者(可选)
  • jti: 令牌的唯一标识符(可选)
  • exp: 过期时间,numericData类型,Unix时间戳(可选)
  • iat: 签发时间,numericData类型,Unix时间戳(可选)
  • nbf: 有效起始时间(当前时间在nbf里的时间之前,则Token不被接受),numericData类型,Unix时间戳(可选)

signature签名

生产token具体步骤

1
2
3
4
5
6
7
8
9
10
11
# 先加头部信息的JSON对象进行[base64编码]
header = encodeBase64(header)
# 再把载体信息的JSON对象进行[base64编码]
payload = encodeBase64(payload)
# 需要提供一个secret(密钥),进行签名操作
key = 'secretkey'
signature = HMAC-SHA256(key, encodeBase64(header) +
'.' + encodeBase64(payload))
# 最后生成的令牌
token = encodeBase64(header) + '.' +
encodeBase64(payload) + '.' + encodeBase64(signature)

jwt传输到服务端的方式相对传统方式比较灵活的,实际场景中,我们接受到服务端认证后返回的token,可以通过cookie或者http请求中自定义头部信息。在通常情况下,大部分会选择头部方法,由于cookie很容易受安全和跨域的影响。

头部规范

1
Authorization: Bearer eyJhbGci...

基于node服务端的实现

所需

koa-jwt

用来校验权限和解析token用的

jwt(obj1).unless(obj2)

  • obj1
  • secret,密钥
  • passthrough
  • key,附属在ctx.state上的属性,存放解析后的数据
  • obj2
  • path,数组类型,设置忽略校验的路径

jsonwebtoken

jsonwebtoken是基于node实现jwt功能的一个库

jwt.sign(payload, secretOrPrivateKey, [options, callback])

  • payload: 载体,类型为object、buffer、string,对于使用exp字段时必须是object。
  • secretOrPrivateKey: 密钥。类型可以查看官方github文档
  • option
  • algorithm (default: HS256)
  • expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, “2 days”, “10h”, “7d”
  • notBefore: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, “2 days”, “10h”, “7d”
  • audience
  • issuer
  • jwtid
  • subject
  • noTimestamp
  • header
  • keyid

其中expiresIn, notBefore, audience, subject, issuer这些属性也可以设置在payload(object), 在其分别对应的
exp,nbf,aud,sub,iss

更多的api解释

开发

思路上实现验证的流程大体上几个步骤

  • 在应用上配置koa-jwt中间件
  • 当权限失效的时候,设置拦截的中间件
  • 实现注册
  • 实现登录

定义权限验证失败中间件

当请求经过koa-jwt校验时,如果没有token或者token已经失效了,该中间件会给出对应的状态码为401的错误信息,如果我们没自定义中间件来处理改错误时会直接返回给用户。

1
2
3
4
5
6
7
8
9
10
this.app.use((ctx, next) => {
return next().catch((err) => {
if (401 === err.status) {
ctx.status = 401;
ctx.body = 'Protected resource, use Authorization header to get access\n';
} else {
throw err;
}
});
});

koa-jwt

1
2
3
4
5
6
this.app.use(jwt({
secret: config.secret,
}).unless({
path: ['/jwt/register', '/jwt/login']
})
);

其中secret为密钥,不限字符串,文件也行。unless()用于设置那些路径不用经过校验,也就是public api(通常对于注册和登录接口是不需要校验权限的)。当校验成功时,koa-jwt会在请求的上下文,即ctxstate中添加user属性,该属性的值为解析后的数据,当然你也可以更换键名。

1
2
app.use(jwt({ secret: 'shared-secret', key: 'jwtdata' }));
# 可以通过ctx.state.jwtdata获取值

register
实现用户注册,将用户信息存入数据库。实际项目中需要加密,字段校验等措施。

login
实现用户登录,查询数据库是否该用户,然后将有关数据通过jsonwebtoken签名生产token并返回给客户端,客户端可以通过localstorage等方式将token存储在本地存储,在每次的 HTTP 请求中,都将 token 添加在 HTTP Header Authorazition: Bearer token 中。后端每次经过koa-jwt去验证该token的正确与否。只有token正确后才能访问到对应的资源。

1
2
3
4
5
jsonwebtoken.sign({
data: user._id,
}, secret, {
expiresIn: 60 * 2, // 设置token有效期
})

secret为密钥,记住这里的secret必须要与中间件jwt()中的secret 一致。

users
实现获取用户信息,我们需要在头部自定义Authorization字段,就可以获取对应的资源。

1
Authorization: Bearer ...(token)

项目地址运行npm run jwt