单点登录SSO(Single Sign On)

本文探讨了Session跨域问题及解决方案,介绍了Token机制的工作原理及其优势,并详细讲解了多种实现Session跨域共享的方法,包括Nginx Session共享、Spring Session共享等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一、什么是Session 跨域问题

session跨域就是不同的域名下,Session无法共享。即设定用户在www.a.com登录,后端在Session中放入了用户的username=zhangsan,用户从www.a.com跳转到www.b.com,无法获取到Session中的用户信息。
什么是跨域: 客户端请求的时候,请求的服务器,不是同一个 IP,端口,域名,主机名
的时候,都称为跨域。
什么是域:在应用模型,一个完整的,有独立访问路径的功能集合称为一个域。如:百
度称为一个应用或系统。百度下有若干的域,如:搜索引擎(www.baidu.com),百度贴吧
(tie.baidu.com),百度知道(zhidao.baidu.com),百度地图(map.baidu.com)等。域信息,
有时也称为多级域名。域的划分: 以 IP,端口,域名,主机名为标准,实现划分。

二、Token 机制

1、传统身份认证

HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用。这里我们把用户看
成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户端再发送请求
时候,还得再验证一下。
解决的方法就是,当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,
这个记录里可以说明一下登录的用户是谁,然后把这条记录的 ID 号发送给客户端,客户端
收到以后把这个 ID 号存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,可以
带着这个 Cookie ,这样服务端会验证一个这个 Cookie 里的信息,看看能不能在服务端这
里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给
客户端。
上面说的就是 Session,我们需要在服务端存储为登录的用户生成的 Session ,这些
Session 可能会存储在内存,磁盘,或者数据库里。我们可能需要在服务端定期的去清理过
期的 Session 。
这种认证中出现的问题是:
Seesion:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来
越多的用户发请求时,内存的开销也会不断增加。
可扩展性:在服务端的内存中使用 Seesion 存储登录信息,伴随而来的是可扩展性问题。
CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会
是一个让人头疼的问题。在使用 Ajax 抓取另一个域的资源,就可以会出现禁止请求的情况。
CSRF(跨站请求伪造):用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,
并且能够被利用其访问其他的网站。
在这些问题中,可扩展性是最突出的。因此我们有必要去寻求一种更有行之有效的方法。

2、Token 身份认证

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程
是这样的:
客户端使用用户名、密码请求登录
服务端收到请求,去验证用户名、密码
验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户
端返回请求的数据
使用 Token 验证的优势:
无状态、可扩展
在客户端存储的 Tokens 是无状态的,并且能够被扩展。基于这种无状态和不存储 Session
信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。
安全性
请求中发送 token 而不再是发送 cookie 能够防止 CSRF(跨站请求伪造)。即使在客户端使
用 cookie 存储 token,cookie 也仅仅是一个存储机制而不是用于认证。不将信息存储在 Session
中,让我们少了对 session 操作

三、Session跨域共享实现方案

所谓 Session 跨域就是摒弃了系统(Tomcat)提供的 Session,而使用自定义的类似 Session
的机制来保存客户端数据的一种解决方案。
如:通过设置 cookie 的 domain 来实现 cookie 的跨域传递。在 cookie 中传递一个自定义
的 session_id。这个 session_id 是客户端的唯一标记。将这个标记作为 key,将客户端需要保
存的数据作为 value,在服务端进行保存(数据库保存或 NoSQL 保存)。这种机制就是 Session
的跨域解决。

1、Nginx Session共享

nginx 中的 ip_hash 技术能够将某个 ip 的请求定向到同一台后端,这样一来这个 ip 下的
某个客户端和某个后端就能建立起稳固的 session,ip_hash 是在 upstream 配置中定义的,具
体如下:

http {
    #在http字段添加
    upstream servers.mydomain.com {
        server 192.168.223.129:15672; 
        server 192.168.223.130:15672; 
        server 192.168.223.131:15672; 
        ip_hash;  #不加这句就不能保持会话
    }
    server{ 
        listen 80; 
        location / { 
            proxy_pass http://servers.mydomain.com; 

			proxy_set_header Host $http_host;
			proxy_set_header Cookie $http_cookie;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header X-Forwarded-Proto $scheme;
			client_max_body_size 100m
        } 
    } 
}

问题:

1)、当某个时刻来自某个IP地址的请求特别多,那么将导致某台后端服务器的压力可能非常大,而其他后端服务器却空闲的不均衡情况,违背了负载均衡的宗旨!

2)、客户端请求的ip是动态的,对应的后端服务器出现故障。这些都使ip_hash技术变得不可用

