如何使用 Java 正确准备'HTTP Redirect Binding' SAML 请求?



有一些测试资源,例如http://sometestresource.com,我在http://sometestresource.com/Login上请求授权,我重定向到idP提供商:http://someidpprovoder.com/idp/authn/CommonLogin

嗅探器检测到重定向到https://someidpprovoder.com/idp/profile/SAML2/Redirect/SSO

SAMLRequest=fZJda...D&RelayState=https%3A%2F%2Fwww.sometestresource.com%2Fcms%2Fextentions%2Fsimplesaml%2Fwww%2Fmodule.php%2Fcore%2Fauthenticate.php%3Fas%3D100000za-sp
&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1
&Signature=dAx0M...D

好,我写了一个简单的servlet应用程序,我想获得相同的功能- Web浏览器SSO配置文件

My ServiceProvider servlet:

@WebServlet(urlPatterns={"/ServiceProvider"},
            initParams={ @WebInitParam(name="Issuer", value="some.issuer"),
                         @WebInitParam(name="IdpUrl", value="https://someidpprovoder.com/idp/profile/SAML2/Redirect/SSO"),
                         @WebInitParam(name="ConsumerUrl", value="http://localhost:8080/com.secure.isusia-1.0-SNAPSHOT/ServiceProvider")
            } )
public class ServiceProvider  extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private SamlConsumerManager consumer;
    public void init(ServletConfig config) throws ServletException {
        try {
            consumer = new SamlConsumerManager(config);
        } catch (ConfigurationException e) {
            throw new ServletException("Errow while configuring SAMLConsumerManager", e);
        }
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException,
            IOException {
        doPost(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String responseMessage = request.getParameter("SAMLResponse");
        if (responseMessage != null) { 
            // response from the identity provider
            if (result == null) {
                // lets logout the user
            } else if (result.size() == 1) {
                 //
                 // No user attributes are returned, so just goto the default
                 //home page.
                 //
            } else if (result.size() > 1) {
                 // We have received attributes, so lets show them in the
                 // attribute home page.
                 //
            } else {
                // something wrong, re-login
            }
        } else { 
            /* time to create the authentication request or logout request */
        }
    }
}
我SamlConsumerManager

:

public class SamlConsumerManager {
    private String consumerUrl;
    private String authReqRandomId;
    private String relayState;
    private String issuerId;
    private String idpUrl;
    private char[] password = "...".toCharArray();
    private String alias = "...";
    private String sigAlg = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
    private Signature signature;
    private BasicX509Credential credential;
    private Element authDOM;
    public SamlConsumerManager(ServletConfig servletConfig) throws ConfigurationException {
        authReqRandomId = Integer.toHexString(new Double(Math.random()).intValue());        
        consumerUrl = servletConfig.getInitParameter("ConsumerUrl");
        idpUrl = servletConfig.getInitParameter("IdpUrl");
        issuerId = servletConfig.getInitParameter("Issuer");
        DefaultBootstrap.bootstrap();
    }
    public String buildRequestMessage(HttpServletRequest request) {
        RequestAbstractType requestMessage = null;
        if (request.getParameter("logout") == null) {
            // time to build the authentication request message
        } else { 
            // ok, user needs to be single logged out
        }
        String encodedRequestMessage = encodeRequestMessage(requestMessage);
        /* SAML2 Authentication Request is appended to IP's URL */
        return idpUrl + "?SAMLRequest=" + encodedRequestMessage + "&SigAlg=" + sigAlg;
    }
    private LogoutRequest buildLogoutRequest(String user) {
        LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject();
        logoutReq.setID(Util.createID());
        DateTime issueInstant = new DateTime();
        logoutReq.setIssueInstant(issueInstant);
        logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000));
        IssuerBuilder issuerBuilder = new IssuerBuilder();
        Issuer issuer = issuerBuilder.buildObject();
        issuer.setValue(issuerId);
        logoutReq.setIssuer(issuer);
        NameID nameId = new NameIDBuilder().buildObject();
        nameId.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
        nameId.setValue(user);
        logoutReq.setNameID(nameId);
        SessionIndex sessionIndex = new SessionIndexBuilder().buildObject();
        sessionIndex.setSessionIndex(UIDGenerator.generateUID());
        logoutReq.getSessionIndexes().add(sessionIndex);
        logoutReq.setReason("Single Logout");
        return logoutReq;
    }
    private AuthnRequest buildAuthnRequestObject() {
        /* Building Issuer object */
        IssuerBuilder issuerBuilder = new IssuerBuilder();
        Issuer issuer = issuerBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:assertion", "Issuer", "samlp");
        issuer.setValue(issuerId);
        /* NameIDPolicy */
        NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder();
        NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject();
        nameIdPolicy.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent");
        nameIdPolicy.setSPNameQualifier("Isser");
        nameIdPolicy.setAllowCreate(new Boolean(true));
        /* AuthnContextClass */
        AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
        AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject(
                        "urn:oasis:names:tc:SAML:2.0:assertion",
                        "AuthnContextClassRef",
                        "saml");
        authnContextClassRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
        /* AuthnContex */
        RequestedAuthnContextBuilder requestedAuthnContextBuilder =
                new RequestedAuthnContextBuilder();
        RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
        requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
        requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
        DateTime issueInstant = new DateTime();
        /* Creation of AuthRequestObject */
        AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
        AuthnRequest authRequest =
                authRequestBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol",
                        "AuthnRequest", "samlp");
        authRequest.setForceAuthn(new Boolean(false));
        authRequest.setIsPassive(new Boolean(false));
        authRequest.setIssueInstant(issueInstant);
        authRequest.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
        authRequest.setAssertionConsumerServiceURL(consumerUrl);
        authRequest.setIssuer(issuer);
        authRequest.setNameIDPolicy(nameIdPolicy);
        authRequest.setRequestedAuthnContext(requestedAuthnContext);
        authRequest.setID(authReqRandomId);
        authRequest.setVersion(SAMLVersion.VERSION_20);
        KeyStore  keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream fileInputStream = new FileInputStream(new File("....jks"));        
        keyStore.load(fileInputStream, password);
        fileInputStream.close();
        KeyStore.PrivateKeyEntry privateKeyEntry = null;
        privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(password));
        PrivateKey privateKey = privateKeyEntry.getPrivateKey();
        X509Certificate certificate = (X509Certificate) privateKeyEntry.getCertificate();
        credential = new BasicX509Credential();
        credential.setEntityCertificate(certificate);
        credential.setPrivateKey(privateKey);
        try {
            DefaultBootstrap.bootstrap();
        } catch (ConfigurationException e) {
            e.printStackTrace();
        }
        signature = (Signature) org.opensaml.xml.Configuration.getBuilderFactory().getBuilder(org.opensaml.xml.signature.Signature.DEFAULT_ELEMENT_NAME)
                .buildObject(org.opensaml.xml.signature.Signature.DEFAULT_ELEMENT_NAME);
        signature.setSigningCredential(credential);
        signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
        signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
        authRequest.setSignature(signature);
        return authRequest;
    }
    private String encodeRequestMessage(RequestAbstractType requestMessage)
            throws MarshallingException,
            IOException {
        Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(requestMessage);
        Element authDOM = marshaller.marshall(requestMessage);
        Deflater deflater = new Deflater(Deflater.DEFLATED, true);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DeflaterOutputStream deflaterOutputStream =
                new DeflaterOutputStream(byteArrayOutputStream,
                        deflater);
        StringWriter rspWrt = new StringWriter();
        XMLHelper.writeNode(authDOM, rspWrt);
        deflaterOutputStream.write(rspWrt.toString().getBytes());
        deflaterOutputStream.close();
        /* Encoding the compressed message */
        String encodedRequestMessage =
                Base64.encodeBytes(byteArrayOutputStream.toByteArray(),
                        Base64.DONT_BREAK_LINES);
        return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim();
    }
}

