SAML Java SSO 单点登录
简介
在企业应用中,用户需要进行多次登录操作才能访问不同的系统,这给用户带来了不便。而单点登录(Single Sign-On,简称SSO)技术的出现解决了这个问题,它可以让用户一次登录后,就可访问多个系统。SAML(Security Assertion Markup Language)是一种用于实现SSO的开放标准,它基于XML并使用安全令牌进行身份验证和授权。
本文将介绍如何使用Java实现SAML SSO单点登录,并提供相应的代码示例。
SAML 单点登录流程
SAML单点登录的流程如下:
- 用户访问应用系统A,并尚未登录。
- 应用系统A将用户重定向到身份提供者(Identity Provider,简称IdP)的登录页面。
- 用户在IdP的登录页面中输入用户名和密码进行身份验证。
- IdP验证用户身份后,生成SAML断言(Assertion),并将其加密后发送给应用系统A。
- 应用系统A通过解密和验证SAML断言,确认用户身份。
- 应用系统A可以根据用户的权限等信息,决定是否授权用户访问系统。
- 用户在应用系统A中进行操作,当访问其他应用系统时,无需重新登录。
SAML SSO Java 实现
下面通过一个简单的示例来演示如何使用Java实现SAML SSO单点登录。
首先,我们需要添加SAML依赖,可以使用Maven来管理依赖。在pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-impl</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-profile-api-impl</artifactId>
<version>3.4.3</version>
</dependency>
接下来,我们创建一个SAMLUtil工具类,用于生成SAML请求和解析SAML断言:
import org.opensaml.saml2.core.*;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.provider.FileBackedHTTPMetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.parse.BasicParserPool;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xml.security.x509.X509Credential;
import org.opensaml.xml.signature.*;
import org.opensaml.xml.signature.impl.SignatureBuilder;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
public class SAMLUtil {
private static XMLObjectBuilderFactory builderFactory;
static {
try {
builderFactory = Configuration.getBuilderFactory();
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
public static AuthnRequest createAuthnRequest(String issuer, String destination) {
AuthnRequest authnRequest = (AuthnRequest) builderFactory.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME).buildObject(AuthnRequest.DEFAULT_ELEMENT_NAME);
authnRequest.setID("_" + SAMLUtil.generateID());
authnRequest.setDestination(destination);
authnRequest.setIssuer(createIssuer(issuer));
authnRequest.setIssueInstant(SAMLUtil.getCurrentTime());
authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
authnRequest.setAssertionConsumerServiceURL(destination);
authnRequest.setVersion(SAMLVersion.VERSION_20);
authnRequest.setForceAuthn(false);
authnRequest.setIsPassive(false);
return authnRequest;
}
private static Issuer createIssuer(String issuer) {
Issuer issuerElement = (Issuer) builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME).buildObject(Issuer.DEFAULT_ELEMENT_NAME);
issuerElement.setValue(issuer);
return issuerElement;
}
public static String generateID() {
return "_" + Long.toHexString