SAML2协议

SAML,全称为Security Assertion Markup Language,是一种用于安全性断言的标记语言,目前的最新版本是2.0。

它是一个基于XML的标准,用于在不同的安全域(security domain)之间交换认证和授权数据。SAML2.0可以帮助我们跨域实现单点登录。

术语介绍

  • 服务提供方(Service Provider,简称SP),服务提供的实体。

  • 身份提供方(Identity Provider,简称IDP),身份提供的实体,包含对用户进行身份验证的能力。在SAML协议里,严格规范了IDP和SP之间传输的Token格式。

  • SAML请求(SAMLRequest),由SP生成,发送给IDP的身份验证请求,所以一般也叫做AuthnRequest(Authentication Request)。

  • SAML响应(SAMLResponse),由IDP生成,携带用户身份信息,发送给SP的身份验证结果,所以一般也叫做SAMLAssertion。

  • 服务提供方发起(SP-initiated),表示整个认证流程是由SP发起的,当客户直接访问SP的资源或网站时,跳转到IDP再跳转回来的过程,我们就称这个过程是SP-initiated的单点登录。

  • 认证提供方发起(IDP-initiated),表示整个认证流程是由IDP发起的,IDP直接提供SAML响应给到SP方,验证用户身份。

SAML认证流程

下面介绍的是一个SP-initiated的完整认证流程,IDP-initiated可以理解为是SP-initiated的简化部分。

  1. 用户打开SP地址。

  2. SP接收到请求之后,携带SAMLRequest请求到到IDP处(SAMLRequest的格式后面会给出)。

  3. IDP接收到SAMLRequest后,会在IDP验证用户身份,玉符统一身份会检查当前用户是否登录,如果未登录跳转登录页面。

  4. IDP验证身份通过后,会重定向到SP,携带SAMLResponse(SAMLResponse的格式后面会给出)。

  5. SP验证SAMLResponse,验证通过后取出SAMLResponse里携带的用户信息,访问对应的用户。

SAMLRequest介绍

下面是一个SAMLRequest的XML示例:

<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceURL="https://www.sp.com/saml/acs" Destination="https://www.idp.com/saml/authn" ForceAuthn="false" ID="_7xvtiohf7xupg9ulo8an1g4bemeranpifvup" IsPassive="false" IssueInstant="2019-07-25T09:59:02.467Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0">
  <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://www.sp.com</saml2:Issuer>
</saml2p:AuthnRequest>

在实际请求中,还需要将SAMLReqeust进行Base64编码,上文示例的SAMLRequest进行Base64编码之后如下:

fZFBT8JAEIXvJv6Hzd5pSwPWbigJSowkGhuoHryYpQywSTu77uwWfr6lYISYcJ158+bNNyOSdRUbMfFui3P49kCO7esKSRw7GfcWhZakSKCsgYQrxWLy+iLiIBLGaqdLXXE2IQLrlMZHjeRrsAuwjSrhff6S8a1zhkQY7na7gExQ6jo8uIeyJM6m7UqF8jB7qVSrc+khIGdP2pbQhc34WlYEnM2mGf9K9k1rsF0ne282qa/0vcT+ZrCENolEo9aNN62UckmkGvgbJvIwQ3ISXcbjqJ/2oqQXD4soFcNURHEwuEs+OctPhz4oXCncXKeyPIpIPBdF3svfFgVnH2Cpu7AV8PHtDWOjDrDoEthz5tfN5S9oPv6PdRSem7ZrToXLB49/AA==

当服务提供方SP未登录时,需要跳转到认证提供方IDP的地址,进行身份验证操作,最终生成的URL如下所示:

https://www.idp.com/saml/authn?SAMLRequest=fZFBT8JAEIXvJv6Hzd5pSwPWbigJSowkGhuoHryYpQywSTu77uwWfr6lYISYcJ158+bNNyOSdRUbMfFui3P49kCO7esKSRw7GfcWhZakSKCsgYQrxWLy+iLiIBLGaqdLXXE2IQLrlMZHjeRrsAuwjSrhff6S8a1zhkQY7na7gExQ6jo8uIeyJM6m7UqF8jB7qVSrc+khIGdP2pbQhc34WlYEnM2mGf9K9k1rsF0ne282qa/0vcT+ZrCENolEo9aNN62UckmkGvgbJvIwQ3ISXcbjqJ/2oqQXD4soFcNURHEwuEs+OctPhz4oXCncXKeyPIpIPBdF3svfFgVnH2Cpu7AV8PHtDWOjDrDoEthz5tfN5S9oPv6PdRSem7ZrToXLB49/AA==

SAMLResponse介绍

当IDP接受到服务提供方SP发起的SAMLRequest时,会在IDP的域内进行身份认证操作,完成登录行为之后,会携带当前登录的用户身份,发送SAMLResponse给到服务提供方SP。

下面是一个SAMLResponse的XML示例:

