当前位置: 首页 > 知识库问答 >
问题:

基于SAML请求创建SAML响应

秋兴思
2023-03-14

我已经开发了一个Java web应用程序,我想实现SAML。我认为这些是实现SAML的正确步骤。

  1. 服务提供商(SP,在本例中是我的应用程序)向IdP发送SAML身份验证请求
  2. 然后,IdP对其进行验证,创建SAML响应断言,并将其与证书一起签名,然后发送回SP。
  3. 然后,SP使用密钥库中证书的公钥对其进行验证,并在此基础上进一步进行

我有一个示例代码,可以创建SAML请求

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    ID="_c7b796f4-bc16-4fcc-8c1d-36befffc39c2" Version="2.0"
    IssueInstant="2014-10-30T11:21:08Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    AssertionConsumerServiceURL="http://localhost:8080/mywebapp/consume.jsp">
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8080/mywebapp
    </saml:Issuer>
    <samlp:NameIDPolicy
        Format="urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified"
        AllowCreate="true"></samlp:NameIDPolicy>
    <samlp:RequestedAuthnContext Comparison="exact">
        <saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
        </saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

我可以对其进行编码并发送给IdP。

我想创建示例Java代码以获取此SAML请求,然后创建SAML响应。如何解码请求并验证它并创建响应?我需要使用证书签署saml响应吗?然后发送回SP?

谢谢

共有2个答案

翟俊远
2023-03-14

您列出的步骤或多或少都是正确的。我要指出的唯一一点是,如果单词sends(例如,“SP…向IdP发送SAML身份验证请求”)的含义,您必须小心。SAML允许SP和IdP之间零直接通信的身份验证场景。

另一个小的补充是SP也可以签署他的请求,因此您可以在双方都进行签名验证。SP端的验证是强制性的。

如果您想实现SAML,您可能需要检查现有解决方案之一,例如Shibboleth。如果您在Spring和JBoss等平台上,您可能需要检查Spring Security SAML或JBoss PicketLink。如果您想进入较低级别,请检查OpenSAML。

在我的公司,我们有JBoss作为标准,并且对PicketLink非常满意。

杜鸿彩
2023-03-14

虽然这是一篇旧文章,但我正在添加我觉得有用的示例代码和引用。

SAMLResponse = hreq.getParameter("SAMLResponse");
InputSource inputSource = new InputSource(new StringReader(SAMLResponse));
SAMLReader samlReader = new SAMLReader();                   
response2 = org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);

现在验证数字签名:

