2.单点登录(SSO)的设计与实现?
Redis+Jwt实现单点登录
功能描述
项目为前后端分离,微服务架构。利用 Redis+JWT实现了单点登录
业务流程分析
登录详细流程
1、用户输入账号、密码发起登录请求
2、前端调用后端接口 /api/auth/mcode
获取加密码(随便取得一个名,后台生成 mcode 后会将 mcode 设置 1分钟的过期时间存于 Reids中,方便后面验证)
3、前端使用 sh256 算法 对 password 加密后,再对(加密密码+mcode)进行加密 ===》sh256算法(sh256(password)+mcode)=mmpwd
密码:password
加密码:mcode
sh256(password):mpwd
sh256(sh256(password)+mcode)==mmpwd
4、发起登录请求 /api/auth/login
,params={mcode:xxx,username:xxxx,password:mmpwd}
5、网关拦截 检测 login请求 不在拦截范围 放行来到 auth
认证中心进行登录认证
6、根据用户名查询数据库获取mpwd(数据库存的密码是被sh256加密后的密码====》 sh256(password)=mpwd)
7、从 Redis 中获取 mcode,利用sh256(mpwd+code)计算得到的mmpwd' 和前端传来的 mmpwd 比较,相同则登录成功
8、生成JWT
9、将 JWT 存于 Redis,以 用户类型+产品码+工程ID+用户名+JWT 作为 Redis 的 key,JWT 作为 value存入 Redis
LoginType_Type(用户类型)_productCode(产品码)_projectId(工程Id)_username(用户名)+jwt(也就是存于header的token),JWt
10、将 key 存入 cookie,存于浏览器
git config --global user.email "2636702424@qq.com"
基本思路介绍
第一阶段
用户发起 登录请求 /api/auh/login
网关拦截 检测不在拦截范围 放行来到 auth
认证中心
根据 用户名密码 查询 MySQL 数据库
若用户存在则利生成 JWT
将 JWT 存于 Redis,以 用户类型+产品码+工程ID+用户名+JWT 作为 Redis 的 key,JWT 作为 value存入 Redis
LoginType_Type(用户类型)_productCode(产品码)_projectId(工程Id)_username(用户名)+jwt(也就是存于header的token),JWt
将 key 存入 cookie,存于浏览器
第二阶段
用户发起其他业务请求 /api/service/my
网关拦截 检测在拦截范围 进行登录验证
取出 cookie 中的 key,从 Redis 取出对应的 JWT 进行校验,通过则代表已登录 放行到其他微服务
第三阶段
通常其他微服务会做请求拦截
拦截器会从 cookie 取出 key 去 Redis 获取 Token 进行校验 并解析得到用户的基本信息 UserInfo
将解析得到的 UserInfo 存于 本地线程变量(ThreadLocal)中
后面业务处理可以将用户基本信息从本地线程变量(ThreadLocal)取出
面试分析
1、为什么 key 要这样设计?
企业级项目考虑系统可能用多种用户类型、不同的子系统等,因此 key 的拼接参数会有很多标识进行拼接,可以根据自己的项目情况选取。
2、为什么 key 前面有了唯一标识串 (用户类型+产品码+工程ID+用户名)后面还要拼接 JWT
用户可能在不同的地方进行登录,每次生成的 JWT 不一样,同一个用户也可能有多个 JWT,因此还需要在后面拼接 JWT 对相同用户进行区分。
3、退出登录怎样实现?
退出登录也就是将 Redis 里存的 JWT 删除即可,但是一个用户可能在不同的地方进行登录,一个用户可能有多个 JWT存于Redis。
因此退出登录,从 Redis 缓存中删除用户 Token 信息时需要正则模糊删除(LoginType_type_productCode_projectId_username_*),前面那串相同用户是一样的。
问题拓展
1、Redis 模糊匹配查询怎样做?
redisTemplate.keys(【正则表达式】
4、Redis 的内存空间是十分宝贵的,你这随着时间推移,用户增加 Redis 存的 JWT 不会越来越多吗?
登录是有过期时间的,Redis 在存 JWT 时会设置一个合理的过期时间。
并且,正常逻辑是用户在进行系统访问操作时,应该对过期时间进行刷新。