前言
自己用react+koa实现了一个包含登陆和注册功能的网址,在这里记录一下实现过程
项目地址:github地址
预览地址:预览地址
注册
注册其实没什么好说的,就是要注意不要明文保存密码,否则数据库泄露后,密码会被其他人用来撞库使用。
前台加密主要是为了防止post请求明文传递密码,本来我想在前端加密,然后数据库直接保存加密的密码到数据库,登陆时传递加密后的密码进行登陆,但是前端使用的加密工具加密同一个密码每次都生成不同的结果,导致每次查询都是密码不正确,我只能在后端先解密前端密码,然后换用了一个加密工具,加密同一个密码生成相同的结果,这样查询密码才会正确。
登陆
传统的登录系统是通过cookie
和session
做的登录,这种登录需要后端记录session,当在线用户很多时,会增加服务器压力,这里我使用JSON Web Token (JWT)
更好的方式来做登录(后端不需要记录任何东西)。关于cookie、session、token的概念我简单的总结过,具体可以参考这里
简述登录流程:
- 用户使用账号密码登录
- 后台验证账号密码是否正确,正确则生成
token
并发送给前台。 - 前台存储
token
并在下次访问传递给后台 - 后台验证前台传过来的
token
是否正确,不正确则返回错误给前台,让他重新登录
实际上我们需要解决以下问题就可以实现一个登陆:
- 后台如何生成token?
- 前台如何保存token,并传递给后台?
- 既然后台不保存任何信息,后台如何验证token?如何知道token不是伪造的
举个简单的例子:
我有个箱子要送出去,我自己不记录我送了哪些箱子,别人送回来的时候我怎么知道这是我的箱子?
很简单,我给箱子上把锁,别人还回来的时候,我用我自己的钥匙打开,能打开就是我的箱子,否则就不是。
生成token时,我们可以将用户名通过某种加密算法加密生成一个串字符串,解密时再用加密算法还原,如果可以还原就证明这个token有效,否则无效。
jsonwebtoken
我自己使用jsonwebtoken来生成和解析token,下面是一个简单的使用
const jwt = require('jsonwebtoken');
const secert = 'secert_key' //相当于钥匙,这个钥匙不要让其他人知道,否则别人会拿这个生成有效token
//加密,根据secert加密生成token
const token = jwt.sign({username:'zzh'},secert)
console.log('token',token)
//解密,如果token正确会还原,否则会抛出错误
try {
const result = jwt.verify(token,secert)
console.log(result)
} catch (error) {
console.log(error)
}
上面就是一个加密和解密的过程,这就实现token
的验证。我们在登录正确时,将用户名或id用上面的方法加密生成token,并返回给前台,前台用某种方式存储(SessionStorage、LocalStorage
),然后前端可以放在请求头中传递给后台,后台再验证token是否正确。
koa-jwt
直接使用上面的jsonwebtoken
太麻烦了,我们需要自己对接口进行token验证和验证失败的处理。可以使用koa-jwt中间件来帮我们完成这些功能,我只需要会用即可。
//app.js
...
var jwt = require('koa-jwt');
app.use(jwt({ secret: 'secert_key' }).unless({ path: [/^\/public/] }));
当我们使用koa-jwt
中间件后,app会自动去验证token,当token不存在或token失效时,会抛出401错误。另外我们要注意几点:
- 生成token时还是用的
jsonwebtoken
进行加密的(koa-jwt自带jsonwebtoken依赖) - 加密和解密的secret必须一样(不要泄露)
koa-jwt
默认是从请求头的Authorization中寻找token,如果需要以其他方式传递token则自行根据官网设置。Authorization:'Bearer ’ + token- 要设置不需要验证token的接口,比如登陆接口是不需要验证token的(通过unless设置)
关于koa-jwt
的使用可以看github文档或这个demo
下面几个问题可以思考
token存在前端什么位置合适?
可以存在SessionStorage、LocalStorage、cookie中。
前端根据什么判断是否登录?
当登陆成功时存储token到cookie中,然后判断cookie中是否有token
如何防止token被盗,然后绕过登陆的问题?
这样有个问题,我可以通过浏览器的调试工具拿到token,然后在其他地方不登录,在浏览器的调试工具手动设置token到cookie或storage中去,这样就绕过了登录。这其实是jwt的一个弊端,只要是一个正确的token,服务器都认为是有效的。解决的方法可以用https或者将token有效时间缩短。
cookie的httpOnly只能防止前端读取cookie,但是不能限制前端设置cookie
如何在退出登录时使token失效?
可以将用过的token加入黑名单,这样不管是退出登录还是窃取别人的token使用都不会登录成功
如何防止同个账号同时登录?