除了标准的用户名/密码表单身份验证之外,我们还需要实现一个支持使用另一个外部 IDP 进行身份验证/授权的 IDP。Picketlink 是否支持这种场景开箱即用?
以下是所需的组件:
- 多个依赖于我们的 IDP(我们称之为 IDP-Internal)进行身份验证的 SP。
- IDP-Internal 应该支持使用 LdapLoginModule 和 DatabaseLogingModule 对不同用户组进行用户名/密码形式的身份验证。它还应该支持使用另一个 IDP 进行身份验证(我们称之为 IDP-External)
- IDP-External 来自具有一些 SAML 扩展的外部提供商。
我们目前已经使用 picketlink 实现了 SP 和 IDP-Internal(遵循 picketlink 示例)。但是,IDP-Internal 到 IDP-External 的集成目前是使用一些手工/解析 SAML 请求/响应的 hacky 借用代码实现的(这里没有纠察链接)。我已经设法通过扩展 Picketlink 的 IDPWebBrowserSSOValve 将这个 hacky 代码集成到 IDP-Internal 中。此自定义 Valve 解析来自 IDP-External 的传入 SAMLResponse,并使用类似于在 SP 端完成的 SAML2LoginModule 进行本地容器身份验证。
这是自定义阀门:
public class IDPInternalAuthentiationValve extends IDPWebBrowserSSOValve {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String samlResponseXML = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
if (isNotNull(samlResponseXML)) {
// This is a SAML Response from IDP-External
// Parse SAML and get subjectName and roles
// Lookup subject in an internal database and get local roles
ServiceProviderSAMLContext.push(subjectName, combinedRoles);
// Authenticate locally using SAML2LoginModule
Principal principal = container.getRealm().authenticate(subjectName, ServiceProviderSAMLContext.EMPTY_PASSWORD);
ServiceProviderSAMLContext.clear();
Session session = request.getSessionInternal(false);
// Save the authenticated Principal in our session
session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
String samlRequestFromSession = (String)session.getNote(GeneralConstants.SAML_REQUEST_KEY);
// This is the request from SP
if (isNotNull(samlRequestFromSession)) {
// Send a response back to SP using the principal created above
processSAMLRequestMessage(request, response);
}
} else {
// Username/password form authentication flow
super.invoke(request, response);
}
}
}
这个解决方案虽然有效,但感觉有点笨拙。有没有更好的方法来做到这一点只使用纠察队?