跳到主内容

概述

现在可以使用Casdoor作为SAML IdP。 到目前为止,Casdoor已经支持了SAML 2.0的主要功能。

在SP中的配置

一般来说,SP需要三个必填字段:Single Sign-OnIssuerPublic Certificate。 大多数SP可以通过上传XML元数据文件或XML元数据URL来自动完成这些字段。

Casdoor中的SAML端点的元数据是<Endpoint of casdoor>/api/saml/metadata?application=admin/<application name>。 假设Casdoor的端点是https://door.casdoor.com,并且它包含一个名为app-built-in的应用程序。 XML元数据端点将是:

https://door.casdoor.com/api/saml/metadata?application=admin/app-built-in

您也可以在应用程序编辑页面中找到元数据。 点击按钮复制URL并粘贴到浏览器中下载XML元数据。

元数据

在Casdoor IdP中的配置

Casdoor支持GET和POST SAMLResponse。 当Casdoor将SAMLResponse发送到SP时,Casdoor需要知道SP支持哪些类型的请求。 您需要根据您的SP支持的SAMLResponse类型在Casdoor中配置应用程序。

信息

如果您填写了Reply URL,Casdoor将通过POST请求发送SAMLResponse。 如果回复URL为空,Casdoor将使用GET请求。 您可能会想知道,如果Reply URL为空,Casdoor如何知道SP的Reply URL。 实际上,Casdoor可以通过解析SAMLRequest获取名为AssertionConsumerServiceURL的URL,并将带有SAMLResponse的请求发送到AssertionConsumerServiceURLReply URL将覆盖SAMLRequest中的AssertionConsumerServiceURL

  • 回复URL:输入验证SAML响应的ACS的URL。

    回复URL

  • 重定向URL:输入一个唯一的名称。 在您的SP中,这可能被称为AudienceEntity ID。 确保您在此处填写的Redirect URL与您的SP中的一致。

    实体ID

SAML attributes

Some SP will require you to provide external attributes in SAML Response, you can add those in SAML attributes table. And you can insert user's field to it.

For examle

名称Name formatValue
https://www.aliyun.com/SAML-Role/Attributes/RoleSessionNameUnspecified$user.name
https://www.aliyun.com/SAML-Role/Attributes/RoleUnspecifiedacs:ram::1879818006829152:role/$user.roles,acs:ram::1879818006829152:saml-provider/testa

will generate response with external saml:Attribute

<saml:Attribute Name="https://www.aliyun.com/SAML-Role/Attributes/RoleSessionName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml:AttributeValue xsi:type="xs:string">admi122n@outlook.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="https://www.aliyun.com/SAML-Role/Attributes/Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml:AttributeValue xsi:type="xs:string"> acs:ram::1879818006829152:role/role1,acs:ram::1879818006829152:saml-provider/testa</saml:AttributeValue>
<saml:AttributeValue xsi:type="xs:string"> acs:ram::1879818006829152:role/role2,acs:ram::1879818006829152:saml-provider/testa</saml:AttributeValue>
</saml:Attribute>
信息

We only support insert $user.owner,$user.name,$user.email,$user.id,$user.phone,$user.roles,$user.permissions,$user.groups

用户资料

成功登录后,Casdoor返回的SAMLResponse中的用户资料有三个字段。 XML中的属性和Casdoor中的用户属性的映射如下:

XML属性名称用户字段
电子邮件电子邮件
显示名称显示名称
Name名称

有关SAML及其不同版本的更多信息,请参见https://en.wikipedia.org/wiki/SAML_2.0

一个例子

gosaml2是基于etree和goxmldsig的服务提供商的SAML 2.0实现,这是XML数字签名的纯Go实现。 我们使用这个库来测试Casdoor中的SAML 2.0,如下所示。

假设您可以通过http://localhost:7001/访问Casdoor,并且您的Casdoor包含一个名为app-built-in的应用程序,该应用程序属于一个名为built-in的组织。 这两个URL:http://localhost:6900/acs/examplehttp://localhost:6900/saml/acs/example,应该添加到app-built-in中的重定向URLs。

