流程大致如下:
第一次请求时,用户发送账号与密码
后台校验通过,则会生成一个有时效性的token,再将此token发送给用户
用户获得token后,将此token存储在本地,一般存储在localstorage或cookie
之后的每次请求都会将此token添加在请求头里,所有需要校验身份的接口都会被校验token,若token解析后的数据包含用户身份信息,则身份验证通过。
对比传统的校验方式,token校验有如下优势:
在基于token的认证,token通过请求头传输,而不是把认证信息存储在session或者cookie中。这意味着无状态。你可以从任意一种可以发送HTTP请求的终端向服务器发送请求。
可以避免CSRF攻击
当在应用中进行 session的读,写或者删除操作时,会有一个文件操作发生在操作系统的temp 文件夹下,至少在第一次时。假设有多台服务器并且 session 在第一台服务上创建。当你再次发送请求并且这个请求落在另一台服务器上,session 信息并不存在并且会获得一个“未认证”的响应。我知道,你可以通过一个粘性 session 解决这个问题。然而,在基于 token 的认证中,这个问题很自然就被解决了。没有粘性 session 的问题,因为在每个发送到服务器的请求中这个请求的 token 都会被拦截。
下面介绍一下利用node+jwt(jwt教学)搭建简易的token身份校验
示例
当用户第一次登录时,提交账号与密码至服务器,服务器校验通过,则生成对应的token,代码如下:
const fs = require('fs');const path = require('path');const jwt = require('jsonwebtoken');//生成token的方法function generateToken(data){ let created = Math.floor(Date.now() / 1000); let cert = fs.readFileSync(path.join(__dirname, '../config/pri.pem'));//私钥 let token = jwt.sign({ data, exp: created + 3600 * 24 }, cert, {algorithm: 'RS256'}); return token;}//登录接口router.post('/oa/login', async (ctx, next) => { let data = ctx.request.body; let {name, password} = data; let sql = 'SELECT uid FROM t_user WHERE name=? and password=? and is_delete=0', value = [name, md5(password)]; await db.query(sql, value).then(res => { if (res && res.length > 0) { let val = res[0]; let uid = val['uid']; let token = generateToken({uid}); ctx.body = { ...Tips[0], data: {token} } } else { ctx.body = Tips[1006]; } }).catch(e => { ctx.body = Tips[1002]; });});
用户通过校验将获取到的token存放在本地:
store.set('loginedtoken',token);//store为插件
之后客户端请求需要验证身份的接口,都会将token放在请求头里传递给服务端:
service.interceptors.request.use(config => { let params = config.params || {}; let loginedtoken = store.get('loginedtoken'); let time = Date.now(); let {headers} = config; headers = {...headers,loginedtoken}; params = {...params,_:time}; config = {...config,params,headers}; return config;}, error => { Promise.reject(error);})
服务端对所有需要登录的接口均拦截token并校验合法性。
function verifyToken(token){ let cert = fs.readFileSync(path.join(__dirname, '../config/pub.pem'));//公钥 try{ let result = jwt.verify(token, cert, {algorithms: ['RS256']}) || {}; let {exp = 0} = result,current = Math.floor(Date.now()/1000); if(current { let {url = ''} = ctx; if(url.indexOf('/user/') > -1){//需要校验登录态 let header = ctx.request.header; let {loginedtoken} = header; if (loginedtoken) { let result = verifyToken(loginedtoken); let {uid} = result; if(uid){ ctx.state = {uid}; await next(); }else{ return ctx.body = Tips[1005]; } } else { return ctx.body = Tips[1005]; } }else{ await next(); }});
本示例使用的公钥与私钥可自己生成,操作如下:
打开命令行工具,输入openssl,打开openssl;
生成私钥:genrsa -out rsa_private_key.pem 2048
生成公钥: rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
js调用webservice构造SOAP进行身份验证【javascript】 | js调用webservice构造SOAP进行身份验证【javascript】 ...