前段时间处理一个抽奖H5,测试过程中想到如果有用户抓到抽奖接口,比如
https:xxx/lottery/userinfo
复制代码
如果直接访问抽奖接口,可以直接进行抽奖动作。这里就涉及到处理验证用户身份的问题
之后的解决方式是 判断接口的cookie中是否包含 userInfo 等参数信息
不过还可以通过另外一种方式来处理-- JWT
什么是JWT (JSON WEB TOKEN)
JWT是通信双方之间以 JSON对象的形式安全传递信息的方法。
其实可以理解为使用非对称算法来进行前后端校验。
JWT 由三部分组成
头部
-
typ 声明类型
-
alg 声明加密的算法
然后按照此规则将头部信息加密,构成JWT第一部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
复制代码
payload
payload 就是存放有效信息的地方
payload 中有一些参数字段是建议使用的 (仅列出几个)
参数 | 含义 |
---|---|
iat | jwt的签发时间 |
exp | jwt的过期时间,这个过期时间必须要大于签发时间 |
nbf | 定义在什么时间之前,该jwt都是不可用的 |
比如来定义一个payload
{
"exp": Math.floor(Date.now() / 1000) + (60 * 60),
"name": "John Doe"
}
复制代码
payload 会进行base64加密,构成JWT第二部分
签证
可以看到,签证部分是由三个部分组成的
参数 | 含义 |
---|---|
base64UrlEncode | base64加密后的Header |
base64UrlEncode | base64加密后的payload |
your-256-bit-secret | 自定义的加密secret |
secret 相当于私钥,不可泄漏,如果客户端可以拿到secret,就可以自我签发JWT了
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload)
var signature = HMACSHA256(encodedString, 'secret')
复制代码
signature 是JWT的第三部分
将以上三部分拼接起来,就是最后的JWT
第三方库
jsonwebtoken
如果自己在生成jwt,有点复杂。目前已经有很多开发的第三方库来支持JWT。比如 jsonwebtoken
-
sign 用于生成 token
-
verify 用于检验token
koa-jwt
koa-jwt 用于验证接口中是否包含token信息
搭建了一个简易的server 来看下效果
app.use(
jwtKoa({secret: SECRET})
.unless({
path: [/\/login/] // 不需要通过jwt验证的请求路径
})
)
router.get('/login', async (ctx) => {
let token = jwt.sign({
name: 'dva'
}, SECRET)
console.log(token, 'token')
ctx.body = {
token
}
})
router.get('/try', async (ctx) => {
let token = ctx.header.authorization
let result = jwt.verify(token, SECRET)
ctx.body = {
result
}
})
复制代码
- /login 拿到token
// {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZHZhIiwiaWF0IjoxNTMxMjgwMDg2fQ.Rh_vAKeytjAL2TbOk-MmXQWFesszjRU3Bzldrx5x17s"}%
复制代码
- 如果不添加token 会被koa-jwt 拦截
- 添加 token
工作机制
图片来自于文章 《前后端分离之JWT用户认证》
-
登录拿到JWT
-
前端发起请求,Header中挂载JWT
项目实战
由于我搭建的这个项目中有这种需要鉴权的接口比较少,所以并没有使用koa-jwt来处理。只是用了 jsonwebtoken
server端
构建两个接口 login , lottery
- login 用来生成JWT 返回给前端
token = jwt.sign({
name: 'who',
exp: Math.floor(Date.now() / 1000) + (60 * 60), // 设置 token 过期时间
}, SECRET)
复制代码
- lottery 用来验证JWT,验证通过则进行抽奖动作
let token = this.headers.authorization
// 解码
let decoded = jwt.verify(token, SECRET)
// console.log(decoded, 'decoded')
let {name} = decoded
if (name != 'who') {
code = 403
return
}
复制代码
前端
- 首页点击登录后 经返回的token信息存储起来
我这里拿到token之后将其写入了localStorage
window.localStorage.setItem('token', token)
复制代码
- 进入抽奖页面进行抽奖,每次请求的时候挂载Authorization
axios.get(`/${APP_NAME}/win`, {
headers: {
Authorization: token
}
})
复制代码
如果传递错误的token 在server端JWT验证的时候就会报错
总而言之,如果你的接口需要考虑鉴权问题,可以参考下JWT来处理。