• 谷粒商城 高级篇 (十七) --------- 单点登录



    一、SSO 介绍

    Single Sign On 一处登陆、处处可用。

    1. 单点登录业务介绍

    早期单一服务器,用户认证。

    在这里插入图片描述

    缺点:单点性能压力,无法扩展

    分布式、SSO (single sign on) 模式:

    在这里插入图片描述

    解决 :

    • 用户身份信息独立管理,更好的分布式管理。
    • 可以自己扩展安全策略
    • 跨域不是问题

    缺点:

    • 认证服务器访问压力较大。

    2. 什么是跨域 Web SSO

    域名通过“.”号切分后,从右往左看,不包含“.”的是顶级域名,包含一个“.”的是一级域名,包含两个“.”的是二级域名,以此类推。

    例如对网址 http://www.cnblogs.com/baibaomen,域名部分是www.cnblogs.com。用“.”拆分后从右往左看:

    cookie.setDomain(.cnblogs.com”);//最多设置到本域的一级域名这里
    cookie.setDomain(.baidu.com”);//最多设置到本域的一级域名
    

    这里”com”不包含“.”,是顶级域名; “cnblogs.com”包含一个“.”,是一级域名;
    www.cnblogs.com 包含两个“.”,是二级域名。

    blog.cnblogs.com
    news.cnblogs.com
    

    跨域 Web SSO 指的是针对 Web 站点,各级域名不同都能处理的单点登录方案。

    3. 浏览器读写 cookie 的安全性限制

    一级或顶级域名不同的网站,无法读到彼此写的 cookie。

    所以 baidu.com 无法读到 cnblogs.com 写的 cookie。

    一级域名相同,只是二级或更高级域名不同的站点,可以通过设置 domain 参数共享cookie读写。这种场景可以选择不跨域的 SSO 方案。域名相同,只是 https 和 http 协议不同的 URL,默认 cookie 可以共享。知道这一点对处理 SSO 服务中心要登出。

    4. http 协议是无状态协议。

    浏览器访问服务器时,要让服务器知道你是谁,只有两种方式:

    方式一: 把“你是谁”写入 cookie。它会随每次 HTTP 请求带到服务端;

    方式二: 在 URL、表单数据中带上你的用户信息 (也可能在 HTTP 头部)。这种方式依赖于从特定的网页入口进入,因为只有走特定的入口,才有机会拼装出相应的信息,提交到服务端。

    大部分 SSO 需求都希望不依赖特定的网页入口 (集成门户除外) ,所以后一种方式有局限性。适应性强的方式是第一种,即在浏览器通过 cookie 保存用户信息相关凭据,随每次请求传递到服务端。我们采用的方案是第一种。

    二、单点登录框架

    我们下载 gitee 上的 xxl-sso 单点登录项目进行使用

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    修改登录认证服务器的配置文件

    在这里插入图片描述

    编写域名映射,模拟一处登录步步登录

    在这里插入图片描述
    启动之前将 应用打包。。。

    在这里插入图片描述

    打完包之后生成 jar 包,用 java -jar 运行即可。。
    在这里插入图片描述
    在这里插入图片描述
    访问服务器

    在这里插入图片描述
    接着启动客户端,两个客户端分别以 8081 与 8082 端口启动。。。

    所以我们需要修改配置文件。。。,要注意我们启动的客户端是这个项目

    在这里插入图片描述

    在这里插入图片描述
    修改完之后要重新打包。。。

    在这里插入图片描述

    启动客户端1。。。

    在这里插入图片描述
    然后以 8082 端口启动客户端2

    在这里插入图片描述

    我们进行测试发现,client1 进行登录 client2 就没必要登录

    在这里插入图片描述
    在这里插入图片描述

    三、登录接入方式

    1. Cookie 接入方式

    在这里插入图片描述

    2. Token 接入方式

    类似于社交登录。。

    3. 有状态登录

    为了保证客户端 cookie 的安全性,服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如 tomcat 中的 session。例如登录:用户登录后,我们把登录者的信息保存在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session。然后下次请求,用户携带 cookie 值来,我们就能识别到对应session,从而找到用户的信息。

    缺点是什么?

    • 服务端保存大量数据,增加服务端压力
    • 服务端保存用户状态,无法进行水平扩展
    • 客户端请求依赖服务端,多次请求必须访问同一台服务器

    即使使用 redis 保存用户的信息,也会损耗服务器资源。

    4. 无状态登录

    微服务集群中的每个服务,对外提供的都是 Rest 风格的接口。而 Rest 风格的一个最重要的规范就是:服务的无状态性,即:

    • 服务端不保存任何客户端请求者信息
    • 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份带来的好处是什么呢?
    • 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务- 服务端的集群和状态对客户端透明
    • 服务端可以任意的迁移和伸缩
    • 减小服务端存储压力

    5. 集成社交登陆

    A、用户点击不同的社交登陆按钮,先来我们自己的服务器

    https://passport.csdn.net/v1/register/authorization?authType=qq /sina

    在这里插入图片描述
    B、命令浏览器重定向到用户授权页

    在这里插入图片描述

    用户确认授权
    https://graph.qq.com/oauth2.0/authorize

    C、qq 返回的响应,会命令用户重定向到指定位置

    D、服务器的这个位置就可以收到我们的code 码收到 code 码,服务器自己用 code 交换 access_token 令牌,并获取到用户的信息。给浏览器只给用户的信息即可;

    access_token=UUID
    

    浏览器访问带 UUID_token 而不是 access_token

    四、JWT

    1. 简介

    JWT,全称是 Json Web Token, 是 JSON 风格轻量级的授权和身份认证规范,可实现无状态、分布式的 Web 应用授权。
    官网:https://jwt.io

    GitHub 上 JWT 的 java 客户端:https://github.com/jwtk/jjwt

    我们最终可以利用 jwt 实现无状态登录

    2. 数据格式

    JWT 包含三部分数据:

    • Header:头部,通常头部有两部分信息:
    • token 类型:JWT
    • 加密方式:base64(HS256)
    • Payload:载荷,就是有效数据,一般包含下面信息:
    • 用户身份信息(注意,这里因为采用 base64 编码,可解码,因此不要存放敏感信息)
    • 注册声明:如 token 的签发时间,过期时间,签发人等
      这部分也会采用 base64 编码,得到第二部分数据
    • Signature:签名,是整个数据的认证信息。根据前两步的数据,再加上指定的密钥(secret)(不要泄漏,最好周期性更换),通过 base64 编码生成。用于验证整个数据完整和可靠性

    在这里插入图片描述

    3. 交互流程

    在这里插入图片描述
    步骤:

    • 用户登录
    • 服务的认证,通过后根据 secret 生成 token
    • 将生成的 token 返回给浏览器
    • 用户每次请求携带 token
    • 服务端利用秘钥解读 JWT 签名,判断签名有效后,从 Payload 中获取用户信息
    • 处理请求,返回响应结果

    因为 JWT 签发的 token 中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,完全符合了 Rest 的无状态规范。

    4. 授权中心流程

    在这里插入图片描述

    5. JWT 优势

    • 易于水平扩展
      • 在 cookie-session 方案中,cookie 内仅包含一个 session 标识符,而诸如用户信息、授权列表等都保存在服务端的 session 中。如果把 session 中的认证信息都保存在JWT 中,在服务端就没有 session 存在的必要了。当服务端水平扩展的时候,就不用处理 session 复制(session replication)/ session 黏连(sticky session)或是引入外部 session 存储了[实际上 spring-session 和 hazelcast 能完美解决这个问题]。
    • 防护 CSRF (跨站请求伪造) 攻击
      • 访问某个网站会携带这个域名下的 cookie。所以可能导致攻击。但是我们可以把 JWT 放在请求头中发送。
      • JWT 放在请求头中,就必须把 JWT 保存在 cookie 或者 localStorage 中。保存这里 js 就会读写,又会导致 xss 攻击。可以设置 cookie,httponly=true 来防止xss
    • 安全
      • 只是 base64 编码了,cookie+session 直接将数据保存在服务端,看都看不见,请问哪个更安全?

    6. 使用 JWT 带来的问题

    • 我们不建议使用 JWT + cookie 代替 session+cookie 机制,JWT 更适合 restful api
    • JWT token 泄露了怎么办?
      • 这个问题可以不考虑,因为 session+cookie 同样泄露了 cookie 的jsessionid 也会有这个问题
      • 我们可以遵循以下规范减少风险
        • 使用 https 加密应用
        • 返 回 JWT 给 客 户 端 时 设 置 httpOnly=true 并 且使用cookie 而不是LocalStorage 存储 JWT,防止 XSS 攻击和 CSRF 攻击
    • secret 如果泄露会导致大面积风险
      • 定期更新
      • Secret 设计可以和用户关联起来,每个用户不一样。防止全用一个secret
    • 注销和修改密码
      • 传统的 session+cookie 方案用户点击注销,服务端清空 session 即可,因为状态保存在服务端。我们不害怕注销后的假登录
      • JWT 会有问题。用户如果注销了或者修改密码了。恶意用户还使用之前非法盗取来的 token,可以在不重新登录的情况下继续使用
        • 可以按程度使用如下设计,减少一定的风险
          • 清空客户端的 cookie,这样用户访问时就不会携带 JWT,服务端就认为用户需要重新登录。这是一个典型的假注销,对于用户表现出退出的行为,实际上这个时候携带对应的 JWT 依旧可以访问系统。
          • 清空或修改服务端的用户对应的 secret,这样在用户注销后,JWT 本身不变,但是由于 secret 不存在或改变,则无法完成校验。这也是为什么将secret 设计成和用户相关的原因
          • 借助第三方存储,管理 JWT 的状态,可以以 JWT 为key,去redis 校验存在性。但这样,就把无状态的 JWT 硬生生变成了有状态了,违背了 JWT 的初衷。实际上这个方案和 session 都差不多了。
          • 修改密码则略微有些不同,假设号被到了,修改密码 (是用户密码,不是 JWT 的 secret) 之后,盗号者在原 JWT 有效期之内依旧可以继续访问系统,所以仅仅清空 cookie 自然是不够的,这时,需要强制性的修改secret
    • 续签问题
      • 传统的 cookie 续签方案一般都是框架自带的,session 有效期 30 分钟,30分钟内如果有访问,session 有效期被刷新至 30 分钟。而 JWT 本身的 payload 之中也有一个 exp 过期时间参数,来代表一个 JWT 的时效性,而 JWT 想延期这个 exp 就有点身不由己了,因为 payload 是参与签名的,一旦过期时间被修改,整个 JWT 串就变了,JWT 的特性天然不支持续签!
      • 可如下解决,但都不是完美方案
        • 每次请求刷新 JWT:简单暴力,性能低下,浪费资源。
        • 只要快要过期的时候刷新 JWT:JWT 最后的几分钟,换新一下。但是如果用户连续操作了 27 分钟,只有最后的 3 分钟没有操作,导致未刷新 JWT,就很难受。
        • 完 善 refreshToken : 借 鉴 oauth2 的 设 计 ,返回给客户端一个refreshToken,允许客户端主动刷新 JWT。这样做,还不如用 oauth2
        • 使用 redis 记录独立的过期时间:JWT 作为 key,在 redis 中保存过期时间,每次使用在 redis 中续期,如果 redis 没有就认为过期。但是这样做,还不如用 session+cookie
    • 总结
      • 在 Web 应用中,别再把 JWT 当做 session 使用,绝大多数情况下,传统的 cookie-session 机制工作得更好
      • JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态。
  • 相关阅读:
    JavaSE学习之--继承和多态
    【Linux】关于进程的理解、状态、优先级和进程切换
    有关栈和队列的三道OJ题【C语言】
    TX Text Control .NET Server for ASP.NET 31.0
    【LeetCode】136. 只出现一次的数字
    【Java】抽象类案例
    算法:(六)栈
    关于 360 评估你可能不知道的事
    02 | Spring Data Common 之 Repository 如何全面掌握?
    【Java】继承练习
  • 原文地址:https://blog.csdn.net/m0_51111980/article/details/126924615