前言
今天是2023年8月12号,周六,今天不用上班,只是前几天得知消息我前项目组的同事们被裁员,说不定哪个明天就轮到我了吧,所以今天过来公司,研究下公司项目架构,离职的时候带点东西也不算亏
说回登录,我还未工作前,行内人一直告诉我登录功能很简单应届生应该都应该会,可工作两年的我发现,登录鉴权这块基本是高级开发、开发经理、技术负责人等人来写的,这让我之前的想法心生旁骛
登录方式
在PC端,登录的方式有很多:微信登录,手机短信登录,注册账号登录,或第三方账号登录....
在早期还是以注册系统内部账号登录为主的,只是后来为方便用户才搞了其他更加便捷的方式,但这些网站或客户端大多数toC,实际上目前很多的登录方式还是账号登录,而我接下来要讲的也是这种
登录功能-后端接口
在这里我们不讲前端的东西,只说登录需要后端提供哪些接口
- 登录接口:也就是用户输入账号密码,点击【登录】按钮后,访问的接口
- 忘记密码。
- 获取登录验证码(图形验证码)
- 获取短信验证码
- 获取加密秘钥
获取加密秘钥
不知大家有没听说过“公钥加密,私钥解密”,这是为了保护用户密码等敏感信息用的,且目前RSA非对称加密比较主流
大家不知道有没像我以前有个疑问:为啥要提供公钥的接口,公钥在前端生成不行吗?然后后面才知道,公钥和私钥生成都是随机的,且都是同时生成且一对一的关系,注意并不是每次登陆都需要生成公钥和秘钥,如果redis里已经有就用
如果我们把公钥和私钥生成都放在前端,就需要把私钥经过网络传给后端解密,这是不安全的做法,因此公钥私钥的生成应该依靠后端,然后仅提供公钥接口给前端即可
目前后端生成公钥和私钥后,会暂时存放在redis居多,不是mysql等
获取登录验证码(图形验证码)
这个相比大家很熟悉了,就是生成4个字母或数字的图片
登录验证码的调用还是看业务,要么是错误到一定次数后才调用,要么是一开始就调,前者比较常见点
后端的实现逻辑一般是:
- 先生成4个数字或字母的验证码,再生成图片
- 验证码存放在redis里,key为账号名或【唯一表示 + 账号名】,value为验证码;方便后续用户填的来比对
- 图片转base64字符串,再返回给前端
- 前端拿到字符串再解析成图片
登录接口
登录常见逻辑如下:
- 校验:校验账号名和密码有没填写、格式是否错误、当前用户状态是否是锁定、停用状态
- 密码rsa解密,再md5加密与数据库比对
- 用户错误次数 < 指定错误次数 && 密码错误,则更新数据库次数+1
- 用户错误次数 >= 指定错误次数 && 密码错误,直接抛异常
- 用户错误次数 >= 指定错误次数 && 密码正确,验证验证码,验证错误抛异常,验证正确表示登录已经成功,登录错误次数要清0,且保存登录日志
- 给用户类设置角色信息,用于跳转到系统内部后获取菜单权限
- 很重要的一步,用户登录成功要有一个持久的状态,在古老的javaweb是存session,而如今有springsecurity的加持,只需要设置用户认证标识即可,如下:
UserPrincipal principal = new UserPrincipal();
principal.setUserId(userInfoDTO.getId());
.....
SecurityContextHolder.getContext().setAuthentication(new UserAuthenticationToken(principal));
其实到这里呢,登录已经成功,接下来就是帮助用户调整到系统内部界面和调用内部系统接口了
大家应该知道,系统内部的接口,在不考虑权限的情况下,是只有登录成功的用户才可以访问的,按理应该给浏览器一个登录成功的标识,常见以cookie形式保存用户token,下次用户带上token就行了
那有人问,所有的接口都要加上验证token的代码吗?当然不是,真是这样代码量会非常大,因此要在所有接口前加一个拦截器或过滤器类,建议是写在单独一个模块里作为依赖来引入,这样哪些项目需要校验token就拿来用就行了
获取短信验证码
短信验证码一般是当忘记密码要找回密码时,判断是否本人用的
- 生成验证码,保存到redis或mysql数据库中
- 发送短信验证码到用户手机
- 返回是否发送成功信息。注意:短信验证码是发给用户手机,不是给到前端
忘记密码
忘记密码,一般是通过短信验证码来确定是否本人,如果是的话,则会让你重新设置新密码
代码逻辑:
- 校验:手机号是否正确,用户当前状态是否允许重置新密码
- 校验短信验证码
- 若成功,两次密码输入是否一致
- 再成功,则重置密码成功