ภาพรวม
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.
การตั้งค่าใน 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.
Redirect URL: พิมพ์ชื่อที่ไม่ซ้ำกัน. อาจเรียกว่า
Audience
หรือEntity ID
ใน SP ของคุณ. ตรวจสอบให้แน่ใจว่าคุณกรอกRedirect URL
เดียวกันนี้ใน SP ของคุณ.
โปรไฟล์ผู้ใช้
หลังจากเข้าสู่ระบบสำเร็จ, โปรไฟล์ผู้ใช้ใน SAMLResponse
ที่ส่งกลับจาก Casdoor มีสามฟิลด์. คุณสมบัติใน XML และคุณสมบัติของผู้ใช้ใน Casdoor ถูกแมปดังนี้:
XML Attribute Name | ฟิลด์ผู้ใช้ |
---|---|
อีเมล | |
DisplayName | displayName |
ชื่อ | name |
ดู 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 จะถูกแสดง.
หลังจากยืนยันตัวตนแล้ว, คุณจะได้รับข้อความตอบกลับดังที่แสดงด้านล่าง.