ข้ามไปยังเนื้อหาหลัก

ภาพรวม

Casdoor สามารถใช้เป็น SAML IdP ได้แล้ว. จนถึงตอนนี้, Casdoor ได้รองรับคุณสมบัติหลักของ SAML 2.0.

การตั้งค่าใน SP

โดยทั่วไป, SP ต้องการสามฟิลด์ที่จำเป็น: Single Sign-On, Issuer, และ Public Certificate. SP ส่วนใหญ่สามารถได้รับฟิลด์เหล่านี้โดยการอัปโหลดไฟล์ XML Metadata หรือ URL ของ XML Metadata เพื่อการเติมข้อมูลอัตโนมัติ.

metadata ของจุดสิ้นสุด SAML ใน Casdoor คือ <Endpoint of casdoor>/api/saml/metadata?application=admin/<application name>. สมมติว่าจุดสิ้นสุดของ Casdoor คือ https://door.casdoor.com, และมีแอปพลิเคชันที่เรียกว่า app-built-in. จุดสิ้นสุดของ XML Metadata จะเป็น:

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

คุณยังสามารถหา metadata ในหน้าแก้ไขแอปพลิเคชันได้. คลิกปุ่มเพื่อคัดลอก URL และวางลงในเบราว์เซอร์เพื่อดาวน์โหลด XML Metadata.

metadata

การตั้งค่าใน Casdoor IdP

Casdoor รองรับทั้ง GET และ POST SAMLResponse. Casdoor จำเป็นต้องรู้ว่า SP รองรับประเภทคำขอใดบ้างเมื่อ Casdoor ส่ง SAMLResponse ไปยัง SP. คุณต้องกำหนดค่าแอปพลิเคชันใน Casdoor ตามประเภท SAMLResponse ที่รองรับโดย SP ของคุณ.

ข้อมูล

หากคุณกรอก Reply URL, Casdoor จะส่ง SAMLResponse โดย POST Request. หาก Reply URL ว่างเปล่า, Casdoor จะใช้ GET request. คุณอาจสงสัยว่า Casdoor รู้ได้อย่างไรว่า Reply URL ของ SP หาก Reply URL ว่างเปล่า. จริงๆ แล้ว Casdoor สามารถรับ URL ที่เรียกว่า AssertionConsumerServiceURL โดยการวิเคราะห์ SAMLRequest และส่งคำขอพร้อม SAMLResponse ไปยัง AssertionConsumerServiceURL. Reply URL จะเขียนทับ AssertionConsumerServiceURL ใน SAMLRequest.

  • Reply URL: พิมพ์ URL ของ ACS ที่ตรวจสอบ SAML response.

    Reply URL

  • Redirect URL: พิมพ์ชื่อที่ไม่ซ้ำกัน. อาจเรียกว่า Audience หรือ Entity ID ใน SP ของคุณ. ตรวจสอบให้แน่ใจว่าคุณกรอก Redirect URL เดียวกันนี้ใน SP ของคุณ.

    Entity 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

โปรไฟล์ผู้ใช้

หลังจากเข้าสู่ระบบสำเร็จ, โปรไฟล์ผู้ใช้ใน SAMLResponse ที่ส่งกลับจาก Casdoor มีสามฟิลด์. คุณสมบัติใน XML และคุณสมบัติของผู้ใช้ใน Casdoor ถูกแมปดังนี้:

XML Attribute Nameฟิลด์ผู้ใช้
อีเมลemail
DisplayNamedisplayName
Namename

ดู https://en.wikipedia.org/wiki/SAML_2.0 เพื่อข้อมูลเพิ่มเติมเกี่ยวกับ SAML และเวอร์ชันต่างๆ.

ตัวอย่าง

gosaml2 เป็นการดำเนินการ SAML 2.0 สำหรับ Service Providers ตาม etree และ goxmldsig, การดำเนินการของ Go ที่บริสุทธิ์ของลายเซ็นดิจิทัล XML. เราใช้ไลบรารีนี้เพื่อทดสอบ SAML 2.0 ใน Casdoor ตามที่แสดงด้านล่าง.

สมมติว่าคุณสามารถเข้าถึง Casdoor ผ่าน http://localhost:7001/, และ Casdoor ของคุณมีแอปพลิเคชันที่เรียกว่า app-built-in, ซึ่งเป็นขององค์กรที่เรียกว่า built-in. URLs, http://localhost:6900/acs/example และ http://localhost:6900/saml/acs/example, ควรถูกเพิ่มเข้าไปใน Redirect URLs ใน app-built-in.

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 จะถูกแสดง.

เข้าสู่ระบบ

หลังจากยืนยันตัวตนแล้ว, คุณจะได้รับข้อความตอบกลับดังที่แสดงด้านล่าง.

ตอบกลับ