1.常用类如下
Samlv1ClaimsTransform.cs
- public static class Samlv1ClaimsTransform
- {
- public static ClaimsPrincipal Transform(ClaimsPrincipal incomingPrincipal)
- {
- if (!incomingPrincipal.Identity.IsAuthenticated)
- {
- return incomingPrincipal;
- }
-
- return CreateClaimsPrincipal(incomingPrincipal);
- }
-
- private static ClaimsPrincipal CreateClaimsPrincipal(ClaimsPrincipal incomingPrincipal)
- {
- var claims = new List
(); -
- // All claims
- claims.AddRange(incomingPrincipal.Claims);
-
- // Or custom claims
- //claims.AddRange(GetSaml2LogoutClaims(incomingPrincipal));
- //claims.Add(new Claim(ClaimTypes.NameIdentifier, GetClaimValue(incomingPrincipal, ClaimTypes.NameIdentifier)));
-
- return new ClaimsPrincipal(new ClaimsIdentity(claims, incomingPrincipal.Identity.AuthenticationType, ClaimTypes.NameIdentifier, ClaimTypes.Role)
- {
- BootstrapContext = ((ClaimsIdentity)incomingPrincipal.Identity).BootstrapContext
- });
- }
-
- private static IEnumerable
GetSaml2LogoutClaims(ClaimsPrincipal principal) - {
- yield return GetClaim(principal, Saml2ClaimTypes.NameId);
- yield return GetClaim(principal, Saml2ClaimTypes.NameIdFormat);
- yield return GetClaim(principal, Saml2ClaimTypes.SessionIndex);
- }
-
- private static Claim GetClaim(ClaimsPrincipal principal, string claimType)
- {
- return ((ClaimsIdentity)principal.Identity).Claims.Where(c => c.Type == claimType).FirstOrDefault();
- }
-
- private static string GetClaimValue(ClaimsPrincipal principal, string claimType)
- {
- var claim = GetClaim(principal, claimType);
- return claim != null ? claim.Value : null;
- }
- }
Samlv2AuthRequest.cs
- public class Samlv2AuthRequest
- {
- public string _id;
- private string _issue_instant;
-
- private string _issuer;
- private string _assertionConsumerServiceUrl;
-
- public enum AuthRequestFormat
- {
- Base64 = 1
- }
-
- public Samlv2AuthRequest(string issuer, string assertionConsumerServiceUrl)
- {
- _id = "_" + Guid.NewGuid().ToString();
- _issue_instant = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture);
-
- _issuer = issuer;
- _assertionConsumerServiceUrl = assertionConsumerServiceUrl;
- }
-
- public string GetRequest(AuthRequestFormat format)
- {
- using (StringWriter sw = new StringWriter())
- {
- XmlWriterSettings xws = new XmlWriterSettings();
- xws.OmitXmlDeclaration = true;
-
- using (XmlWriter xw = XmlWriter.Create(sw, xws))
- {
- xw.WriteStartElement("samlp", "AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol");
- xw.WriteAttributeString("ID", _id);
- xw.WriteAttributeString("Version", "2.0");
- xw.WriteAttributeString("IssueInstant", _issue_instant);
- xw.WriteAttributeString("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
- xw.WriteAttributeString("AssertionConsumerServiceURL", _assertionConsumerServiceUrl);
-
- xw.WriteStartElement("saml", "Issuer", "urn:oasis:names:tc:SAML:2.0:assertion");
- xw.WriteString(_issuer);
- xw.WriteEndElement();
-
- xw.WriteStartElement("samlp", "NameIDPolicy", "urn:oasis:names:tc:SAML:2.0:protocol");
- xw.WriteAttributeString("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
- xw.WriteAttributeString("AllowCreate", "true");
- xw.WriteEndElement();
-
- /*xw.WriteStartElement("samlp", "RequestedAuthnContext", "urn:oasis:names:tc:SAML:2.0:protocol");
- xw.WriteAttributeString("Comparison", "exact");
- xw.WriteStartElement("saml", "AuthnContextClassRef", "urn:oasis:names:tc:SAML:2.0:assertion");
- xw.WriteString("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
- xw.WriteEndElement();
- xw.WriteEndElement();*/
-
- xw.WriteEndElement();
- }
-
- if (format == AuthRequestFormat.Base64)
- {
- //byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(sw.ToString());
- //return System.Convert.ToBase64String(toEncodeAsBytes);
-
- //https://stackoverflow.com/questions/25120025/acs75005-the-request-is-not-a-valid-saml2-protocol-message-is-showing-always%3C/a%3E
- var memoryStream = new MemoryStream();
- var writer = new StreamWriter(new DeflateStream(memoryStream, CompressionMode.Compress, true), new UTF8Encoding(false));
- writer.Write(sw.ToString());
- writer.Close();
- string result = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length, Base64FormattingOptions.None);
- return result;
- }
-
- return null;
- }
- }
-
- //returns the URL you should redirect your users to (i.e. your SAML-provider login URL with the Base64-ed request in the querystring
- public string GetRedirectUrl(string samlEndpoint, string relayState = null)
- {
- var queryStringSeparator = samlEndpoint.Contains("?") ? "&" : "?";
-
- var url = samlEndpoint + queryStringSeparator + "SAMLRequest=" + Uri.EscapeDataString(GetRequest(AuthRequestFormat.Base64));
-
- if (!string.IsNullOrEmpty(relayState))
- {
- url += "&RelayState=" + Uri.EscapeDataString(relayState);
- }
-
- return url;
- }
- }
Samlv2Response.cs
- public class Samlv2Response
- {
- private static byte[] StringToByteArray(string st)
- {
- byte[] bytes = new byte[st.Length];
- for (int i = 0; i < st.Length; i++)
- {
- bytes[i] = (byte)st[i];
- }
- return bytes;
- }
-
- protected XmlDocument _xmlDoc;
- protected readonly X509Certificate2 _certificate;
- protected XmlNamespaceManager _xmlNameSpaceManager; //we need this one to run our XPath queries on the SAML XML
-
- public string Xml { get { return _xmlDoc.OuterXml; } }
-
- public Samlv2Response(string certificateStr, string responseString)
- : this(StringToByteArray(certificateStr), responseString) { }
-
- public Samlv2Response(byte[] certificateBytes, string responseString) : this(certificateBytes)
- {
- LoadXmlFromBase64(responseString);
- }
-
- public Samlv2Response(string certificateStr) : this(StringToByteArray(certificateStr)) { }
-
- public Samlv2Response(byte[] certificateBytes)
- {
- _certificate = new X509Certificate2(certificateBytes);
- }
-
- public void LoadXml(string xml)
- {
- _xmlDoc = new XmlDocument();
- _xmlDoc.PreserveWhitespace = true;
- _xmlDoc.XmlResolver = null;
- _xmlDoc.LoadXml(xml);
-
- _xmlNameSpaceManager = GetNamespaceManager(); //lets construct a "manager" for XPath queries
- }
-
- public void LoadXmlFromBase64(string response)
- {
- UTF8Encoding enc = new UTF8Encoding();
- LoadXml(enc.GetString(Convert.FromBase64String(response)));
- }
-
- public bool IsValid()
- {
- XmlNodeList nodeList = _xmlDoc.SelectNodes("//ds:Signature", _xmlNameSpaceManager);
-
- SignedXml signedXml = new SignedXml(_xmlDoc);
-
- if (nodeList.Count == 0) return false;
-
- signedXml.LoadXml((XmlElement)nodeList[0]);
- return ValidateSignatureReference(signedXml) && signedXml.CheckSignature(_certificate, true) && !IsExpired();
- }
-
- public string GetSamlXmlResponse()
- {
- return _xmlDoc.InnerXml;
- }
-
- //an XML signature can "cover" not the whole document, but only a part of it
- //.NET's built in "CheckSignature" does not cover this case, it will validate to true.
- //We should check the signature reference, so it "references" the id of the root document element! If not - it's a hack
- private bool ValidateSignatureReference(SignedXml signedXml)
- {
- if (signedXml.SignedInfo.References.Count != 1) //no ref at all
- return false;
-
- var reference = (Reference)signedXml.SignedInfo.References[0];
- var id = reference.Uri.Substring(1);
-
- var idElement = signedXml.GetIdElement(_xmlDoc, id);
-
- if (idElement == _xmlDoc.DocumentElement)
- return true;
- else //sometimes its not the "root" doc-element that is being signed, but the "assertion" element
- {
- var assertionNode = _xmlDoc.SelectSingleNode("/samlp:Response/saml:Assertion", _xmlNameSpaceManager) as XmlElement;
- if (assertionNode != idElement)
- return false;
- }
-
- return true;
- }
-
- private bool IsExpired()
- {
- DateTime expirationDate = DateTime.MaxValue;
- XmlNode node = _xmlDoc.SelectSingleNode("/samlp:Response/saml:Assertion[1]/saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData", _xmlNameSpaceManager);
- if (node != null && node.Attributes["NotOnOrAfter"] != null)
- {
- DateTime.TryParse(node.Attributes["NotOnOrAfter"].Value, out expirationDate);
- }
- return DateTime.UtcNow > expirationDate.ToUniversalTime();
- }
-
- public string GetNameID()
- {
- XmlNode node = _xmlDoc.SelectSingleNode("/samlp:Response/saml:Assertion[1]/saml:Subject/saml:NameID", _xmlNameSpaceManager);
- return node.InnerText;
- }
- public string GetUserID()
- {
- return GetCustomAttribute("http://schemas.microsoft.com/identity/claims/objectidentifier");
- }
- public string GetRoles()
- {
- return GetCustomAttribute("User.roles")
- ?? GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/roles") //some providers (for example Azure AD) put last name into an attribute named "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
- ?? GetCustomAttribute("roles"); //some providers put last name into an attribute named "mail"
- }
- public virtual string GetUpn()
- {
- return GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn");
- }
-
- public virtual string GetEmail()
- {
- return GetCustomAttribute("User.email")
- ?? GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress") //some providers (for example Azure AD) put last name into an attribute named "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
- ?? GetCustomAttribute("mail"); //some providers put last name into an attribute named "mail"
- }
-
- public virtual string GetFirstName()
- {
- return GetCustomAttribute("first_name")
- ?? GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname") //some providers (for example Azure AD) put last name into an attribute named "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
- ?? GetCustomAttribute("User.FirstName")
- ?? GetCustomAttribute("givenName"); //some providers put last name into an attribute named "givenName"
- }
-
- public virtual string GetLastName()
- {
- return GetCustomAttribute("last_name")
- ?? GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname") //some providers (for example Azure AD) put last name into an attribute named "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
- ?? GetCustomAttribute("User.LastName")
- ?? GetCustomAttribute("sn"); //some providers put last name into an attribute named "sn"
- }
-
- public virtual string GetDepartment()
- {
- return GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/department")
- ?? GetCustomAttribute("department");
- }
-
- public virtual string GetPhone()
- {
- return GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone")
- ?? GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/telephonenumber");
- }
-
- public virtual string GetCompany()
- {
- return GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/companyname")
- ?? GetCustomAttribute("organization")
- ?? GetCustomAttribute("User.CompanyName");
- }
-
- public virtual string GetLocation()
- {
- return GetCustomAttribute("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/location")
- ?? GetCustomAttribute("physicalDeliveryOfficeName");
- }
-
- public string GetCustomAttribute(string attr)
- {
- XmlNode node = _xmlDoc.SelectSingleNode("/samlp:Response/saml:Assertion[1]/saml:AttributeStatement/saml:Attribute[@Name='" + attr + "']/saml:AttributeValue", _xmlNameSpaceManager);
- return node == null ? null : node.InnerText;
- }
-
- //returns namespace manager, we need one b/c MS says so... Otherwise XPath doesnt work in an XML doc with namespaces
- //see https://stackoverflow.com/questions/7178111/why-is-xmlnamespacemanager-necessary
- private XmlNamespaceManager GetNamespaceManager()
- {
- XmlNamespaceManager manager = new XmlNamespaceManager(_xmlDoc.NameTable);
- manager.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);
- manager.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion");
- manager.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol");
-
- return manager;
- }
- }
2.控制器调用
- //根据企业码查看当前的企业码是否存在,然后并且返回跳转数据
- [Route("sso/ssologin")]
- [HttpGet]
- public async Task
> SsoLogin(string CorporateCode) - {
- if (!string.IsNullOrEmpty(CorporateCode))
- {
- //查询企业码是否存在
- var result = await ——**.GetSpIdp(CorporateCode, "", "", "");
- if (result.Rows.Count == 0)
- {
- return ApiResultHelper.renderError("CorporateCode is null ");
- }
- if (result.Rows[0]["isena**"].ToString() == "0")
- {
- return ApiResultHelper.renderError("CorporateCode is null ");
- }
- var samlEndpoint = result.Rows[0]["idpsso**"].ToString();
- var sp_consumer_url = result.Rows[0]["spconsum**"].ToString();
- var request = new Samlv2AuthRequest(
- "****", //TODO: put your app's "entity ID" here urn:auth0:YOUR_TENANT:YOUR_CONNECTION_NAME
- sp_consumer_url //TODO: put Assertion Consumer URL (where the provider should redirect users after authenticating)
- );
- //redirect the user to the SAML provider
- return ApiResultHelper.renderSuccess(request.GetRedirectUrl(samlEndpoint), "success!");//, "https://www.corriere.it/"
- }
- return ApiResultHelper.renderError("CorporateCode is null ");
- }
-
- [Route("**/**/**/{param1}/{param2}")]
- [HttpPost]
- //ASP.NET MVC action method... But you can easily modify the code for Web-forms etc.
- public async Task
> Samlv2Consumer(string param1, string param2) - {
- var url = "";
- var result = await ——**.GetSpIdp("", "", param2, param1);
- if (result.Rows.Count == 0)
- {
- return ApiResultHelper.renderError("request is fail");
- }
- // 1. TODO: specify the certificate that your SAML provider gave you
- string samlCertificate = result.Rows[0]["idp_certificate"].ToString();
- // string samlCertificate = ByFingerpritntGetBase64("**");
-
- // 2. Let's read the data - SAML providers usually POST it into the "SAMLResponse" var
- var samlResponse = new Samlv2Response(samlCertificate, Request.Form["SAMLResponse"]);
-
- //for log purpose
- var samlTextResponse = samlResponse.GetSamlXmlResponse();
- Dictionary<string, string> dic = new Dictionary<string, string>();
- // 3. We're done!
- if (samlResponse.IsValid())
- {
- //WOOHOO!!! user is logged in
- //Some more optional stuff for you
- //let's extract username/firstname etc
- string username, email, firstname, lastname, userid;
- try
- {
- userid = samlResponse.GetUserID();
- username = samlResponse.GetNameID();
- email = samlResponse.GetEmail();
- firstname = samlResponse.GetFirstName();
- lastname = samlResponse.GetLastName();
-
- /*删除部分代码*/
- url = Appsettings.GetNode("**") + "error";
- return Redirect(url);
- }
- catch (Exception ex)
- {
- //insert error handling code
- //no, really, please do
- return ApiResultHelper.renderError(ex.Message);
- }
-
- //user has been authenticated, put your code here, like set a cookie or something...
- //or call FormsAuthentication.SetAuthCookie()
- //or call context.SignInAsync() in ASP.NET Core
- //or do something else
- }
- return ApiResultHelper.renderError(false);
- }