yunnick

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

SSO单点登录对比分析

 

引言:

单点登录的英文名叫做:Single Sign On(简称SSO)。

指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的系统,实现多个系统,统一登陆。

本文主要介绍了常见单点登录流程,并对三种登录授权方式原理做了分析比较:cookie,cas,oauth2。

 

可以将单点登录简单分为:同根域单点登录不同根域下单点登录

 

一、同根域Cookie登录

Cookie和Session是相对的概念,都是基于key-value结构存储数据,不同是一个用在客户端一个用在服务端。

Cookie可以应用在很多场景,如一些内网后台管理系统、Portal门户、SSO统一登录、追踪用户的身份、根据特定用户进行推送或推荐等…

默认情况下,Cookie只能在同一域名下使用,通过设置domain为根域名,则Cookie可以实现同根域下共享,同根域名下基于cookie就可以方便实现SSO登录;在某些约束下,也可以实现跨域的cookie共享。

 

1.1 同根域下单点登录:

一般一个企业只会有一个主域名,通过二级域名区分子系统。举个例子:一个企业中有三个子系统(app1.domain.com、app2.domain.com、sso.domain.com),根域名同为domain.com。

登录过程:登录sso.domain.com后,通过http response写入cookie信息,包含了登录的sessionId等信息,domain设置为根域 domain.com;用户访问app1和app2时服务器就可以获取到sessionId,验证用户为登录状态,用户在浏览器上就可以正常获取app1和app2的资源。

简单易用缺点也比较明显:

cookie中如果存放了用户的敏感信息,一旦被窃取(用户的Cookie可能会由于网站的XSS漏洞而被盗取),这个负面影响大。

如果大量的用户信息放入cookie,对浏览器端压力较大,同时cookie存储的信息量大小有限不够轻量级,跨域情况下将会带来一定的麻烦。

 

1.2 cookie共享代码实例

搭建了基于SpringBoot的HTTPS服务,使用keytools生成了证书,SSL配置:

 

server:
port: 9043
ssl:
key-store: classpath:ssLserver.jks
key-store-password: '@lmggTy6XNZmJwu7'
key-store-type: JKS

 

为了消除浏览器安全提示,需要将证书导入信任证书列表中。

并修改host文件,配置域名映射,均指向本机,类似:

 

127.0.0.1       app1.domain.com
127.0.0.1 app2.domain.com

 

关于Cookie共享的代码验证,分5个场景:

(1)  设置默认cookie

请求: https://app1.domain.com:9043/setDefaultCookie?name=defaultCk&value=ckValue

 

ResponseCookie cookie = ResponseCookie.from(name, value)
.httpOnly(true)//httpOnly 防止XSS漏洞js读取cookie
.secure(true)
.path("/")
.maxAge(3600)
.build();
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());

 

(2)  设置根域名cookie: domain.com

请求:https://app1.domain.com:9043/setRootCookie?name=rootCk&value=rtCkValue

 

ResponseCookie cookie = ResponseCookie.from(name, value)
.httpOnly(true)
.secure(true)
.domain("domain.com")
.path("/")
.maxAge(3600)
.build();
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());

 

访问https://app2.domain.com:9043/printCookie 可发送根域名的cookie

 

(3)  不同子域名cookie:app2.domain.com

请求:https://app1.domain.com:9043/setOtherDomainSameRootCookie?name=sameRoot&value=Val

 

        ResponseCookie cookie = ResponseCookie.from(name, value)
.httpOnly(true)
.secure(true)
.domain("app2.domain.com")
.path("/")
.maxAge(3600)
.build();
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());

 

(4)  设置不同根域名cookie: app1.host.com

请求:https://app1.domain.com:9043/setOtherDomainDiffRootCookie?name=diffRoot&value=val

 

domain("app1.host.com")

 

(5)  设置跨域共享cookie: app1.host.com

请求:https://app1.domain.com:9043/setNoneSameSite?name=noneSameSiteCk&value=val

 

ResponseCookie cookie = ResponseCookie.from(name, value)
.httpOnly(true)
.secure(true)
.path("/")
.maxAge(3600)
.sameSite("None")
.build();
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());

 

跨域获取:https://app2.domain.com:9043/printCookie 是无法获取cookie的。实际上完整描述应该是“跨域请求获取”。

 

跨域请求获取又可以细分为:基于Form的表单请求、Ajax跨域请求

Form表单跨域请求:

某高仿APP1提供了一个页面https://app1.host.com:9043/indexPage 如下:

 

<form id="fm_crossDomainForm" method="post">
</form>
<div >
<a href="javascript:void(0);" onclick="formSubmit()">Form提交</a>
</div>
<script>
function formSubmit() {
$('#fm_crossDomainForm').form('submit', {
url:"https://app1.domain.com:9043/printCookies"
});
}
</script>

 

用户访问后就会将第(5)种cookie提交上去,这就是一种CSRF攻击。

