【问题标题】:Create SAML response based on SAML request根据 SAML 请求创建 SAML 响应
【发布时间】:2014-12-26 04:18: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?

谢谢。

【问题讨论】:

    标签: java saml saml-2.0 assertions


    【解决方案1】:

    虽然这是一篇旧文章,但我正在添加我发现有用的示例代码和参考。

    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);    
     }
    

    }

    【讨论】:

      【解决方案2】:

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

      另外一个小补充是SP也可能对他的请求进行签名,所以你可能在双方都有签名验证。 SP 端的验证是强制性的。

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

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

      【讨论】:

      • 我想测试我的应用程序。我创建了 SAML 请求。我尝试了一些 IdP,但它不是免费的。所以我正在尝试创建自己的 SAML 响应。我有一个样本证书。我想签署响应并发送到我的应用程序。
      • 我认为您可以免费使用 SalesForce 进行测试。这是 PicketLink 的文档,但您也可以将其应用于您的 SP:docs.jboss.org/author/display/PLINK/…
      • 我们如何使用 java 代码从 SAMLResponse 中获取 SAMLAssertion?
      猜你喜欢
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2015-05-19
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 2020-02-26
      • 1970-01-01
      相关资源
      最近更新 更多