3)、nginx 不是最前端的服务器。
ip_hash 要求 nginx 一定是最前端的服务器,否则 nginx 得不到正确 ip,就不能根据 ip
作 hash。譬如使用的是 squid 为最前端,那么 nginx 取 ip 时只能得到 squid 的服务器 ip 地址,
用这个地址来作分流是肯定错乱的。
nginx 的后端还有其它方式的负载均衡。
假如 nginx 后端又有其它负载均衡,将请求又通过另外的方式分流了,那么某个客户端
的请求肯定不能定位到同一台 session

2、Spring Session 共享

spring-session 技术是 spring 提供的用于处理集群会话共享的解决方案。spring-session
技术是将用户 session 数据保存到三方存储容器中,如:mysql,redis 等。
Spring-session 技术是解决同域名下的多服务器集群 session 共享问题的。不能解决跨域
session 共享问题。
使用: 配置一个 Spring 提供的 Filter,实现数据的拦截保存,并转换为 spring-session
需 要 的 会 话 对 象 。 必 须 提 供 一 个 数 据 库 的 表 格 信 息 ( 由 spring-session 提 供 , 找
spring-session-jdbc.jar/org/springframework/session/jdbc/*.sql,根据具体的数据库找对应的
SQL 文件,做表格的创建)。
spring-session 表:保存客户端 session 对象的表格。
spring-session-attributes 表:保存客户端 session 中的 attributes 属性数据的表格。
spring-session 框架,是结合 Servlet 技术中的 HTTPSession 完成的会话共享机制。在代码
中是直接操作 HttpSession
在这里插入图片描述

3、自定义cookie——token + redis

Token实质上是服务端给客户端的一个字符串,上面包含着一些验证信息,相当于一个身份令牌;而通过客户端Cookie机制你每次访问的时候都会拿着这个令牌访问服务,通过与redis中存储的登录用户信息对比,从而完成身份校验。因为登录用户信息是集中保存在共享的内存服务器中的,所以所有的“域”都可以使用,从而达到跨域的目的!
实际生产中如果采用这种模式,其他项目这个地方需要使用MVC拦截器统一进行登录校验,登录成功后需要处理跳转回原先的访问地址 繁琐,复杂,耦合度高,不可靠(万一设备不支持cookie呢?)并且每次访问都要默认携带cookie信息(万一cookie信息被篡改了呢?),这样会增加带宽的压力

@Controller
@RequestMapping("/user")
public class UserController {
 
    @Autowired
    private UserService userService;
 
    @Autowired
    private TokenService tokenService;
 
    @RequestMapping("/login")
    @ResponseBody
    public String login(String username, String password
            , HttpServletRequest request , HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length != 0){
            for (Cookie cookie : cookies) {
                if(cookie.getName().equals("token")){
                    String token = tokenService.get(cookie.getValue());
                    if (token != null){
                        return "已登录";
                    }
                }
            }
        }
        //先验证用户是否存在
        User user = this.userService.login(username, password);
        if (user != null) {
            //如果存在,根据request请求生成token码
            String userAgent = request.getHeader("user-agent");
            String token = this.tokenService.generateToken(userAgent, username);
            //将token码保存到redis中
            this.tokenService.save(token, user);
            Cookie cookie = new Cookie("token", token);
            cookie.setMaxAge(60*60*24*30);//设置cookie有效期30天(这只会对手机有效,当然你也可以区别设置)
            response.addCookie(cookie); //保存cookie.setAttribute("token",token);
        } else {
            return "登录失败!";
        }
        return "登录成功!";
    }
}

4、自定义cookie——JWT生成token保存在客户端

5、Spring Security Oauth2

OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本。OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(authorization layer)。“客户端”不能直接登录“服务提供商”,只能登录授权层,以此将服务提供商与客户端分离。“客户端”登录需要获取OAuth提供的令牌,否则将提示认证失败而导致客户端无法访问服务。

四、JSON Web Token(JWT)机制

JWT 是一种紧凑且自包含的,用于在多方传递 JSON 对象的技术。传递的数据可以使用
数字签名增加其安全行。可以使用 HMAC 加密算法或 RSA 公钥/私钥加密方式。
紧凑:数据小,可以通过 URL,POST 参数,请求头发送。且数据小代表传输速度快。
自包含:使用 payload 数据块记录用户必要且不隐私的数据,可以有效的减少数据库访
问次数,提高代码性能。
JWT 一般用于处理用户身份验证或数据信息交换。
用户身份验证:一旦用户登录,每个后续请求都将包含 JWT,允许用户访问该令牌允许
的路由,服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并
且能够轻松地跨不同域使用。
数据信息交换:JWT 是一种非常方便的多方传递数据的载体,因为其可以使用数据前面
来保证数据的有效性和安全性。

1、JWT 数据结构

JWT 的数据结构是 : A.B.C。 由字符点‘.’来分隔三部分数据。
A - header 头信息
B - payload (有效荷载?)
C - Signature 签名

①header

数据结构: {“alg”: “加密算法名称”, “typ” : “JWT”}
alg 是加密算法定义内容,如:HMAC SHA256 或 RSA
typ 是 token 类型,这里固定为 JWT。

② payload

在 payload 数据块中一般用于记录实体(通常为用户信息)或其他数据的。主要分为三
个部分,分别是:已注册信息(registered claims),公开数据(public claims),私有数据(private
claims)。
payload 中常用信息有:iss(发行者),exp(到期时间),sub(主题),aud(受众)等。
前面列举的都是已注册信息。
公开数据部分一般都会在 JWT 注册表中增加定义。避免和已注册信息冲突。
公开数据和私有数据可以由程序员任意定义。
注意:即使 JWT 有签名加密机制,但是 payload 内容都是明文记录,除非记录的是加 密数据,否则不排除泄露隐私数据的可能。不推荐在 payload 中记录任何敏感数据。

③Signature

签名信息。这是一个由开发者提供的信息。是服务器验证的传递的数据是否有效安全的
标准。在生成 JWT 最终数据的之前。先使用 header 中定义的加密算法,将 header 和 payload
进行加密,并使用点进行连接。如:加密后的 head.加密后的 payload。再使用相同的加密算
法,对加密后的数据和签名信息进行加密。得到最终结果。

2、JWT 执行流程

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

五、单点登录(SSO)

单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分。

1、SSO认证流程

在这里插入图片描述

2、HTTP协议

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

3、cookie

  • cookie中默认的sessionId有效期为当前会话(内存级保存)浏览器第一次请求服务器时,web容器会生成一个sessionId,并返回给浏览器,这个sessionId会被保存在浏览器的会话cookie中。在浏览器不关闭的情况下,之后的每次请求请求头都会携带这个sessionId到服务器。session在服务器的默认有效时间是30分钟,可以通过3种方式去设置session的过期时间(具体那三种可以百度),下面通过代码的方式设置session过期时间为180秒request.getSession().setMaxInactiveInterval(30*6);

  • 在单点登录中cookie作为SSO令牌的载体

  • 决定cookie内容,生命周期和有效范围的7个属性:
    name
    value
    expires
    path
    domain
    httpOnly
    secure

  • cookie不要存放敏感信息

  • 浏览器对cookie有大小限制

  • 浏览器对cookie有个数限制

  • 过多过大的cookie浪费网络流量

写cookie

Cookie cookie = new Cookie("key","value");
cookie.setHttpOnly(false);//javaScript不能处理
cookie.setMaxAge(24*60*60);//24小时后过期
cookie.setPath("/");//cookie可用的路径
cookie.setDomain("www.a.b");//cookie可用的域
cookie.setSecure(false);//如果为true则仅支持HTTPS协议
response.addCookie(cookie);

读cookie

Cookie[] cookies = request.getcookies();

单点登录实现方式:

1、基于 JWT 机制的单点登录

注意: 使用 JWT 实现单点登录时,需要注意 token 时效性。token 是保存在客户端的令牌数据, 如果永久有效,则有被劫持的可能。token 在设计的时候,可以考虑一次性有效或一段时间 内有效。如果设置有效时长,则需要考虑是否需要刷新 token 有效期

1)token 保存位置

使用 JWT 技术生成的 token,客户端在保存的时候可以考虑 cookie 或 localStorage。cookie
保存方式,可以实现跨域传递数据。localStorage 是域私有的本地存储,无法实现跨域。

2)webstorage

webstorage 可保存的数据容量为 5M。且只能存储字符串数据。
webstorage 分为 localStorage 和 sessionStorage。
localStorage 的生命周期是永久的,关闭页面或浏览器之后 localStorage 中的数据也不会
消失。localStorage 除非主动删除数据,否则数据永远不会消失。
sessionStorage 是会话相关的本地存储单元,生命周期是在仅在当前会话下有效。
sessionStorage 引入了一个“浏览器窗口”的概念,sessionStorage 是在同源的窗口中始终存在
的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然
存在。但是 sessionStorage 在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口
同一个页面,sessionStorage 也是不一样的。

2、CAS

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点:

  • 开源的企业级单点登录解决方案。
  • CAS Server 为需要独立部署的 Web 应用。
  • CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

1)搭建客户端系统

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值