然而,而不是从我的idP登录页面,我得到一个错误页面。

我做错了什么?我想知道如何正确准备一份授权申请。例如,我不明白如何在URL中添加签名:

&Signature=dAx0M...D

更新
    我正在使用SP发起的Web单点登录。基于Shibboleth
  • 远程idP(在客户端,我对此一无所知。只是它是在Shibboleth的基础上写的。

你需要签名,

in buildAuthnRequestObject under authRequest.setSignature(signature);

添加
Configuration.getMarshallerFactory().getMarshaller(authRequest).marshall(authRequest);`
    if(signature!=null) {
        Signer.signObject(signature);
    }

Signerorg.opensaml.xml.signature.Signer

** Update **

使用SAMLMessageContextHTTPRedirectDeflateEncoder发送重定向

public void doAuthenticationRedirect (HttpServletResponse response, final HttpSession currentSessiond) throws Exception {  
    /** Generate your authnrequest **/
    AuthnRequest authnRequest = generateAuthnRequest();  
    /** Create an adapter from tomcat response servlet **/
    HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter(response, true); 
    /** 302 status code **/
    responseAdapter.setStatusCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
    /** Build blank context **/
    SAMLMessageContext<?, AuthnRequest, ?> context =  SAMLUtility.makeSamlMessageContext();
    /** Set entity end point**/
    context.setPeerEntityEndpoint(endpointObject);  
    context.setOutboundSAMLMessage(authnRequest);  
   /** Sign this request **/
    context.setOutboundSAMLMessageSigningCredential(getSigningCredential());  
    context.setOutboundMessageTransport(responseAdapter);
     /** Run encoder **/
    try {  
        runEncoder(new HTTPRedirectDeflateEncoder(), context);
    } catch (Throwable t) {  
        Log.error("Error endcoding AuthnRequest: ",t);
    }  
}  

 /** 
  * Encode our Context and send it
  * 
  * @param encoder
  * @param context
  * @throws IOException
  * @throws MessageEncodingException
  */
  private void runEncoder(MessageEncoder encoder, MessageContext context)      throws IOException, MessageEncodingException {
      encoder.encode(context);
  }

这里是makeSamlMessageContext(),如果你需要的话

 public static <TI extends SAMLObject, TO extends SAMLObject, TN extends SAMLObject>
    SAMLMessageContext<TI, TO, TN> makeSamlMessageContext() {
      return new BasicSAMLMessageContext<TI, TO, TN>();
 }

相关内容

  • 没有找到相关文章

最新更新