前文OpenSAML 使用引导 I : 简介介绍了OpenSAML的基础概况, 本文将从Service Provider(SP)角度出发,讲解如何使用OpenSAML如申请身份鉴别请求(AuthnRequest),并从IDP出得到断言的引用标识——SAML Artifact
相关阅读SAML2.0入门指南,
源码地址:https://github.com/sunrongxin7666/OpenSAML-ref-project-demo-v3.git
用户对SP中的资源进行请求。这一步中,SP会做出判断是否要需要鉴别用户身份。一般而言,如果当前用户在SP上不存在已经被认证的session信息,就需要对用户提出身份鉴别请求。
如果需要对用户进行身份鉴别,SP将会创建AuthnRequest对象指明要用户要如何才能被鉴别。这个AuthnRequest将会被以URL参数的形式编码到HTTP请求中,然后通过浏览器重定向到IDP。
IDP解码获得AuthnRequest并根据其中的要求来鉴别用户身份。
如果用户通过身份鉴别,IDP会将认证信息和一个标识联系起来,这个标识被称为SAML制品(SAML Artifact,简称制品)。这个制品也是以URL参数的形式加入到HTTP请求中,然后重定向发回SP。
SP创建ArtifactResolve对象,并将Artifact包含在其中。这个ArtifactResolve对象通过SOAP请求发送到IDP。
IDP接受到ArtifactResolve并将Artifact抽离出来。IDP通过ArtifactResponse响应SOAP请求,ArtifactResponse中包含认证信息,以SAML断言格式包含在其中。
这里讨论鉴别过程的第一步。实际上,用户拦截并不是认证过程的一部分,但是确实这一步却决定着鉴别过程是否会发生。
用户的交互以拦截非认证的用户去尝试获取SP资源时开始。对于Java Web应用而言,Servlet过滤器是一个很好的选择去做这样的拦截。过滤器检查是否当前用户已被认证,如果已被验证则允许访问,反之要启动身份验证流程。
public class AccessFilter implements Filter {
private static Logger logger = LoggerFactory
.getLogger(AccessFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException{
JavaCryptoValidationInitializer javaCryptoValidationInitializer =
new JavaCryptoValidationInitializer();
try {
javaCryptoValidationInitializer.init();
} catch (InitializationException e) {
e.printStackTrace();
}
try {
InitializationService.initialize();
} catch (InitializationException e) {
new RuntimeException("Initialization failed");
}
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest =
(HttpServletRequest) request;
HttpServletResponse httpServletResponse =
(HttpServletResponse) response;
if (httpServletRequest.getSession().getAttribute(
SPConstants.AUTHENTICATED_SESSION_ATTRIBUTE) != null) {
chain.doFilter(request, response);
} else {
//将本来要访问的目标路径保存到Session
setGotoURLOnSession(httpServletRequest);
redirectUserForAuthentication(httpServletResponse);
}
}
}
如果用户已经通过身份鉴别,则session中会有AUTHENTICATED_SESSION_ATTRIBUTE,此时用户是已经被认证的,过滤器应该不对该操作做任何处理;反之,如果AUTHENTICATED_SESSION_ATTRIBUTE并不存在则意味着需要开启鉴别流程:保留当前的目标URL,然后重定向到IDP。
AuthnRequest authnRequest = OpenSAMLUtils
.buildSAMLObject(AuthnRequest.class);
其中如下属性需要设置:
authnRequest.setIssueInstant(new DateTime());
authnRequest.setDestination(getIPDDestination());
authnRequest.setProtocolBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
authnRequest
.setAssertionConsumerServiceURL(getAssertionConsumerEndpoint());
authnRequest.setID(OpenSAMLUtils.generateSecureRandomId());
注意:获得安全随机数的方法:
RandomIdentifierGenerationStrategy secureRandomIdGenerator = new RandomIdentifierGenerationStrategy();
String id = secureRandomIdGenerator.generateIdentifier();
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(SPConstants.SP_ENTITY_ID);
authnRequest.setIssuer(issuer);
NameIDPolicy nameIDPolicy =
OpenSAMLUtils.buildSAMLObject(NameIDPolicy.class);
nameIDPolicy.setFormat(NameIDType.TRANSIENT);
nameIDPolicy.setAllowCreate(true);
authnRequest.setNameIDPolicy(nameIDPolicy);
NameID Formats:
在SAML中有多种NameID的格式存在,比如Kerberos,邮箱以及Windows域限定名称(Windows Domain Qualified Name),这里要特别说明如下两种:
- 持久标识(Persistent Identifier):一个随机的ID标识被分配给用户,以避免暴露用户的真实账户。无论用户何时登入,都会返回相同的标识。SP可以将这个标识和本地的用户账号绑定;
- 临时标识(Transient Identifier):临时标识是一个和用户账户没有关系的随机标识,不会被重复使用,用户每次登陆所返回的标识都是不一样的。
authnRequest.setRequestedAuthnContext(buildRequestedAuthnContext());
可以如此获得*RequestedAuthnContext *
RequestedAuthnContext requestedAuthnContext = OpenSAMLUtils
.buildSAMLObject(RequestedAuthnContext.class);
AuthnContextClassRef passwordAuthnContextClassRef
= OpenSAMLUtils.buildSAMLObject(AuthnContextClassRef.class);
passwordAuthnContextClassRef
.setAuthnContextClassRef(AuthnContext.PASSWORD_AUTHN_CTX);
requestedAuthnContext.getAuthnContextClassRefs()
.add(passwordAuthnContextClassRef);
requestedAuthnContext
.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);
同时请求认证上下文也可能有多个,如果是这样的情况他们就要安装优先级排列。
Comparison代表着如何IDP要如何依据所给出的鉴别方式选项处理鉴别结果,其取值包括:
为了帮助数字签名以及序列化参数以发送重定向消息,OpenSAML提供了HTTPRedirectDefalteEncoder,它将帮助我们来对于AuthnRequest进行序列化和签名,并把消息和用户一起重定向到Idp。
OpenSAML message encoders是对SAML消息传输的一种抽象封装,HTTPRedirectDefalteEncoder也是如此,它是对HTTP重定向绑定过程的抽象。
编码器(encoder)用来处理数据对象,也就是消息上下文(MessageContext),其中包含消息的信息内容和传输细节。
在新版OpenSAML中,Message Context相关的类如下:
以上contexts都是主环境上下文的子内容创建的,以下使用实例:
MessageContext context = new MessageContext();
SAMLPeerEntityContext peerEntityContext = context.getSubcontext(SAMLPeerEntityContext.class, true);
SAMLEndpointContext endpointContext = peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
getSubcontext方法的最后一个参数表示如果存在该信息不存在是否创建,当然也可以使用setter方法来设置:
endpointContext.setEndpoint(getIPDEndpoint());
一般而言,SAMLEndpointContext
是AuthnRequest所必须的,用以指明消息发送的目的地。SAMLEndpointContext是SAMLPeerEntityContext的子内容。如何创建请见下一部分。
MessageContext context = new MessageContext();
context.setMessage(authnRequest);
SAMLPeerEntityContext peerEntityContext = context.getSubcontext(SAMLPeerEntityContext.class, true);
SAMLEndpointContext endpointContext = peerEntityContext.getSubcontext(SAMLEndpointContext.class, true);
SingleSignOnService endpoint = OpenSAMLUtils.buildSAMLObject(SingleSignOnService.class);
endpoint.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
endpoint.setLocation(getIPDSSODestination());
context.setPeerEntityEndpoint(endpoint);
endpointContext.setEndpoint(endpoint);
SignatureSigningParameters signatureSigningParameters = new SignatureSigningParameters();
signatureSigningParameters.setSigningCredential(SPCredentials.getCredential());
signatureSigningParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
context.getSubcontext(SecurityParametersContext.class,true)
.setSignatureSigningParameters(signatureSigningParameters);
SecurityParametersContext被设置后,HTTPRedirectDefalteEncoder会自动帮我们对AuthnRequest签名,并添加签名结果和签名算法到URL参数中。
HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
encoder.setMessageContext(context);
encoder.setHttpServletResponse(httpServletResponse);
encoder.initialize();
encoder.encode();
以下是redirect URL和 AuthnRequest XML的实例:
接下来SP将使用SOAP协议将Artifact发给IDP换取断言信息(Assertion),由于篇幅有限,这部分内容将会在后面的文章中讲解,敬请期待,欢迎关注和指教。
最后给出源码地址 https://github.com/sunrongxin7666/OpenSAML-ref-project-demo-v3.git
更多关于SAML协议的是实现的内容,请参见本人编写的一系列教程文章,其介绍如何使用OpenSAML,欢迎阅读指正: