Visão Geral
Casdoor agora pode ser usado como um SAML IdP. Até este ponto, o Casdoor suportou as principais funcionalidades do SAML 2.0.
Configuração no SP
Em geral, o SP requer três campos obrigatórios: Single Sign-On
, Issuer
e Public Certificate
. A maioria dos SPs pode obter esses campos fazendo o upload do arquivo de Metadados XML ou da URL de Metadados XML para preenchimento automático.
Os metadados do ponto de extremidade SAML no Casdoor são <Endpoint of casdoor>/api/saml/metadata?application=admin/<application name>
. Suponha que o ponto de extremidade do Casdoor seja https://door.casdoor.com
, e ele contém uma aplicação chamada app-built-in
. O ponto de extremidade de Metadados XML será:
https://door.casdoor.com/api/saml/metadata?application=admin/app-built-in
Você também pode encontrar os metadados na página de edição da aplicação. Clique no botão para copiar a URL e cole-a no navegador para baixar os Metadados XML.
Configuração no Casdoor IdP
Casdoor suporta tanto GET quanto POST SAMLResponse
. O Casdoor precisa saber quais tipos de solicitações o SP suporta quando o Casdoor envia o SAMLResponse
para o SP. Você precisa configurar a aplicação no Casdoor com base no tipo de SAMLResponse
suportado pelo seu SP.
Se você preencher a URL de Resposta
, o Casdoor enviará o SAMLResponse
por Solicitação POST. Se a URL de Resposta estiver vazia, o Casdoor usará a solicitação GET. Você pode se perguntar como o Casdoor sabe a URL de Resposta
do SP se a URL de Resposta
estiver vazia. Na verdade, o Casdoor pode obter a URL chamada AssertionConsumerServiceURL
analisando o SAMLRequest
e enviar a solicitação com SAMLResponse
para AssertionConsumerServiceURL
. A URL de Resposta
substituirá a AssertionConsumerServiceURL
no SAMLRequest
.
URL de Resposta: Digite a URL do ACS que verifica a resposta SAML.
URL de Redirecionamento: Digite um nome único. Isto pode ser chamado de
Audience
ouEntity ID
no seu SP. Certifique-se de preencher a mesmaURL de Redirecionamento
aqui como no seu SP.
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
Nome | Name format | Value |
---|---|---|
https://www.aliyun.com/SAML-Role/Attributes/RoleSessionName | Unspecified | $user.name |
https://www.aliyun.com/SAML-Role/Attributes/Role | Unspecified | acs: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
Perfil do usuário
Após o login bem-sucedido, o perfil do usuário na SAMLResponse
retornada do Casdoor tem três campos. Os atributos no XML e os atributos do usuário no Casdoor são mapeados da seguinte forma:
Nome do Atributo XML | Campo do usuário |
---|---|
DisplayName | displayName |
Name | name |
Veja https://en.wikipedia.org/wiki/SAML_2.0 para mais informações sobre SAML e suas diferentes versões.
Um exemplo
gosaml2 é uma implementação do SAML 2.0 para Provedores de Serviço baseada em etree e goxmldsig, uma implementação pura em Go de assinaturas digitais XML. Usamos esta biblioteca para testar o SAML 2.0 no Casdoor como mostrado abaixo.
Suponha que você possa acessar o Casdoor através de http://localhost:7001/
, e seu Casdoor contém uma aplicação chamada app-built-in
, que pertence a uma organização chamada built-in
. As URLs, http://localhost:6900/acs/example
e http://localhost:6900/saml/acs/example
, devem ser adicionadas às URLs de Redirecionamento em 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)
}
}
Execute o código acima, e o console exibirá a seguinte mensagem.
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
Clique na URL para autenticar, e a página de login do Casdoor será exibida.
Após autenticar, você receberá as mensagens de resposta como mostrado abaixo.