<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://www.sp.com/saml/acs" ID="_80ff50f0-73e9-4357-934e-15939aa197ae" InResponseTo="_7xvtiohf7xupg9ulo8an1g4bemeranpifvup" IssueInstant="2019-07-25T09:59:01.653Z" Version="2.0">
  <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://www.idp.com</saml2:Issuer>
  <saml2p:Status>
    <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </saml2p:Status>
  <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_ee51e413-cb8c-4cff-a833-8e5ab5a65712" IssueInstant="2019-07-25T09:59:01.653Z" Version="2.0">
    <saml2:Issuer>https://www.idp.com</saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <ds:SignedInfo>
        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
        <ds:Reference URI="#_ee51e413-cb8c-4cff-a833-8e5ab5a65712">
          <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
          </ds:Transforms>
          <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
          <ds:DigestValue>SeLenKpDFTFSrd4RodgCxUbXi1I=</ds:DigestValue>
        </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>fEN31jRzjpGPSFEazXSYIu8mlhSrB/2rhlGmkQw8vbgUlHveU4BfX91knG11yF4HcLd5686WYWCS9P9szLvcxuQlVi1z5NiIFs967Z3hCZuU6mP86/5qEJVOVeUnaS39YtP8Jwxoev2d4QJjJ0+9+9ifOQkf4bhM+9b8Q1/3A5Q4xqjvArHTwGQMnjpi5hJQprkreChvNXGu3vemzHb5HEGkQHsbwavuA65VDwQ3sxdQbbDjQrXMIZWlKt51IXHhDVrlCwVIDXPCrN1e3wJrkLPitAUbhDCcRdVvmc8a5lcmvX4SkDn7PwzmFAf1rXACCyvKft5YycYnN7zHkymLCA==</ds:SignatureValue>
      <ds:KeyInfo>
        <ds:X509Data>
          <ds:X509SubjectName>C=CN,L=Beijing,CN=com,CN=yufuid</ds:X509SubjectName>
          <ds:X509Certificate>MIIC/DCCAeSgAwIBAgIIBjRkHLoMipUwDQYJKoZIhvcNAQEFBQAwPjEPMA0GA1UEAwwGeXVmdWlkMQwwCgYDVQQDDANjb20xEDAOBgNVBAcMB0JlaWppbmcxCzAJBgNVBAYTAkNOMB4XDTE5MDcyMzEwNDA1NloXDTIwMDcyMjEwNDA1NlowPjEPMA0GA1UEAwwGeXVmdWlkMQwwCgYDVQQDDANjb20xEDAOBgNVBAcMB0JlaWppbmcxCzAJBgNVBAYTAkNOMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0dk/S9XEiBio3CRccVIsjFX4KBGZPfz47jrm9TF1v3t+7Cy7WkYD6EW/rQYi7ORfRTVx8m8UsiB04MjwHARtVdDPP9COxfjkVp5h5nQv1P6WYtZjIFDiIBbS0of1YmeHzCixxGQDk+PL/M19aey9DoRVg9MjcQYppjoiYbJhqj/YL2kCO5QgaquPptBCVKcMpqE1BCggPLyH3M1V7/QJXzRKBs/iOO/3yUnapftK/ysZeWMZ2hEGlB/AW3OP9pO2vijONqxbHqTFyngyYof+QRl7F5KsmZNP5Pd6SoYo0MtrEsDVIchdx9Mb1iCLZfiHqbq8s4mr7+alvuyBE1lW+QIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAdz05eOepSmE+IFMdwLkkD5oDBBwC16ie5BpXBxFAYxEYrQ8sZPmjTIhG+tQe3lnkxmrgyPbq1pevybGl3aR9dPWysg3ps2pK+pJQt6aRHQbNhy+fFT4uqZzVXy/61LPt8OhhhzjGVoApIh53A4OuffV2B1PueBChjrrKEZGHk5iVuzv1kEtGu0NIKbTXBcG7ZfX+2pKYYKw3AKllFDSBL/ePFjUh5iXJWwwGflqwtQjmZYZt4k+UMDIW7L6r5FlDNc4JGjH1gWOXpCZ6EkGR1rAmK2Fnv1/5LTRVYrMlGYlCx29DhM7GVnpgfTmua15E3dW+WyS4TOM8UnjavAIBa</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </ds:Signature>
    <saml2:Subject>
      <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">sanzhang</saml2:NameID>
      <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <saml2:SubjectConfirmationData InResponseTo="_7xvtiohf7xupg9ulo8an1g4bemeranpifvup" NotOnOrAfter="2019-07-25T10:04:01.647Z" Recipient="https://www.sp.com/saml/acs"/>
      </saml2:SubjectConfirmation>
    </saml2:Subject>
    <saml2:Conditions NotBefore="2019-07-25T09:54:01.647Z" NotOnOrAfter="2019-07-25T10:04:01.647Z">
      <saml2:AudienceRestriction>
        <saml2:Audience>https://www.sp.com</saml2:Audience>
      </saml2:AudienceRestriction>
    </saml2:Conditions>
    <saml2:AuthnStatement AuthnInstant="2019-07-25T09:59:01.653Z">
      <saml2:AuthnContext>
        <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
      </saml2:AuthnContext>
    </saml2:AuthnStatement>
  </saml2:Assertion>
</saml2p:Response>

这里的参数比较多,就不一一介绍了,只标记几个关键字段进行解释:

  • 这里的Destination其实表示的是服务提供方SP接收SAML响应的地址,如示例的 https://www.sp.com/saml/acs 。注意一定要跟SAMLRequest里的Destination区分开来。

  • NameID表示登录的用户身份,这里的响应表示登录的用户为sanzhang。

  • SAMLResponse是会指定有效期的,用NotBefore和NotOnOfAfter进行表示,上述示例中,该响应的有效期为2019年7月25日9点54分到2019年7月25日10点54分(北京时间需要增加八小时)。

SAML如何保证响应的有效性

身份提供方IDP需要生成密钥对,用密钥对生成配套的证书,并将证书给到服务提供方SP。SP需要存储证书,当IDP签发SAMLResponse时,会对Response响应进行签名,当SP接受到IDP签发的SAMLResponse时,需要用证书校验签名的有效性。