可以采取以下措施防范防范措施:

✓ 搭建HTTPS协议安全网站,cookie不要设置为 sameSite:none

✓ 短信验证码

:sameSite: https://blog.csdn.net/qq_36690992/article/details/117965822

 

Ajax跨域请求:CORS

跨域请求服务端需要在响应头添加

 

response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));

 

浏览器Ajax跨域请求

 

$.ajax({
url: "https://app1.domain.com:9043/printCookie",
contentType: 'application/x-www-form-urlencoded',
type: 'POST',
async: false,
xhrFields: {
withCredentials: true
},
success: function (result) {
console.log(result);;
},
error: function (data) {
console.log(data);
}
});

 

Ajax请求默认是不会传任何cookie的。加上代码第6、7行参数配置,才开启了浏览器上传cookie,同时服务端配置响应头

 

response.setHeader("Access-Control-Allow-Credentials", "true");

 

参考:https://blog.csdn.net/qq_33479841/article/details/123364237

 

二、不同根域下CAS单点登录

基于浏览器的安全机制, 不同的域名之间无法实现cookie共享。针对这种情况要实现单点登录,需要借鉴CAS(Central Authentication Service)的设计思想。CAS的设计思路主要如下图所示:

 

图 CAS时序图 https://apereo.github.io/cas/6.6.x/protocol/CAS-Protocol.html

 

示例:阿里云sso路径

 

https://login.alibaba-inc.com/ssoLogin.htm?
BACK_URL=https%3A%2F%2Fmail.alibaba-inc.com%2Falimail%2F
&preLoginKey=gQksImpGUd1666162247475pCPJHRnhCV
&CONTEXT_PATH=%2Falimail%2F
&APP_NAME=webmail

 

 

 

三、基于oauth2的认证

Oauth2是一种授权框架/协议,用来授权给第三方应用,获取用户数据(在不知道用户名、密码的前提下)。用于APP或网页应用微信登录、 QQ 登录、微博登录。第三方网站上授权获得用户信息之后,进行绑定等操作。

 

OAuth2.0 协议的授权流程可以参考下面的流程图,共四个角色,其中 Client 指第三方应用,Resource Owner 指用户,Authorization Server 是开放平台的授权服务器,Resource Server 是开放平台的 API 服务器(用户数据)。

图 抽象协议流

 

图 获取token及认证过程

 

强调local state的概念,这个字段是在登录成功后获取Authorization Code时传给AS的,是什么作用呢

以绑定两个账号为例:攻击者A获取自己的Authorization Code,制造跳转连接到APP服务,用户B在不知情的情况下,在自己浏览器上使用该连接发起了绑定请求(GET方式),最终绑定了A的账号。

 

要防止这样的攻击其实很容易,作为第三方应用的开发者,只需在OAuth认证过程中加入state参数,并验证它的参数值即可。具体细节如下:
 
在将用户重定向到OAuth2的Authorization Endpoint去的时候,为用户生成一个随机的字符串,并作为state参数加入到URL中。
在收到OAuth2服务提供者返回的Authorization Code请求的时候,验证接收到的state参数值。如果是正确合法的请求,那么此时接受到的参数值应该和上一步提到的为该用户生成的state参数值完全一致,否则就是异常请求。
state参数值需要具备下面几个特性:
1.不可预测性:足够的随机,使得攻击者难以猜到正确的参数值
2.关联性:state参数值和当前用户会话(user session)是相互关联的
3.唯一性:每个用户,甚至每次请求生成的state参数值都是唯一的
4.时效性:state参数一旦被使用则立即失效
 
链接:https://www.jianshu.com/p/c7c8f51713b6
 

 

 

OAuth考虑了各种Web攻击,比如CSRF (Cross-Site Request Forgery), Clickjacking等,OAuth协议文档中已经有了比较全面的阐述

参考:https://www.rfc-editor.org/rfc/rfc6749

https://www.jianshu.com/p/c7c8f51713b6

 

四、CAS与OAuth2区别:

● CAS 客户端要获取的最终信息是,这个用户到底有没有权限访问我(CAS 客户端)的资源;

● OAuth2 获取的最终信息是,我(oauth2 服务提供方)的用户的资源到底能不能让你(oauth2 的客户端)访问。 存在资源的转移使用。

● CAS感觉就是把oauth2的Client和RS合并到了一起(很微妙也很关键的区别)。

总结:CAS是"直接认证",OAuth2是"授权访问",前者更简单直接,后者更灵活安全。

 

思考:

如何登出

 

附:

实现SSO常见的几个协议:CAS, SAML、OpenID, OAuth2(开放授权)

一些开源框架:

Apereo CAS https://www.apereo.org/projects/cas

keycloak https://www.keycloak.org/

云平台:

阿里云SSO https://help.aliyun.com/document_detail/93684.html

posted on 2026-08-14 16:06  yunnick  阅读(6)  评论(0)    收藏  举报