import (
"crypto/x509"
"fmt"
"net/http"

"io/ioutil"

"encoding/base64"
"encoding/xml"

saml2 "github.com/russellhaering/gosaml2"
"github.com/russellhaering/gosaml2/types"
dsig "github.com/russellhaering/goxmldsig"
)

func main() {
res, err := http.Get("http://localhost:7001/api/saml/metadata?application=admin/app-built-in")
if err != nil {
panic(err)
}

rawMetadata, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}

metadata := &types.EntityDescriptor{}
err = xml.Unmarshal(rawMetadata, metadata)
if err != nil {
panic(err)
}

certStore := dsig.MemoryX509CertificateStore{
Roots: []*x509.Certificate{},
}

for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors {
for idx, xcert := range kd.KeyInfo.X509Data.X509Certificates {
if xcert.Data == "" {
panic(fmt.Errorf("metadata certificate(%d) must not be empty", idx))
}
certData, err := base64.StdEncoding.DecodeString(xcert.Data)
if err != nil {
panic(err)
}

idpCert, err := x509.ParseCertificate(certData)
if err != nil {
panic(err)
}

certStore.Roots = append(certStore.Roots, idpCert)
}
}

randomKeyStore := dsig.RandomKeyStoreForTest()

sp := &saml2.SAMLServiceProvider{
IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location,
IdentityProviderIssuer: metadata.EntityID,
ServiceProviderIssuer: "http://localhost:6900/acs/example",
AssertionConsumerServiceURL: "http://localhost:6900/v1/_saml_callback",
SignAuthnRequests: true,
AudienceURI: "http://localhost:6900/saml/acs/example",
IDPCertificateStore: &certStore,
SPKeyStore: randomKeyStore,
}

http.HandleFunc("/v1/_saml_callback", func(rw http.ResponseWriter, req *http.Request) {
err := req.ParseForm()
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
samlReponse := req.URL.Query().Get("SAMLResponse")
assertionInfo, err := sp.RetrieveAssertionInfo(samlReponse)
if err != nil {
fmt.Println(err)
rw.WriteHeader(http.StatusForbidden)
return
}
fmt.Println(assertionInfo)
if assertionInfo.WarningInfo.InvalidTime {
fmt.Println("here12:", assertionInfo.WarningInfo.InvalidTime)
rw.WriteHeader(http.StatusForbidden)
return
}

if assertionInfo.WarningInfo.NotInAudience {
fmt.Println(assertionInfo)
fmt.Println("here13:", assertionInfo.WarningInfo.NotInAudience)
rw.WriteHeader(http.StatusForbidden)
return
}

fmt.Fprintf(rw, "NameID: %s\n", assertionInfo.NameID)

fmt.Fprintf(rw, "Assertions:\n")

for key, val := range assertionInfo.Values {
fmt.Fprintf(rw, " %s: %+v\n", key, val)
}
fmt.Println(assertionInfo.Values.Get("FirstName"))
fmt.Fprintf(rw, "\n")

fmt.Fprintf(rw, "Warnings:\n")
fmt.Fprintf(rw, "%+v\n", assertionInfo.WarningInfo)
})

println("Visit this URL To Authenticate:")
authURL, err := sp.BuildAuthURL("")
if err != nil {
panic(err)
}

println(authURL)

println("Supply:")
fmt.Printf(" SP ACS URL : %s\n", sp.AssertionConsumerServiceURL)

err = http.ListenAndServe(":6900", nil)
if err != nil {
panic(err)
}
}

运行上述代码,控制台将显示以下消息。

Visit this URL To Authenticate:
http://localhost:7001/login/saml/authorize/admin/app-built-in?SAMLRequest=lFVbk6K8Fv0rFvNo2QR...
Supply:
SP ACS URL : http://localhost:6900/v1/_saml_callback

点击URL进行身份验证,Casdoor的登录页面将被显示。

登录

身份验证后,您将收到如下所示的响应消息。

响应