JWT简介JWTJSONWebToken全称是什么,是目前最流行的跨域认证方案。基本实现是在服务器端认证后生成一个JSON对象,返回给用户。当用户与服务器通信时,必须发回此JSON对象。JSON类似如下:{"Name":"ZhangSan","Role":"Administrator","ExpirationTime":"0:00onJuly1,2018"}为什么需要JWT?认证过程是根据session_id和Cookie实现的1.用户向服务器发送用户名和密码。2、服务器认证通过后,在当前会话(session)中保存相关数据,如用户角色、登录时间等。3、服务器返回一个session_id给用户,写入用户的Cookie中。4、用户后续的每一次请求都会将session_id通过cookie传回服务器。5、服务端收到session_id,找到之前保存的数据,从而知道用户的身份。但是这里有一个大问题。如果是服务器集群,需要session数据共享,每个服务器都可以读取session。这个实现成本比较大。但是JWT改变了思路,将JSON数据返回给前端。前端再次请求时,会将数据发送给后端,后端进行校验。即服务器是无状态的,所以更容易扩展。JWT数据结构JWT的三部分如下:头部(header),类似如下{"alg":"HS256","typ":"JWT"}alg属性表示签名算法(algorithm),默认是HMACSHA256(写成HS256)。typ属性表示token(令牌)的类型(type),JWTtoken统一写为JWTPayload(负载)。它也是一个JSON,用于存储实际需要传递的数据。JWT规定了7个官方字段。如下图iss(issuer):发行者exp(expirationtime):过期时间sub(subject):主题aud(audience):受众nbf(NotBefore):有效时间iat(IssuedAt):发行时间jti(JWTID):号码当然也可以自定义为私有字段。但是要注意,JWT默认是不加密的,任何人都可以读取,所以这部分不要放机密信息。签名。Signature部分是前两部分的签名,防止数据被篡改。首先,您需要指定一个密钥(秘密)。这个密钥只有服务器知道,不能透露给用户。然后,使用Header中指定的签名算法(默认为HMACSHA256),按照以下公式生成签名。HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)计算完签名后,将Header、Payload、Signature三部分组合成一个字符串,每部分用“.”分隔,即可以返回给用户。如下图,JWT的安全JWT默认是不加密的,但是也可以加密。如果JWT未加密,则无法将机密数据写入JWT。JWT本身包含身份验证信息。一旦泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期应该设置的比较短。对于一些比较重要的权限,用户在使用时需要重新认证。为了减少盗用,JWT不应使用HTTP协议明文传输,而应使用HTTPS协议传输Node简单demo——KoaJWT实现JWT的实现方式,大致流程如下:首先,用户登录后,服务端根据用户信息生成token返回给客户端,前端在下一次请求中将token发送给服务端,服务端验证有效后返回数据。如果无效,则返回401状态码这里我们使用Node来实现。主要用到的两个库是jsonwebtoken,可以生成token,校验等,koa-jwt中间件进一步封装了jsonwebtoken,主要用来校验token,快速搭建一个koa项目,发现目前官方没有快速的方法构建一个koa项目,比如Vue-cli。(可能是搭建koa项目的成本也很低)。不过作为一个懒人,我还是找到了一个工具——koa-generator,使用起来还是比较简单的。安装如下:npminstall-gkoa-generatorkoa2my-project新建一个名为my-project的koa2项目cdmy-projectandnpminstall启动项目npmstart打开localhost:3000生成Token。为了演示方便,我这里直接定义了变量userList,用来存放用户信息,实际上应该存放在数据库中。constcrypto=require("crypto"),jwt=require("jsonwebtoken");//TODO:使用数据库//这个要存到数据库里,这里只是演示letuserList=[];classUserController{//用户登录staticasynclogin(ctx){constdata=ctx.request.body;if(!data.name||!data.password){returnctx.body={code:"000002",message:"参数无效"}}constresult=userList.find(item=>item.name===data.name&&item.password===crypto.createHash('md5').update(data.password).digest('hex'))if(result){consttoken=jwt.sign({name:result.name},"Gopal_token",//秘密{expiresIn:60*60}//60*60s);returnctx.body={code:"0",message:"登录成功",data:{token}};}else{returnctx.body={code:"000002",message:"用户名或密码错误"};}}}模块。exports=用户控制器;通过jsonwebtoken的sign方法生成token。该方法的第一个参数是指Payload(负载),用于token编码后存储的数据,也是token验证后可以得到的数据。第二个是密钥,它是服务器唯一的。注意验证时必须相同才能解码,而且是保密的。一般来说,最好设置一个公共变量。这个只是为了演示方便,直接写死了。第三个参数是option,可以定义token过期时间。客户端获取令牌。前端登录获取token后,可以保存在cookie中,也可以保存在localStorage中。这里我直接保存到localStoragelogin(){this.$axios.post("/api/login",{...this.ruleForm,}).then(res=>{if(res.code==="0"){this.$message.success('登录成功');localStorage.setItem("token",res.data.token);this.$router.push("/");}else{this.$message(res.message);}});}封装了axios的拦截器,每次请求时将请求头中的token发送给服务器进行校验。这里如果之前放在cookie里面,可以自动发送,但是这个不能跨域。所以推荐的方法是放在HTTP请求头Authorization中,注意这里Authorization的设置,在前面加上Bearer。详见BearerAuthentication//axios请求拦截器处理请求数据axios.interceptors.request.use(config=>{consttoken=localStorage.getItem('token');config.headers.common['Authorization']='Bearer'+token;//注意这里的Authorizationreturnconfig;})验证token使用koa-jwt中间件进行验证,方法比较简单,如下所示//错误处理app.use((ctx,next)=>{returnnext().catch((err)=>{if(err.status===401){ctx.status=401;ctx.body='受保护的资源,使用授权标头获取访问权限\n';}else{throwerr;}})})//注意:放在路由前面app.use(koajwt({secret:'Gopal_token'}).unless({//配置白名单路径:[/\/api\/register/,/\/api\/login/]}))//routesapp.use(index.routes(),index.allowedMethods())app.use(users.routes(),users.allowedMethods())required注意以下几点:secret必须和sign时间一致,接口白名单可以通过unless配置,即哪些url不能验证,比如登录/注册,哪个无需验证即可验证。中间件需要放在验证过的路由前面,前面的url无法验证。如果直接访问需要登录的界面,会先注册401,再登录,否则提示用户名或密码错误,登录后带上Authorization,即可正常访问。返回200和正确的数据汇总总结了关于JWT认证的知识,并提供了一个简单的koa2实现的demo。希望对大家有所帮助。限于篇幅,有机会单独说一下koa-jwt的源码,比较简单~本文demo地址:Client和Server参考JSONWebToken入门教程Node.js应用:Koa2使用JWT进行身份验证
