有一些测试资源,例如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);
}
Signer
为org.opensaml.xml.signature.Signer
** Update **
使用SAMLMessageContext
和HTTPRedirectDeflateEncoder
发送重定向
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>();
}