SAML 模板使用指南
一、概述
IDaaS平台支持基于标准SAML协议的SSO(Single Sign On 单点登录),IDaaS作为SAML协议中的IDP(Identity Provider身份提供方)角色,提供用户的身份认证服务,用户可以登录一次就直接使用多个SP(Service Provider 业务提供方)的服务,免去了每个应用都要登录的烦恼。
二、IDP发起和SP发起
SAML(Security Assertion Markup Language 安全断言标记语言)是一个基于XML的开源标准数据格式,为在安全域间交换身份认证和授权数据,尤其是在IDP和SP之间。SAML是OASIS(Organization for the Advancement of Structured Information Standards 安全服务技术委员会)制定的标准,始于2001年,其最新主要版本SAML 2.0于2005年发布。
作为一种流行的SSO协议, SAML同时支持IDP发起和SP发起, 也就是可以在登录门户后,跳转到任意一个应用, 也可以从一个应用发起,跳转到IDP, 登录认证后,再跳转回这个应用, 继续SSO。 二者都是SSO, 流程的前半部分参数不同, 后半部分是很相似的。
2.1、SAML的流程
2.1.1、SP发起SSO
用户请求SP资源,SP生成SAML请求,IDP接收并解析SAML请求并进行用户认证后返回SAML响应,SP接收并解析SAML响应后,提起其中的令牌Assertion, 提供被请求的资源给用户使用。
具体流程如下:
2.1.1.1、用户请求目标资源
用户向SP请求目标资源,例如目标资源为:
https://sp.example.com/myresource
SP会进行安全检查,如果SP已经存在有效的IDP安全会话上下文,则认为已经登录过, 跳过步骤2~8。
2.1.1.2、重定向到IDP的SSO服务
SP会生成SAMLRequest,同时会把SP当前发起的URL生成一个随机数opaque, 临时存放, 同时把它作为RelayState,然后使用标准的HTTP 302重定向redirect到IDP的SSO服务,例如:
302 Redirect
Location: http://idp4/enduser/api/application/plugin_saml/
RelayState是SP的发起URL的不透明引用,SAMLRequest是Base64编码以后的<samlp:AuthnRequest>元素,<samlp:AuthnRequest>示例:
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="identifier_1"
Version="2.0"
IssueInstant="2004-12-05T09:21:59Z"
AssertionConsumerServiceIndex="0">
<saml:Issuer>https://sp.example.com/SAML2</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="true"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
</samlp:AuthnRequest>
如果需要的话,SAMLRequest还可以使用SigningKey进行签名。
2.1.1.3、浏览器转发SAML请求,重定向到IDP的SSO服务
浏览器将SP的SAMLRequest和RelayState通过一个GET请求转发到IDP的SSO服务:
GET /SAML2/SSO/Redirect?SAMLRequest=request&RelayState=opaque HTTP/1.1
Host: idp.example.org
2.1.1.4、IDP解析SAML请求
IDP解析SAML请求,通过Base64解码得到<samlp:AuthnRequest>元素。IDP会验证用户是否已经登录,如果已经登录则跳过步骤5。
2.1.1.5、认证用户
IDP认证用户身份,常用的方法是IDP返回登录页面给用户,IDP可以配置自己需要的认证方式, 比如用户使用账号和密码进行登录认证。
2.1.1.6、用户认证成功后返回SAML响应
IDP认证用户身份以后会返回SAMLResponse响应,响应中包含如下表单:
<form method="post" action="https://sp.example.com/SAML2/SSO/POST" ...>
<input type="hidden" name="SAMLResponse" value="response" />
<input type="hidden" name="RelayState" value="opaque" />
...
<input type="submit" value="Submit" />
</form>
表单中的RelayState参数值就是步骤2中生成的RelayState,IDP会将其原封不动的返回。表单中的SAMLResponse是Base64编码以后的<samlp:Response>元素,<samlp:Response>示例:
<samlp:Response
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="identifier_2"
InResponseTo="identifier_1"
Version="2.0"
IssueInstant="2004-12-05T09:22:05Z"
Destination="https://sp.example.com/SAML2/SSO/POST">
<saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
<samlp:Status>
<samlp:StatusCode
Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="identifier_3"
Version="2.0"
IssueInstant="2004-12-05T09:22:05Z">
<saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
<!-- a POSTed assertion MUST be signed -->
<ds:Signature
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature>
<saml:Subject>
<saml:NameID
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">
3f7b3dcf-1674-4ecd-92c8-1544f346baf8
</saml:NameID>
<saml:SubjectConfirmation
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData
InResponseTo="identifier_1"
Recipient="https://sp.example.com/SAML2/SSO/POST"
NotOnOrAfter="2004-12-05T09:27:05Z"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions
NotBefore="2004-12-05T09:17:05Z"
NotOnOrAfter="2004-12-05T09:27:05Z">
<saml:AudienceRestriction>
<saml:Audience>https://sp.example.com/SAML2</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement
AuthnInstant="2004-12-05T09:22:00Z"
SessionIndex="identifier_3">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
</saml:Assertion>
</samlp:Response>
这里重要的是Assertion部分, 包含有用户的Subject 身份信息。 默认一般用IDP的私钥对整个SAMLResponse 签名, 也可以是对Assertion 签名, 或是二者兼而有之, 取决于IDP和SP的协商。
2.1.1.7、浏览器将SAML响应转发到SP的ACS
浏览器将SAMLResponse和RelayState以POST的方式转发到SP的ACS URL, SP继续解析令牌。
POST /SAML2/SSO/POST HTTP/1.1
Host: sp.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: nnn
SAMLResponse=response&RelayState=opaque
2.1.1.8、SP解析验证SAML响应
SP处理SAMLResponse响应,Base64解码得到<samlp:Response>元素,最重要的是要用SP中的公钥, 来检查签名的合法性, 如果合法 ,则抽取其中包含的用户信息Subject,找到对应的SP应用子账户, 生成SP安全会话上下文。
2.1.1.9、用户获取目标资源
用户成功获取SP提供的目标资源。如果SP发现RelayState中有对应的URL, 则提取这个URL, 跳转到对应的URL。
2.1.2、IDP发起SSO
同上面的SP发起SSO不同, IDP发起可以实现用户登录IDP,在IDP中选择某个SP应用,IDP跳转到SP,用户使用SP的资源。
具体的流程如下:
2.1.2.1、用户访问IDP
用户打开IDP的登录页面。
2.1.2.2、用户登录IDP
使用配置好的如账号密码等方式登录到IDP。
2.1.2.3、用户选择需要的SP应用
用户在IDP中选择需要使用的SP应用, 背后会触发https://xxxx.login.aliyunidaas.com/api/bff/v1.2/enduser/portal/sso/go_0fbd26xxx?access_token=9a2e8d41-cde9-4ba9-b09b-yyyy, 继续流程。
2.1.2.4、IDP返回用户选择的SP应用的SAML响应
IDP生成用户选择的SP应用的SAMLResponse响应(前文已介绍),返回给用户的浏览器。
2.1.2.5、浏览器将SAML响应转发到SP的ACS
浏览器将SAMLResponse和RelayState以POST的方式转发到SP的ACS URL。
2.1.2.6、SP解析验证SAML响应
SP处理SAMLResponse响应,Base64解码得到<samlp:Response>元素,最重要的是要用SP中的公钥, 来检查签名的合法性, 如果合法 ,则抽取其中包含的用户信息Subject,找到对应的SP应用子账户, 生成SP安全会话上下文。
注: 可以看到, 这一步和SP发起中的第8步非常类似, 包括下一步。
2.1.2.7、用户获取目标资源
自此,SSO结束,用户成功获取SP提供的目标资源。如果SP发现RelayState中有对应的URL, 则提取这个URL, 跳转到对应的URL。
2.1.2.8、显示目标资源
用户看到对应的应用目标资源。
2.2、SAML的Metadata
SAML协议中规定,IDP或SP的配置信息通过元数据(Metadata)信息实现,配置过程只要交换IDP和SP的元数据配置信息就可以快速实现SSO配置。
2.2.1、IDP的Metadata
IDP的Metada是<md:EntityDescriptor>元素,示例如下:
<md:EntityDescriptor entityID="https://idp.example.org/SAML2" validUntil="2013-03-22T23:00:00Z"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- insert ds:Signature element (omitted) -->
<!-- insert md:IDPSSODescriptor element (below) -->
<md:Organization>
<md:OrganizationName xml:lang="en">Some Non-profit Organization of New York</md:OrganizationName>
<md:OrganizationDisplayName xml:lang="en">Some Non-profit Organization</md:OrganizationDisplayName>
<md:OrganizationURL xml:lang="en">https://www.example.org/</md:OrganizationURL>
</md:Organization>
<md:ContactPerson contactType="technical">
<md:SurName>SAML Technical Support</md:SurName>
<md:EmailAddress>mailto:saml-support@example.org</md:EmailAddress>
</md:ContactPerson>
</md:EntityDescriptor>
主要元素信息为:
标签 | 说明 |
entityID | IDP的唯一标识。 |
validUtil | 元数据的过期时间。 |
ds:Signature | 包含数字签名,以确保元数据的真实性和完整性。 |
md:Organization | 组织信息。 |
md:ContactPerson | 联系人信息。 |
IDP的SSO相关Metadata是<md:IDPSSODescriptor>元素,示例如下:
<md:IDPSSODescriptor
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo>...</ds:KeyInfo>
</md:KeyDescriptor>
<md:ArtifactResolutionService isDefault="true" index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="https://idp.example.org/SAML2/ArtifactResolution"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://idp.example.org/SAML2/SSO/Redirect"/>
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://idp.example.org/SAML2/SSO/POST"/>
<md:SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="https://idp.example.org/SAML2/Artifact"/>
<saml:Attribute
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"
FriendlyName="eduPersonAffiliation">
<saml:AttributeValue>member</saml:AttributeValue>
<saml:AttributeValue>student</saml:AttributeValue>
<saml:AttributeValue>faculty</saml:AttributeValue>
<saml:AttributeValue>employee</saml:AttributeValue>
<saml:AttributeValue>staff</saml:AttributeValue>
</saml:Attribute>
</md:IDPSSODescriptor>
主要元素信息为:
标签 | 说明 |
<md:KeyDescriptor use="signing"> | IDP配置的一个私有SAML签名密钥和/或一个私有后端通道TLS密钥。 |
<md:ArtifactResolutionService>下的Binding | SAML绑定信息。 |
<md:NameIDFormat> | SSO支持的SAML名称标识格式。 |
<md:SingleSignOnService> | 单点登录信息。 |
<saml:Attribute> | IDP提供的断言的属性。 |
2.2.2、SP的Metadata
SP的Metada是<md:EntityDescriptor>元素,示例如下:
<md:EntityDescriptor entityID="https://sp.example.com/SAML2" validUntil="2013-03-22T23:00:00Z"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- insert ds:Signature element (omitted) -->
<!-- insert md:SPSSODescriptor element (see below) -->
<md:Organization>
<md:OrganizationName xml:lang="en">Some Commercial Vendor of California</md:OrganizationName>
<md:OrganizationDisplayName xml:lang="en">Some Commercial Vendor</md:OrganizationDisplayName>
<md:OrganizationURL xml:lang="en">https://www.example.com/</md:OrganizationURL>
</md:Organization>
<md:ContactPerson contactType="technical">
<md:SurName>SAML Technical Support</md:SurName>
<md:EmailAddress>mailto:saml-support@example.com</md:EmailAddress>
</md:ContactPerson>
</md:EntityDescriptor>
主要元素信息为:
标签 | 说明 |
entityID | SP的唯一标识。 |
validUtil | 元数据的过期时间。 |
ds:Signature | 包含数字签名,以确保元数据的真实性和完整性。 |
md:Organization | 组织信息。 |
md:ContactPerson | 联系人信息。 |
SP的ACS相关Metadata是<md:SPSSODescriptor>元素,示例如下:
<md:SPSSODescriptor
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo>...</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo>...</ds:KeyInfo>
</md:KeyDescriptor>
<md:ArtifactResolutionService isDefault="true" index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="https://sp.example.com/SAML2/ArtifactResolution"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:AssertionConsumerService isDefault="true" index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://sp.example.com/SAML2/SSO/POST"/>
<md:AssertionConsumerService index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="https://sp.example.com/SAML2/Artifact"/>
<md:AttributeConsumingService isDefault="true" index="1">
<md:ServiceName xml:lang="en">Service Provider Portal</md:ServiceName>
<md:RequestedAttribute
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"
FriendlyName="eduPersonAffiliation">
</md:RequestedAttribute>
</md:AttributeConsumingService>
</md:SPSSODescriptor>
标签 | 说明 |
<md:KeyDescriptor use="signing"> | SP配置的一个私有SAML签名密钥和/或一个私有后端通道TLS密钥。 |
<md:KeyDescriptor use="encryption"> | SP公共SAML加密密钥。 |
<md:AssertionConsumerService>下的index | <samlp:AuthnRequest>元素中的AssertionConsumerServiceIndex属性的值。 |
<md:AssertionConsumerService>下的Binding | SAML的绑定信息。 |
<md:AttributeConsumingService> | IDP用来构造一个<saml:AttributeStatement>元素,该元素与Web浏览器SSO一起推送到SP。 |
<md:AttributeConsumingService>下的index | SP在SSO时生成<samlp:AuthnRequest>元素中AttributeConsumingServiceIndex属性的值。 |
三、IDaaS中配置SAML应用示例
IDaaS平台支持基于标准SAML协议的SSO,并提供来一系列的应用模版来方便配置,这里以阿里云RAM为例演示如何配置。
3.1、获取SP的元数据信息
SP会提供自己的元数据信息,以阿里云RAM为例,登录控制台后找到元数据URL。
浏览器访问该URL得到元数据信息,显示如下:
将上述内容可以导出, 放在下一步使用。
3.2、IDaaS中配置SP的元数据信息
3.2.1、添加SP应用
准备好后, 接下来, 在IDaaS 中添加一个RAM应用。
以IT管理员账号登录云盾IDaaS管理平台,具体操作请参考 IT管理员指南-登录 。
点击左侧导航栏 应用 > 添加应用
在右侧选择一个SAML应用,点击添加应用。IDaaS支持多种SAML应用,这里以添加阿里云RAM-用户SSO为例进行展示。
点击添加SigningKey按钮,输入名称等信息,系统会据此生成应用的证书,私钥保留在IDP,公钥导出到SP, 用于IDP和SP通信的签名验签。
如果没有现成的证书可以选择, 则填写以下信息生成一个,其中的名称信息最好是和这个应用比如RAM关联的, 方便将来识别。
无论是选择已有的还是刚添加的,找到对应的SigningKey,选择它。
接下来要填写更多的应用信息,名称等信息可以自定义,EntityId、ACS URL等信息从步骤1中的到的SP的元数据中复制过来,需要填写的主要信息如下:
参数名称 | 说明 |
应用名称 | 所添加应用的名称,可以为任意值,但最好和应用相关。 |
应用类型 | 引用的类型,只有选中的应用类型才会在用户对应客户端中显示。 |
IDaaS EntityId | 在IDaaS中设置的认证参数,需要将此参数配置到SP中,在IDaaS导出的 metadata 里可以获取,例如 https://signin.aliyun.com/117xxxxxxxxxxx63/saml/SSO。 |
SP Entity ID | 在SP中设置的Entity ID,需要复制到IDaaS的配置中, 可以在RAM的metadata中获取, 例如https://signin.aliyun.com/117yyyyyyyyyyy63/saml/SSO。 |
SP ACS URL(SSO Location) | 单点登录地址,这里以阿里云RAM为例:https://signin.aliyun.com/saml/SSO。 |
NameldFormat | 名称标识格式类型,这里以阿里云RAM为例,选择urn:oasis:names:tc:SAML:2.0:nameid-format:persistent。 |
填写完成后提交保存, 如果应用是禁用状态, 可以继续修改重新提交。
3.2.2、启用应用并且授权
应用配置好以后需要先启用应用,并且将服务授权给一个账户,点击左侧导航栏 应用 > 应用列表 启用该应用并授权给账户。
IDaaS支持多种方式进行授权,这里以按应用授权账户为例。
保存后, 这个用户登录就可以看到这个应用了。
3.2.3、IDP新建子账户(非必须步骤)
一个系统要SSO到另外一个系统,需要使用对方能够识别的子账号进行认证,往往登录到IDP的主账户和应用SP的子账户是不一样的,可以使用账号同步(两套系统中的账号信息相同)或者新建子账户进行账号映射的方法。账号映射是指给IDP的账户建立一个SP中已经存在的账户作为子账户,身份认证的时候通过子账户进行认证。例如SP系统中有个账户“demo”,我们想用IDP系统中的“zhangsan”账号SSO到SP,则需要给账号“zhangsan”新建一个对应的子账户“demo”。这里以阿里云RAM演示新建子账户的功能,如下图,阿里云RAM中有账户demo@117yyyyyyyyyyy63.onaliyun.com。
IDaaS中新建子账户有两种方式,操作如下:
3.2.3.1、授权账号新建子账户
登录授权账户,点击左侧导航栏 主导航 > 应用子账户 添加应用子账户功能中提交新建子账户申请。由于上一步阿里云RAM中的账户是demo@117yyyyyyyyyyy63.onaliyun.com,所以这里子账户的名称应该填demo。IDaaS在SSO的时候,会将子账户(demo)和步骤3.2.1中配置的阿里云个人域名(117yyyyyyyyyyy63.onaliyun.com)进行拼接映射到阿里云RAM的账户。
登录管理员账户,点击左侧导航栏 其它管理 > 审批中心 审核通过该应用子账户的添加。
3.2.3.2、管理员新建子账户
管理员新建子账户不需要审核过程,具体操作为:
登录管理员账户,点击左侧导航栏 应用 > 应用列表 找到添加的应用,点击详情中的查看应用子账户。
点击添加账户关联,添加子账户。
输入授权账户(主账户)和子账户,点击保存完成子账户添加。
3.3、SP中配置IDaaS的元数据信息
3.3.1、获取IDaaS的元数据信息
以IT管理员账号登录云盾IDaaS管理平台,点击左侧导航栏 应用 > 应用列表 选择刚才添加的应用,点击查看详情,如下图:
点击导出SAML元配置文件,将IDaaS的元数据文件保存到本地电脑。
IDaaS元配置文件示例如下:
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://signin.aliyun.com/117xxxxxxxxxxx63/saml/SSO">
<md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>TIIC4jCCAcqgAwIBAgIIDmXMktHMYX8wDQYJKoZIhvcNAQEFBQAwMTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMRUwEwYDVQQDDAzor5XnlKjlhazlj7gwHhcNMjAxMjA3MDMwNTU3WhcNMjExMjA3MDMwNTU3WjAxMQswCQYDVQQGEwJDTjELMAkGA1UECBMCQkoxFTATBgNVBAMMDOivleeUqOWFrOWPuDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiuwcn+sbMaVRT2Byb3GzBV1P0eOoK326fQS9rdeaGaIykSMqCMMKOZ+/QfdMWh+9Fr59A5pIEbCN7aP3P+cV1ClqhfKD4DTbsmGikSiUYgYf4tWztZx9NFWyuoucm8LOKKpKlPbjUyLudzLlQGOCrX/4Be0md4mIVZMK96J41jRuJXUTxFepE0cTEi15SXbEsXrnJ1wueFylNKl9JerbCJ1EDayktAYvkMrmn2d2R2etiVR4Una9pBqtPvCElPKCNesWAE/3AcWTgSj+u8ocnTgnknIfVO65QRxaNrDAyTOpkquXFshs+DtlILEdk2p9UUxkUCNySbIIM/gVgL0TkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAeDdH7BYcalDYfpNKvJrHA9rKZ2vBTi4uy2WoXclE0EdzBGUC41oPL5g3ictNA+4S8tnCkzl8aQr79tjUmcL/0Uzv4sdOggwglmkgw3kek9Yq44i/ycMN8HVeF/vtyVxhlvqBeXU2P5n6jFqatG+VkeVGyiJQHwuP1UHokXWwyukcjr35CQQX5WALFNJ+F68ICKT9Ulqb5GtQgrd1JoRQB1Eb//IjxlZJAvZ6CxLnVCgVUSOI4xYEb8ATZPbzLIMIyXN4U6r6VxvBJuW/eMcqogYSYssbngSgpHmZFV9+MrDSjJLLtsVRuzmF+cBisojvo53z3EiNu/c4FGlUuKozPA==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>TIIC4jCCAcqgAwIBAgIIDmXMktHMYX8wDQYJKoZIhvcNAQEFBQAwMTELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMRUwEwYDVQQDDAzor5XnlKjlhazlj7gwHhcNMjAxMjA3MDMwNTU3WhcNMjExMjA3MDMwNTU3WjAxMQswCQYDVQQGEwJDTjELMAkGA1UECBMCQkoxFTATBgNVBAMMDOivleeUqOWFrOWPuDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiuwcn+sbMaVRT2Byb3GzBV1P0eOoK326fQS9rdeaGaIykSMqCMMKOZ+/QfdMWh+9Fr59A5pIEbCN7aP3P+cV1ClqhfKD4DTbsmGikSiUYgYf4tWztZx9NFWyuoucm8LOKKpKlPbjUyLudzLlQGOCrX/4Be0md4mIVZMK96J41jRuJXUTxFepE0cTEi15SXbEsXrnJ1wueFylNKl9JerbCJ1EDayktAYvkMrmn2d2R2etiVR4Una9pBqtPvCElPKCNesWAE/3AcWTgSj+u8ocnTgnknIfVO65QRxaNrDAyTOpkquXFshs+DtlILEdk2p9UUxkUCNySbIIM/gVgL0TkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAeDdH7BYcalDYfpNKvJrHA9rKZ2vBTi4uy2WoXclE0EdzBGUC41oPL5g3ictNA+4S8tnCkzl8aQr79tjUmcL/0Uzv4sdOggwglmkgw3kek9Yq44i/ycMN8HVeF/vtyVxhlvqBeXU2P5n6jFqatG+VkeVGyiJQHwuP1UHokXWwyukcjr35CQQX5WALFNJ+F68ICKT9Ulqb5GtQgrd1JoRQB1Eb//IjxlZJAvZ6CxLnVCgVUSOI4xYEb8ATZPbzLIMIyXN4U6r6VxvBJuW/eMcqogYSYssbngSgpHmZFV9+MrDSjJLLtsVRuzmF+cBisojvo53z3EiNu/c4FGlUuKozPA==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://lidcfkpjfb.login.aliyunidaas.com/enduser/api/application/plugin_aliyun/idaas-cn-hangzhou-vr533mky3c3plugin_aliyun/sp_sso"/>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://lidcfkpjfb.login.aliyunidaas.com/enduser/api/application/plugin_aliyun/idaas-cn-hangzhou-vr533mky3c3plugin_aliyun/sp_sso_post"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>
3.3.2、SP中配置元数据信息
不同的SP配置IDP的元数据方式不同,有的需要填入参数,有的可以直接上传元数据文件。以阿里云RAM为例,在阿里云RAM选择开启SSO功能,并且上传刚刚下载的元配置文件,完成SP中的IDP元数据信息配置。
3.4、功能演示
3.4.1、IDP发起SSO
配置完成后, 就可以检查结果了。 授权用户登录IDaaS,点击左侧导航栏 主导航 > 首页 在我的应用中点击该应用进行单点登录,点击应用的图标进行单点登录。
选择子账户demo进行单点登录。
成功登录阿里云RAM控制台,然后就可以看到阿里云作为SP提供的资源了。
如果账号配置错误或者选择的登录账号不是阿里云RAM中的账户,则会提示账户不存在。
3.4.2、SP发起SSO
同样, 正确配置后, 也支持SP发起, 首先找到阿里云RAM子账户登录地址。
贴到浏览器跳转后, 登录界面上可以看到“主账号登录”和“使用企业账号登录”两种选择。主账号登录是使用阿里云RAM自己的账号和密码进行登录,点击使用企业账号登录,则开始进行IDaaS的SSO过程。
浏览器会自动跳转到IDP的登录界面,登录IDaaS授权账号,例如zhangsan,然后IDaaS认证完成以后,找到对应的子账号,生成SAMLResponse, 就会跳转到阿里云RAM控制台。
自此, IDP发起和SP发起全部工作正常!
四、FAQ
4.1、代码中如何解析SAMLRequest
SP发起SSO的时候会生成SAMLRequest,SAMLRequest是Base64编码后的内容,我们需要解析以后才能得到需要的内容,如下代码可以解析SAMLRequest,然后就可以拿到AuthnRequest进行认证。
import java.io.*;
import org.opensaml.xml.util.Base64;
import java.util.zip.InflaterInputStream;
import java.util.zip.Inflater;
public class SamlRequestTest {
public static void main(String[] args) throws Exception {
// 接收到的原始SAMLRequest
String samlRequest = "fZJNT%2BMwEIbv%2Bysi3%2FNhd9umVhPUXYQWiRUVCRy4IMedFIMzzmacavvvCQll4bAcfLD0fnjm8frsb2ODA3RkHGaMRwkLALXbGdxn7La8CFN2ln9bk2qsaOWm9494A396IB9siKDzg%2B%2BnQ%2Bob6AroDkbD7c1Vxh69b0nGMZk9GoyUNcceI%2B2a%2BDUqLoprFpwPKQaVH6tPBmyttmiPhyqybv9uNTulaPQD7vqhOFatGU5rjR4T4tb2g%2FxhksejPtQYVmCehmHC2h3h%2BETPz%2B3yk5LH1D4QORZcuE7DOGHGamUJWHB5njElaljCTOxTriuuVlzVS6iTNIVK8F01iGiriMwB%2FtmIerhE8gp9xkQikpCLMElLzuV8JvkqWqTf71mw7Zx32tkfBqeF9x1Kp8iQRNUASa9lsfl9JUWUyGoSkfxVlttwe12ULLg7gROv4AaUSHJC9XVW%2B1bM8omsHF%2FcfUz4OkCd2LP8%2F6R5ukgWq8V8NpsnKyEW7%2BjX8cfW%2FO36%2BXvlLw%3D%3D&RelayState=https%3A%2F%2Fhomenew.console.aliyun.com%2Fhome%2Fscene%2FOperation";
// base64解码
byte[] decodedBytes = Base64.decode(java.net.URLDecoder.decode(samlRequest, "utf-8"));
// 获取输入流
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodedBytes);
InflaterInputStream inflaterInputStream = new InflaterInputStream(byteArrayInputStream, new Inflater(true));
byte[] buffer = new byte[decodedBytes.length];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 信息写到输出流
for (int i = 0; i != -1; i = inflaterInputStream.read(buffer)) {
byteArrayOutputStream.write(buffer, 0, i);
}
String result = new String(byteArrayOutputStream.toByteArray(), "UTF-8");
// 输出解析后结果
System.out.println(result);
}
}
解析后的结果为
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest AssertionConsumerServiceURL="https://signin.aliyun.com/saml/SSO" Destination="https://nplclnlyvb.login.aliyunidaas.com/enduser/api/application/plugin_aliyun/idaas-cn-beijing-foyeyjskkp7plugin_aliyun1/sp_sso" ForceAuthn="false" ID="a2fe7e32g81cb1a91af7ef088eb21db" IsPassive="false" IssueInstant="2020-12-08T11:53:19.684Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://signin.aliyun.com/1860696533509226/saml/SSO</saml2:Issuer></saml2p:AuthnRequest>