Обзор
Casdoor теперь можно использовать как SAML IdP. На данный момент Casdoor поддерживает основные функции SAML 2.0.
Конфигурация в SP
В общем, SP требует три обязательных поля: Single Sign-On
, Issuer
и Public Certificate
. Большинство SP могут получить эти поля, загрузив файл XML Metadata или URL XML 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
Вы также можете найти метаданные на странице редактирования приложения. Нажмите кнопку, чтобы скопировать URL и вставить его в браузер для загрузки XML Metadata.
Конфигурация в Casdoor IdP
Casdoor поддерживает как GET, так и POST SAMLResponse
. Casdoor должен знать, какие типы запросов поддерживает SP, когда Casdoor отправляет SAMLResponse
в SP. Вам нужно настроить приложение в Casdoor в зависимости от типа SAMLResponse
, поддерживаемого вашим SP.
Если вы заполните Reply URL
, Casdoor отправит SAMLResponse
с помощью POST запроса. Если URL ответа пуст, Casdoor будет использовать GET запрос. Возможно, вы задаетесь вопросом, как Casdoor узнает Reply URL
SP, если Reply URL
пуст. На самом деле, Casdoor может получить URL, называемый AssertionConsumerServiceURL
, анализируя SAMLRequest
и отправить запрос с SAMLResponse
на AssertionConsumerServiceURL
. Reply URL
перезапишет AssertionConsumerServiceURL
в SAMLRequest
.
Reply URL: Введите URL ACS, проверяющего SAML ответ.
Redirect URL: Введите уникальное имя. Это может называться
Audience
илиEntity ID
в вашем SP. Убедитесь, что вы заполнили тот жеRedirect URL
здесь, что и в вашем SP.
Профиль пользователя
После успешного входа в систему профиль пользователя в возвращенном SAMLResponse
от Casdoor имеет три поля. Атрибуты в XML и атрибуты пользователя в Casdoor сопоставлены следующим образом:
XML Имя Атрибута | Поле пользователя |
---|---|
DisplayName | displayName |
Имя | name |
Смотрите https://en.wikipedia.org/wiki/SAML_2.0 для получения дополнительной информации о SAML и его различных версиях.
Пример
gosaml2 это реализация SAML 2.0 для Поставщиков Услуг на основе etree и goxmldsig, чистая реализация Go цифровых подписей XML. Мы используем эту библиотеку для тестирования SAML 2.0 в Casdoor, как показано ниже.
Предположим, вы можете получить доступ к Casdoor через http://localhost:7001/
, и ваш Casdoor содержит приложение под названием app-built-in
, которое принадлежит организации под названием built-in
. URL 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.
После аутентификации вы получите сообщения ответа, как показано ниже.