type
status
date
slug
summary
tags
category
icon
password
什么是SAML
SAML全称安全断言标记语言(Security Assertion Markup Language),它是一种基于XML的开放标准,用于表示和交换用户属性、身份和认证信息。在对用户进行一次身份验证的基础上,将验证结果传递给多个应用程序,达到单点登录SSO的效果。
SAML是一种身份验证,并不包括用户授权认证的过程。身份验证用于确认用户的身份,类似于身份证,用来展示一个人的信息并证明“我是我“。而授权则是确认用户的权限,表明用户他能进行哪些操作
SAML是如何运行的
三个参与方:
1. 用户
2. 身份提供方(IDP):是一种存储和管理用户数字身份的服务,IDP 可以通过验证账号密码等方式来检查用户身份,并提供用户身份列表供其他服务提供商(如 SSO)检查。
3. 服务提供方(SP):用户使用的相关服务,如B站、淘宝、飞书都是服务的提供方.

如上图所示,处理流程:
- 用户访问SP提供的服务页面,并进行SSO登录
- SP通过重定向跳转到IDP服务
- IDP识别到用户未登录则会跳转到登录页面,用户进行登录操作
- IDP登录成功后,返回SAML Response断言信息
- SP获取到SAML断言后根据结果提供对应的访问策略 SAML断言包含了SP所需要的所有信息,包括断言的ID来源、签发时间、有效时间、SP的地址等信息。
在SP重定向到IDP时会发送一个AuthNRequest消息
可以看出,该XML里面存储了几个关键信息:
ID="ONELOGIN_809707f0030a5d00620c9d9df97f627afe9dcc24"- 唯一ID

IssueInstant="2014-07-16T23:52:45Z"- 签发时间
Destination="<http://idp.example.com/SSOService.php>"- IDP地址
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"- 使用的协议
AssertionConsumerServiceURL="<http://sp.example.com/demo1/index.php?acs>"- 回调SP的接口地址
<saml:Issuer><http://sp.example.com/demo1/metadata.php></saml:Issuer>- 发起方
IDP登录成功后就会返回响应的SAML Response
返回结果中也有一些关键信息:
InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"- 对应于SP发起请求的ID
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>- 响应结果
<saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">- 会话有效期
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">- 返回结果相关属性参数
整个流程图如下图所示:

上述用户以SP为入口是一种场景,还有一种场景是用户先登录IDP,然后在选择指定的SP应用,IDP再跳转到SP应用中去。

该场景处理流程:
- 用户访问IDP,进行登录;登录完成选择SP访问
- IDP重定向到SP,生成SAML Response,并调用SP 的ACS url 将结果返回
- SP验证SAML Response,并授权访问
代码实践
我是用go进行代码编辑和测试,使用https://samltest.id 这个网站进行测试,用到的三方库是
go get github.com/crewjam/saml服务启动后,首先访问 http://localhost:8001/saml/metadata 获取xml元数据,上传到https://samltest.id/upload.php ,再访问 http://localhost:8001/auth 就会跳转到samltest的登录页面。

选择页面下方任意一个提供的账号密码进行登录,登录成功后就会显示如下信息

通过Wireshake抓包,监听lo网卡,端口设置tcp.port == 8001,我们可以得到

可以看出,首先访问/auth地址时,我们启动的服务(SP)将请求重定向到了https://samltest.id/idp/profile/SAML2/Redirect/SSO, 然后我们输入账号密码验证成功后,IDP就回调了/saml/acs接口,发送了saml断言信息,我们服务根据信息再经过重定向到我们最开始访问的/auth接口,最终返回具体的信息。整个测试过程,我们启动的服务是作为服务提供方,https://samltest.id, 则是作为身份提供方,对身份进行验证,并返回验证结果和身份的相关属性。
对接Okta
为了更好的模拟真实的对接场景,我将通过与okta进行SSO集成对接进行演示。使用ruby进行测试。首先登录okta后台,选择Application --> Create App Integration创建应用,然后再选择SAML2.0;再输入应用名。

- Single sign on URL 填写的是SP的回调接口Audience URI 填写SP的metadata地址
- Attribute Statements可填写一些属性,如用户名,ID等信息。

应用创建完成后,再将该应用授权给指定用户,在Directory --> People --> Add person 创建用户,然后点击该用户,选择Assign Application选择授权的应用。


所有的步骤操作完成后,我们获取okta的saml 登录证书地址,选择Application,点击创建的应用,再选择Sign on,就可看到okta提供的证书,为了拿到其地址我们点击View Idp metadata,他就会回跳转到新的页面,页面地址就是我们需要的IDP metadata url,将其替换代码的相应位置即可。

接下来测试流程就和上面经过抓包,使用我们对相关请求使用工具(https://www.scottbrady91.com/tools/saml-parser) 进行解码处理得到:
这是在重定向时携带的SAML Request的信息,包含ID、发起时间、目标IDP地址,回调SP的地址和发起者。
这是okta登录成功后回调请求/saml/acs接口时发送的SAML Response,包含目的SP回调地址,该消息对应的请求ID,响应状态,用户属性等。
总结
SAML协议制定于2005年,所以在制定的时候主要针对Web应用程序,并没有考虑后来的App,在兼容性上不是那么好。特别是在2012后OAuth2的出现,越来越多的人在单点登录上的第一选择更多是OAuth2(后面也会写相关文章来介绍OAuth2)。但SAML通过HTTP Redirect 和Post 协议来传递身份验证消息,并使用Form格式进行数据提交,整个协议交互比较简单,方便使用集成,在今天还是有它的一席之地的。
参考文档
- 作者:Ryan Wu
- 链接:https://hexo.fridaylab.top/article/1b76dd5a-4608-4d60-a2a4-4bf65def9cc0
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章