SAML2.0详解,助力企业级SSO_saml2.0协议-程序员宅基地

技术标签: php  开发语言  

前言

在国内,提到认证授权、单点登录(SSO),第一时间想到的大多是 OAuth 2.0。OAuth 2.0 是一个开放标准,在国内的技术社区中得到了广泛的支持和推广,许多互联网公司以及开发者积极采用。还有一点就是国内的许多场景更关注于授权而非认证,OAuth 2.0 专注于授权机制,允许用户授权第三方应用程序访问其存储在另一个服务提供者上的资源,而无需共享用户名和密码,这种机制非常适合于社交媒体、移动应用、开放平台等场景。

今天我想说的是一种不一样的声音,今日的主角并不是 OAuth 2.0,而是 SAML 2.0

SAML

SAML(Security Assertion Markup Language)安全断言标记语言,是一个基于XML的开源标准数据格式。它用于在不同的安全域之间交换身份验证和授权数据,尤其是在身份提供者和服务提供者之间。

SAML 的核心是一组协议,这些协议可以用来传输安全声明,这些声明是关于用户身份验证、授权和属性(例如姓名、电子邮件地址和电话号码)的信息。这些声明以 XML 格式编码,并可以在网络上从一个实体传输到另一个实体。因此,SAML允许跨不同系统和平台实现单点登录(SSO)和联合身份认证。

简单了解下,SAML 也是经历了几个版本的变化。

  • SAML 1.0:这是SAML的最初版本,于 2002 年发布。它定义了基本的 SAML 断言格式和处理流程,但相对于后续版本来说功能较为有限。

  • SAML 1.1:SAML 1.1 版本在 SAML 1.0 的基础上进行了一些改进和扩展。它增加了对更多场景和用例的支持,并提供了更好的互操作性和安全性。然而,SAML 1.0 和 1.1 版本之间并不完全兼容。

  • SAML 2.0:SAML 2.0 是 SAML 协议的最重要和广泛使用的版本。它引入了许多新特性和改进,包括更灵活的断言格式、增强的安全性机制、更好的互操作性以及对更多身份联合场景的支持。SAML 2.0 是目前被广泛应用和支持的标准,并且与之前的版本不兼容。本文要说的就是 SAML 2.0。

SAML 2.0 工作流程

先来粗略的看一下 SAML 2.0 的工作流程,里边涉及到的一些概念后边会加以说明。SAML 2.0 会涉及到非常多的概念我们慢慢往下看。

  1. 用户访问服务提供者:用户尝试访问服务提供者(SP)提供的某个受保护资源,但尚未经过身份验证。

  2. 重定向到身份提供者:由于用户尚未认证,服务提供者会生成一个 SAML 认证请求(AuthnRequest),并将用户重定向到身份提供者(IdP)。这个重定向可以通过多种绑定协议完成,如 HTTP Redirect 或 POST 请求。

  3. 用户身份验证:在 IDP 的页面,用户输入其凭据(如用户名和密码)进行验证。如果凭据成功,身份提供者会生成一个包含用户身份信息的 SAML 响应(Response)。

  4. 发送 SAML 响应:身份提供者将 SAML 响应发送回服务提供者,同样可以通过多种绑定协议完成。响应中包含了用户的身份信息、任何额外的属性(如角色、组成员身份等)以及认证时间和有效期等安全声明。

  5. 服务提供者验证并接受响应:服务提供者收到 SAML 响应后,会进行验证,以确保响应的真实性和完整性。如果验证通过,验证通过后,SP 根据断言中的信息确定用户身份,并允许用户访问受保护的资源。

  6. 用户访问受保护资源:用户已经成功认证,并可以访问服务提供者提供的受保护资源。

通过流程可以看到主要涉及两个关键实体,SP 和 IDP,我们先来分析下这两个实体。

SP 和 IDP

身份提供者(Identity Provider) : IdP 是负责验证用户身份的实体,通常是一个集中式的身份管理系统。当用户试图访问依赖于 SAML 的资源时,会首先与IdP进行交互来证明自己的身份。IdP验证用户凭证并通过SAML断言的形式向服务提供者传达用户身份和授权信息。

服务提供者(Service Provider) :SP 是实际提供在线服务的应用程序或系统,它依赖于 IdP 来进行用户的身份验证。SP 接收来自 IdP 的 SAML 断言,并基于这些断言决定是否允许用户访问其服务。