org.opensaml.saml2.core.Response response2 = (org.opensaml.saml2.core.Response)samlReader.readFromFile(inputSource);  
//To fetch the digital signature from the response.
Signature signature  = response2.getSignature(); 
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(domainName);
//pull out the public key part of the certificate into a KeySpec
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
//get KeyFactory object that creates key objects, specifying RSA - java.security.KeyFactory
KeyFactory keyFactory = KeyFactory.getInstance("RSA");                  
//generate public key to validate signatures
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//we have the public key                    
BasicX509Credential publicCredential = new BasicX509Credential();
//add public key value
publicCredential.setPublicKey(publicKey);
//create SignatureValidator
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
//try to validate
try{
signatureValidator.validate(signature); 
catch(Exception e){
//
} 

现在获取断言映射:

samlDetailsMap = setSAMLDetails(response2);

在上面的逻辑中,使用下面的私有方法来提取所有断言属性。最后,您将拥有发送给您的所有字段的映射。

 private Map<String, String> setSAMLDetails(org.opensaml.saml2.core.Response  response2){
        Map<String, String> samlDetailsMap = new HashMap<String, String>();
        try {
            List<Assertion> assertions = response2.getAssertions();
            LOGGER.error("No of assertions : "+assertions.size());
            for(Assertion assertion:assertions){
                List<AttributeStatement> attributeStatements = assertion.getAttributeStatements();
                for(AttributeStatement attributeStatement: attributeStatements){
                    List<Attribute> attributes = attributeStatement.getAttributes();
                    for(Attribute attribute: attributes){
                        String name = attribute.getName();                          
                        List<XMLObject> attributes1 = attribute.getAttributeValues();
                        for(XMLObject xmlObject : attributes1){
                            if(xmlObject instanceof XSString){
                                samlDetailsMap.put(name, ((XSString) xmlObject).getValue());
                                LOGGER.error("Name is : "+name+" value is : "+((XSString) xmlObject).getValue());
                            }else if(xmlObject instanceof XSAnyImpl){
                                String value = ((XSAnyImpl) xmlObject).getTextContent();

                                samlDetailsMap.put(name, value);

                            }         
                    }
                }
            }       
       }
      } catch (Exception e) {             
          LOGGER.error("Exception occurred while setting the saml details");        
        }       
        LOGGER.error("Exiting from  setSAMLDetails method"); 
        return samlDetailsMap;
    }

添加新类SAMLReader如下:

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.opensaml.DefaultBootstrap;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.UnmarshallingException;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class SAMLReader {

 private static DocumentBuilder builder;

 static{
        try{
            DefaultBootstrap.bootstrap ();
            DocumentBuilderFactory factory = 
                    DocumentBuilderFactory.newInstance ();
                factory.setNamespaceAware (true);        
            builder = factory.newDocumentBuilder ();
        }catch (Exception ex){
            ex.printStackTrace ();
        }
    }



/**
 * 
 * @param filename
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public XMLObject readFromFile (String filename)
            throws IOException, UnmarshallingException, SAXException{
            return fromElement (builder.parse (filename).getDocumentElement ());    
}
/**
 *      
 * @param is
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public XMLObject readFromFile (InputStream is)
                throws IOException, UnmarshallingException, SAXException{
                return fromElement (builder.parse (is).getDocumentElement ());    
}
/**
 *      
 * @param is
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public XMLObject readFromFile (InputSource  is)
                throws IOException, UnmarshallingException, SAXException{                   
                return fromElement (builder.parse (is).getDocumentElement ());    
}

/**
 * 
 * @param element
 * @return
 * @throws IOException
 * @throws UnmarshallingException
 * @throws SAXException
 */
public static XMLObject fromElement (Element element)
            throws IOException, UnmarshallingException, SAXException{   
    return Configuration.getUnmarshallerFactory ()
                .getUnmarshaller (element).unmarshall (element);    
 }

}

 类似资料:
  • 作为服务提供商(SP),我正在尝试使用URL重定向流创建SLO请求。在阅读了很多文章后,这就是我想出的: 创建一个注销请求xml并对其进行数字签名 将请求xml转换为base64 url编码字符串 将base64编码的请求xml作为查询字符串附加到SLO url 将页面重定向到上述构造的url(IDP) 然后IDP将在其结束时为所有SP执行必要的SLO步骤并重定向回我们当前的应用程序(发起SLO请

  • 在使用OpenAM和Spring security saml2示例时,我遇到了一个问题。 我按照本教程配置了Spring saml2示例以及OpenAM。现在,我在选择 http://localhost:8080/OpenAM-12.0.0 并单击登录后遇到错误,但浏览器返回“HTTP状态500 - SAML请求无效”。 示例项目和OpenAM都部署在同一个tomcat服务器上,但是我没有在任何日

  • 我在使用Spring-SAML的webapp中看到了极其复杂和不一致的行为。 根据url模式选择两个IDP-使用CustomSAMLContextProviderImpl实现(基于url工作良好,它重定向到配置的IDP) 使用带有ExtendedMetadataManager列表的CachingMetadataManager配置了两个SAML Provider/IDP。基于此处的解释和代码示例 S

  • 我已经通过SAML通过基于OpenAM的IDP验证了我的应用程序。现在我想通过SAML注销我的应用程序。所以我的问题是: > 我选择了“spSingleLogoutInit.jsp”实现,这样做对吗? 如果是,而不是RelayState,我已经给出了我的应用程序的登录页面,即“http://www.myexample.com/login.do. 3.在sessionIndex中,我需要提供当前会话

  • 我有一个Java web应用程序。我想为我的应用程序实现SAML单点登录登录。我有这个GitHub onelogin程序来发送请求并获得响应。但它没有正常工作。我在那里创建了一个帐户。但我没有企业账户。当我运行应用程序时,它将进入onelogin登录页面。我试图登录,但在响应中没有返回任何权限,表明我没有权限。如果我也提供了错误的凭据,那么它不会给出任何SAML响应。 所以我决定创建一个断言并签名