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

目录

  • 一、SSO 介绍
    • 1. 单点登录业务介绍
    • 2. 什么是跨域 Web SSO
    • 3. 浏览器读写 cookie 的安全性限制
    • 4. http 协议是无状态协议。
  • 二、单点登录框架
  • 三、登录接入方式
    • 1. Cookie 接入方式
    • 2. Token 接入方式
    • 3. 有状态登录
    • 4. 无状态登录
    • 5. 集成社交登陆
  • 四、JWT
    • 1. 简介
    • 2. 数据格式
    • 3. 交互流程
    • 4. 授权中心流程
    • 5. JWT 优势
    • 6. 使用 JWT 带来的问题


一、SSO 介绍

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

1. 单点登录业务介绍

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

谷粒商城 高级篇 (十七) --------- 单点登录_第1张图片

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

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

谷粒商城 高级篇 (十七) --------- 单点登录_第2张图片

解决 :

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

缺点:

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

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 单点登录项目进行使用

谷粒商城 高级篇 (十七) --------- 单点登录_第3张图片

谷粒商城 高级篇 (十七) --------- 单点登录_第4张图片
谷粒商城 高级篇 (十七) --------- 单点登录_第5张图片
修改登录认证服务器的配置文件

谷粒商城 高级篇 (十七) --------- 单点登录_第6张图片

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

谷粒商城 高级篇 (十七) --------- 单点登录_第7张图片
启动之前将 应用打包。。。

谷粒商城 高级篇 (十七) --------- 单点登录_第8张图片

打完包之后生成 jar 包,用 java -jar 运行即可。。
谷粒商城 高级篇 (十七) --------- 单点登录_第9张图片
谷粒商城 高级篇 (十七) --------- 单点登录_第10张图片
访问服务器

谷粒商城 高级篇 (十七) --------- 单点登录_第11张图片
接着启动客户端,两个客户端分别以 8081 与 8082 端口启动。。。

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

谷粒商城 高级篇 (十七) --------- 单点登录_第12张图片

谷粒商城 高级篇 (十七) --------- 单点登录_第13张图片
修改完之后要重新打包。。。

谷粒商城 高级篇 (十七) --------- 单点登录_第14张图片

启动客户端1。。。

谷粒商城 高级篇 (十七) --------- 单点登录_第15张图片
然后以 8082 端口启动客户端2

谷粒商城 高级篇 (十七) --------- 单点登录_第16张图片

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

谷粒商城 高级篇 (十七) --------- 单点登录_第17张图片
谷粒商城 高级篇 (十七) --------- 单点登录_第18张图片

三、登录接入方式

1. Cookie 接入方式

谷粒商城 高级篇 (十七) --------- 单点登录_第19张图片

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

谷粒商城 高级篇 (十七) --------- 单点登录_第20张图片
B、命令浏览器重定向到用户授权页

谷粒商城 高级篇 (十七) --------- 单点登录_第21张图片

用户确认授权
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 编码生成。用于验证整个数据完整和可靠性

谷粒商城 高级篇 (十七) --------- 单点登录_第22张图片

3. 交互流程

谷粒商城 高级篇 (十七) --------- 单点登录_第23张图片
步骤:

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

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

4. 授权中心流程

谷粒商城 高级篇 (十七) --------- 单点登录_第24张图片

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,真正实现无状态。

你可能感兴趣的:(微服务项目,服务器,网络,运维)