简单理解 SP 是用户要访问的系统,IDP 就是验证凭据的系统。SP 和 IDP 交互是通过 SAML 请求(SAMLRequest)和 SAML 响应 (SAMLResponse)来完成的。

SAMLRequest

SAMLRequest 就是 SP 向 IDP 发起的请求。

来看一个 SP 请求 IDP 对用户进行身份验证的 XML 内容。

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="SP_809707a0130a5d00120c9d9df97Z627afe9ddc24" Version="2.0" IssueInstant="2024-02-08T23:52:45Z" Destination="http://idp.saml.com/sso" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://sp.saml.com/test?acs">
  <saml:Issuer>http://sp.saml.com/test/</saml:Issuer>
  <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true"/>
  <samlp:RequestedAuthnContext Comparison="exact">
    <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
  </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

感觉有点小复杂,怎么这么多元素。我们来分析下,看看这些元素的含义。

  • ID: 请求的唯一标识符,主要用于后续消息处理的匹配和关联。

  • Version: 表明请求遵循的SAML版本,上文提到过的 SAML 版本号,这里是 2.0。

  • IssueInstant: 请求发出的时间。

  • Destination: 身份提供者的 URL,表明请求应该被送达的目标地址。

  • Issuer: 提供此请求的 SP 的唯一标识符,通常是一个 URL,以便身份提供者识别请求来源。

  • NameIDPolicy: 可选参数,指示身份提供者应该如何在断言中构造用户的名称ID。

  • ForceAuthn: 可选参数,指示是否强制用户重新进行身份验证,即使他们已经处于已验证的会话中。

  • ProtocolBinding: 表明身份验证响应应当采用哪种绑定方式进行传输。

  • AssertionConsumerServiceURL: 服务提供者希望接收身份验证响应的位置(Assertion Consumer Service URL)。

  • RequestedAuthnContext: 可选参数,指定了预期的身份验证上下文,例如要求特定级别的身份验证强度。

AuthnRequest 也可以通过 Signature 标签来发送签名消息。

SAMLResponse

SAMLResponse 就是 IDP 向 SP 发起的请求。

来看一个 IDP 向 SP 的 AssertionConsumerServiceURL 发送的 XML 内容。

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="IDP_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="2023-07-17T01:01:48Z" Destination="http://sp.saml.com/test?acs" InResponseTo="SP_809707a0130a5d00120c9d9df97Z627afe9ddc24">
  <saml:Issuer>http://idp.saml.com</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="2023-07-17T01:01:48Z">
    <saml:Issuer>http://idp.saml.com</saml:Issuer>
    <saml:Subject>
      <saml:NameID SPNameQualifier="http://sp.saml.com/test" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.saml.com/test/acs" InResponseTo="SP_809707a0130a5d00120c9d9df97Z627afe9ddc24"/>
      </saml:SubjectConfirmation>
    </saml:Subject>
    <saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
      <saml:AudienceRestriction>
        <saml:Audience>http://sp.saml.com/test</saml:Audience>
      </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
      <saml:AuthnContext>
        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
      </saml:AuthnContext>
    </saml:AuthnStatement>
    <saml:AttributeStatement>
      <saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
        <saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
        <saml:AttributeValue xsi:type="xs:string">[email protected]</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
        <saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
        <saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

  • ID:响应的唯一标识符。

  • InResponseTo:引用之前发出的请求的ID。

  • Version:表明请求遵循的SAML版本,上文提到过的 SAML 版本号,这里是 2.0。

  • IssueInstant:响应创建的时间戳。

  • Destination:预期接收此响应的服务提供者的URL。

  • Status:包含响应状态码,指示操作是否成功。

  • Issuer:标识发件人(身份提供者)的URI。

  • Assertion:包含实际的身份验证声明和用户属性声明。

  • Subject:关于被认证用户的详细信息,包括NameID。

  • Conditions:声明的有效条件,比如有效期和受众限制。

  • AuthnStatement:记录身份验证事件,如何时何地进行了身份验证。

  • AttributeStatement:提供关于用户的属性信息,如姓名、姓氏等。

Binding

