用户请求鉴权的实现方案
为什么需要鉴权
鉴权:是指是指验证用户是否拥有访问系统的权利。
为什么要鉴权:对用户进行鉴权,防止非法用户占用网络资源,非法用户接入网络,被骗取关键信息。
明文传输风险:如果您直接在网页或应用程序中输入账号和密码,这些信息可能以明文形式通过网络传输。这会使您的账号和密码容易受到窃听者的攻击,尤其是在未加密的连接上。
密码存储风险:如果网站或应用程序以明文形式存储您的密码,那么如果其数据库遭到入侵,攻击者可以轻松获取您的密码。这将对您的账户安全构成威胁。
缺乏多因素认证:直接输入账号和密码通常不提供多因素认证,这是一种额外的安全层,可以在您登录时要求您提供额外的身份验证信息,如短信验证码或令牌,以增强账户安全。
减少用户动手频率:有了 token ,可以避免用户多次获取信息时重复输入密码,浏览器不必保存密码,只需通过 token 与服务器进行通信验证
Token可以有效减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
初步调研
市面上通常有两种流行的鉴权方案:
Session :此方案适用于非跨域请求,实现较为简单
Session 原理图解,服务器端如何保证一次会话范围内多次获取的Session对象是同一个
由图可见Session是依赖于Cookie而存在的
session 认证原理
于是就要聊到 Cookie 了
对于 Cookie 大家应该都不陌生,很多网站都会询问你是否允许使用 Cookie 。它十分流行,但它并不具备安全性,
这也是为什么网站需要询问你是否允许使用。
https://cloud.tencent.com/developer/article/1705905 参考这篇文章,发现如果采用这种解决方案,则需要将Session和Cookie相结合来使用,使用起来较为困难且不灵活
JWT :此方案适用于跨域请求,是市面上最为流行的解决方案
JWT 认证流程
在身份验证中,当用户成功登录系统时,授权服务器将会把 JSON Web Token 返回给客户端,用户需要将此凭证信息存储在本地(cookie或浏览器缓存)。当用户发起新的请求时,需要在请求头中附带此凭证信息,当服务器接收到用户请求时,会先检查请求头中有无凭证,是否过期,是否有效。如果凭证有效,将放行请求;若凭证非法或者过期,服务器将回跳到认证中心,重新对用户身份进行验证,直至用户身份验证成功。以访问 API 资源为例,下图显示了获取并使用 JWT 的基本流程:
JWT 认证流程
这两者实际上都可以用于我们的项目中,但 JWT 的认证流程更为简单,安全性更高,且能解决跨域需求,于是选择 JWT 作为鉴权的选择
初次上手实现
在查询学习相关内容之后,手写了一套简单的代码,使用的是 jsonwebtoken 这个库:
并在之前的注册登录中加入如下代码:
又进行一番研究之后,改为使用 express-jwt 库,兴致冲冲地去运行服务时却告诉我
询问GPT却得到了这样的回复
难绷
看来似乎不是我的问题(毕竟gpt也妹搞清楚),于是经过查询文档和查找论坛,发现是库的升级导致了老的语法无法使用,需要在函数中加上这样一句:
再次运行,发现又出错了,网上也难以找到解决方法,无奈只能自行研究。
几十分钟之后发现了奇怪的问题,总之问题解决了
验证成果
当正确输入token时:
当输入错误token时:
结果是正常的,但返回值不太好看,于是添加一个错误中间件:
router.use((err, req, res, next) => {
// token验证失败
if (err.name === 'UnauthorizedError') {
res.status(401).json({ message: '无效的token' });
}
// 其他错误
res.status(500).json({ message: err.message });
});
再次发送请求,完成:
简洁多了的错误返回
完善代码
JWT 的生成需要一个初始密钥,而前往多个文件中修改一串字符串是很浪费时间和精力的事情,还有可能遗漏而造成代码错误。
于是经过查询,我找到了config这个库,它可以从一个统一的配置管理文件中读取对应的数据
顺带把其他的一些配置比如端口、数据库之类的也统一管理了
考虑到这是一个较大的项目,应该尽量降低后期维护成本,于是采用模块设计,将各个中间件分成一个个组件。
此过程中遇到了一些困难,查找无果后去 Mattermost 询问,后来不知道为什么好了,于是这一步也解决了。
结束
本次使用 JWT 鉴权已经实现基本功能,后续还需根据需求与其他 API 进行对接