SAML 的 Binding 是指在SAML生态系统中传输和接收SAML消息的协议规范和格式。介绍几种 SAML 2.0 中常见的 Binding。

  • HTTP Redirect Binding:将 SAML 消息编码为 URL 参数,并通过 HTTP 重定向发送。这种方式简单易用,但受限于 URL 长度,适用于传输较小的 SAML 消息,如 AuthnRequest 和 LogoutRequest。

  • HTTP POST Binding:将 SAML 消息作为 HTML 表单的隐藏字段,通过 HTTP POST 方法发送。这种方式可以传输更大的 SAML 消息,更安全,因为它不会将消息暴露在 URL 中。

  • HTTP Artifact Binding:将 SAML 消息的一个引用(称为 Artifact)通过 HTTP 传输。这种方式允许将实际的 SAML 消息存储在更高效的后端存储中,并减少传输的数据量。但是,它需要一个额外的步骤来检索实际的 SAML 消息。

  • SOAP Binding:将 SAML 消息嵌入到 SOAP 消息中,通过 SOAP 协议传输。这种方式适用于需要在客户端和服务器之间进行复杂交互的场景,但增加了实现的复杂性。

实际使用时要根据实际场景选择合适的 Binding 类型。

LogoutRequest

LogoutRequest 是 SP 向 IdP 发送的请求,用于通知 IdP 用户已经登出 SP 系统,请求 IdP 终止用户的会话并撤销其授权。

终止会话就要用到 LogoutRequest 了,XML 示例如下。

<saml2p:LogoutRequest  
    xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"  
    ID="SP_b37a441e-5720-4d3d-be3d-795292604840"  
    Version="2.0"  
    IssueInstant="2023-04-01T12:00:00Z"  
    Destination="https://idp.saml.com/SingleSignOutService">  

    <saml:Issuer>https://sp.saml.com</saml:Issuer>
    
    <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">john.doe</saml2:NameID>  
    
    <saml2p:SessionIndex>SP_8c5ea886-d2e8-45e6-8554-887372e0d626</saml2p:SessionIndex>  
</saml2p:LogoutRequest>

元素上文基本都说过,重点说一下 SessionIndex。SessionIndex(可选)是一个索引值,用于标识在 IdP 侧要终止的特定会话。这对于处理同一用户在 IdP 上有多个会话的情况时是很有用的

LogoutResponse

LogoutResponse 是 SAML 协议中与 LogoutRequest 对应的响应消息,它由 IdP 向 SP 发送,以通知SP注销请求已经被处理。

来看下 LogoutResponse 的 XML 示例。

<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                      xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                      ID="IDP_b123aaa1e-5720-4d1d-be3d-795292602340"
                      InResponseTo="SP_b37a441e-5720-4d3d-be3d-795292604840"
                      Version="2.0"
                      IssueInstant="2024-02-18T12:34:56Z">
    <saml:Issuer>https://idp.saml.com/SAML2</saml:Issuer>
    <samlp:Status>
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
    </samlp:Status>
</samlp:LogoutResponse>

Status 表示注销操作的状态信息。

当然这些 SAML 请求和响应,为了增强安全性,通常也会被签名,以确保响应的完整性和来源的可靠性。

MetaData

元数据(Metadata)是 SAML 协议中非常关键的一部分,它包含了关于 SAML 通信各方(如身份提供者IDP和服务提供者SP)的配置信息。这些配置信息对于 SAML 实体的互操作和通信至关重要。

SP 和 IDP 通常通过交换元数据的方式来互相接入。

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
                     validUntil="2024-02-01T12:34:56Z"
                     entityID="https://idp.saml.com/SAML2">
    <md:IDPSSODescriptor WantAuthnRequestsSigned="true"
                          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>...</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>...</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </md:KeyDescriptor>
        
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
        <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.saml.com/SAML2/SSO/Redirect"/>
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                                Location="https://idp.saml.com/SAML2/SSO/POST"/>
        
        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                                Location="https://idp.saml.com/SAML2/SingleLogout/Redirect"/>
        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                                Location="https://idp.saml.com/SAML2/SingleLogout/POST"/>
    </md:IDPSSODescriptor>
</md:EntityDescriptor>

SP Metadata也是类似,这里就不贴了,就是将 <md:IDPSSODescriptor> 标签换成 <md:SPSSODescriptor>,里边内容差不多。

总结

来张图方便理解。

图片

SAML 提供了一种强大且灵活的方法,用于在网络应用程序和服务之间安全地交换身份信息,从而简化了身份验证和授权的过程,提高了用户体验,并增强了安全性。理解 SAML 的各种元素是灵活使用 SAML 的关键。

原文地址: SAML2.0详解,助力企业级SSO

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u012613251/article/details/137099294

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法