可使用 GenericSecurityTokenFactory API 将您创建的 SAML 令牌传递至 SAMLGenerateLoginModule
或 GenericIssuedTokenGenerateLoginModule 模块。还可使用这些 API 来获取 SAMLConsumeLoginModule
或 GenericIssuedTokenConsumeLoginModule 模块使用的 SAML 令牌。
开始之前
您必须具有 JAX-WS 服务客户机和提供者应用程序的功能集,您可以将新的 JAAS 登录模块类添加到其中。
关于此任务
此任务生成 SAML 1.1 不记名令牌,但该令牌很容易修改为使用 sender-vouches 和运行时环境所支持的任何 SAML 版本。
尽管可在 SAMLGenerateLoginModule
下堆栈登录模块以检查该类创建的 SAML 令牌,但您无法修改以此方式获取的 SAML 令牌,除非该令牌未包含数字签名。如果修改包含数字签名的 SAML 令牌,那么系统将针对您的更新来调整该 SAML 令牌的 XML;但是,因为该令牌是在更新之前签署的,所以接收方上的签名验证将失败。
此任务生成
SAML 1.1 不记名令牌,但您可使用运行时环境支持的任何 SAML 版本和类型。有关使用 GenericSecurityTokenFactory API 来创建和修改
SAML 令牌的更多信息,请参阅“开发 SAML 应用程序”。有关如何将该令牌放置到客户机的请求上下文上(而不使用 JAAS 登录模块)的更多信息,请参阅
com.ibm.wsspi.wssecurity.core.Constants 中的 com.ibm.wsspi.wssecurity.token.tokenHolder 和
com.ibm.wsspi.wssecurity.token.enableCaptureTokenContext 常量。
因为生成者示例不容易修改为使用 holder-of-key,所以在此任务的末尾提供了特定于
SAML holder-of-key 的 createSamlToken() 方法的示例。
过程
- 创建以下生成者 JAAS 登录模块并使其对应用程序代码可用。
package test.tokens;
import java.util.ArrayList;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory;
import com.ibm.websphere.wssecurity.wssapi.token.SAMLToken;
import com.ibm.websphere.wssecurity.wssapi.token.SAMLTokenFactory;
import com.ibm.wsspi.wssecurity.saml.config.CredentialConfig;
import com.ibm.wsspi.wssecurity.saml.config.ProviderConfig;
import com.ibm.wsspi.wssecurity.saml.config.RequesterConfig;
import com.ibm.wsspi.wssecurity.saml.data.SAMLAttribute;
import com.ibm.wsspi.wssecurity.core.config.KeyInformationConfig;
import com.ibm.wsspi.wssecurity.core.config.KeyStoreConfig;
import com.ibm.websphere.wssecurity.wssapi.WSSUtilFactory;
public class MySamlGenerator implements LoginModule {
private Map _sharedState;
private Map _options;
private CallbackHandler _handler;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this._handler = callbackHandler;
this._sharedState = sharedState;
this._options = options;
}
public boolean login() throws LoginException {
GenericSecurityTokenFactory factory = null;
try {
factory = GenericSecurityTokenFactory.getInstance();
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (factory == null) {
throw new LoginException("GenericSecurityTokenFactory.getInstance() returned null");
}
SAMLToken myToken = null;
try {
myToken = createSamlToken();
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (myToken == null) {
throw new LoginException("myToken is null");
}
//Put the token in a list on the shared state where it will be available to be used by
//stacked login modules
factory.putGeneratorTokenToSharedState(_sharedState, myToken);
return true;
}
private SAMLToken createSamlToken() throws Exception {
//SAML Bearer example
SAMLTokenFactory samlFactory = SAMLTokenFactory.getInstance(SAMLTokenFactory.WssSamlV11Token11);
RequesterConfig reqData = samlFactory.newBearerTokenGenerateConfig();
reqData.setAuthenticationMethod("Password"); //Authentication method for Assertion
ProviderConfig samlIssuerCfg = samlFactory.newDefaultProviderConfig("self-issue");
CredentialConfig cred = samlFactory.newCredentialConfig ();
cred.setRequesterNameID("Alice"); // SAML NameIdentifier
//Add some SAML attributes:
SAMLAttribute attribute = new SAMLAttribute
("email", new String[] {"joe@websphere"},null, "WebSphere", "email", "joe");
ArrayList<SAMLAttribute> al = new ArrayList<SAMLAttribute>();
al.add(attribute);
attribute = new SAMLAttribute("Membership",
new String[] {"Super users", "My team"}, null, null, null, null );
al.add(attribute);
cred.setSAMLAttributes(al);
SAMLToken samlToken = samlFactory.newSAMLToken(cred, reqData, samlIssuerCfg);
return samlToken;
}
public boolean logout() throws LoginException {
return false;
}
public boolean abort() throws LoginException {
return false;
}
public boolean commit() throws LoginException {
return true;
}
}
- 创建以下使用者 JAAS 登录模块并使其对应用程序代码可用。
package test.tokens;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.xml.namespace.QName;
import com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory;
import com.ibm.websphere.wssecurity.wssapi.token.SAMLToken;
import com.ibm.websphere.wssecurity.wssapi.token.SAMLTokenFactory;
import com.ibm.websphere.wssecurity.wssapi.token.SecurityToken;
public class MySamlConsumer implements LoginModule {
private Map _sharedState;
private Map _options;
private CallbackHandler _handler;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this._handler = callbackHandler;
this._sharedState = sharedState;
this._options = options;
}
public boolean login() throws LoginException {
GenericSecurityTokenFactory factory = null;
try {
factory = GenericSecurityTokenFactory.getInstance();
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (factory == null) {
throw new LoginException("GenericSecurityTokenFactory.getInstance() returned null");
}
//Get the token that was consumed by the GenericIssuedConsumeLoginModule
SecurityToken myToken = factory.getConsumerTokenFromSharedState(_sharedState, new QName(SAMLTokenFactory.WssSamlV11Token11));
if (myToken == null) {
throw new LoginException("myToken is null");
}
if (myToken instanceof SAMLToken) {
//Examine the SAML token with SAML APIs
SAMLToken samlToken = (SAMLToken)myToken;
String id = samlToken.getSamlID();
String subjectDns = samlToken.getSubjectDNS();
//...
} else {
throw new LoginException("Did not receive a SAML token");
}
return true;
}
public boolean logout() throws LoginException {
return false;
}
public boolean abort() throws LoginException {
return false;
}
public boolean commit() throws LoginException {
return true;
}
}
- 创建 JAAS 登录配置。
- 在管理控制台中,转至。
- 在“认证”下,转至 。
- 创建 SAML 令牌生成者。
- 单击新建,然后在“别名”下输入 test.generate.saml。
- 在“JAAS 登录模块”下面,单击新建,然后在“模块类名”下输入 test.tokens.MySamlGenerator。选择使用登录模块代理,然后单击确定。
- 单击新建,在“模块类名”下输入 com.ibm.ws.wssecurity.wssapi.token.impl.SAMLGenerateLoginModule。单击确定。
- 创建 SAML 令牌使用者。
- 在面包屑中,单击 JAAS - 系统登录以返回至 JAAS 系统登录页面。
- 单击新建,然后在“别名”下输入 test.consume.saml。
- 在“JAAS 登录模块”下面,单击新建,然后在“模块类名”下输入 com.ibm.ws.wssecurity.wssapi.token.impl.SAMLConsumeLoginModule。选择使用登录模块代理,然后单击确定。
- 单击保存。
- 配置 SAML 令牌生成者以使用新的 JAAS 登录配置。
- 在管理控制台中,打开要更改的绑定配置。
- 选择 。
- 在“认证令牌”下,选择要更改的 SAML 出站令牌。
- 在“JAAS 登录”下,选择 test.generate.saml。
- 配置 SAML 令牌使用者以使用新的 JAAS 配置。
- 在管理控制台中,打开要更改的绑定配置。
- 选择 。
- 在“认证令牌”下,选择要更改的 SAML 入站令牌。
- 在“JAAS 登录”下,选择 test.consume.saml。
- 单击保存。
- 重新启动应用程序服务器以应用 JAAS 配置更改。
- 测试服务。
示例
以下示例说明了用于创建非对称 SAML 2.0 holder-of-key 令牌的
createSamlToken() 方法。
private SAMLToken createSamlToken() throws Exception {
SAMLTokenFactory samlFactory = SAMLTokenFactory.getInstance(SAMLTokenFactory.WssSamlV20Token11);
RequesterConfig reqData = samlFactory.newAsymmetricHolderOfKeyTokenGenerateConfig();
reqData.setAuthenticationMethod("Password"); //Authentication method for Assertion
ProviderConfig samlIssuerCfg = samlFactory.newDefaultProviderConfig("self-issue");
CredentialConfig cred = samlFactory.newCredentialConfig ();
cred.setRequesterNameID("Alice"); // SAML NameIdentifier
// (Optional) If you want to use keystore and key properties other than what
// is set in the SAMLIssuerConfig.properties file, reset the keystore,
// trust store and alias information in the ProviderConfig object.
KeyInformationConfig kic = samlFactory.newKeyInformationConfig("private_key","keypass","CN=Private");
KeyStoreConfig ksc = samlFactory.newKeyStoreConfig("jks","/keystores/myKeystore.ks","storepass");
samlIssuerCfg.setKeyStoreConfig(ksc); //keystore that holds the private key
samlIssuerCfg.setTrustStoreConfig(ksc); //keystore that holds the public key
// Set the alias for the public certificate that must exist in the trust store.
// This alias must not require a password.
reqData.setKeyAliasForRequester("public_cert");
SecurityToken samlToken = samlFactory.newSAMLToken(cred, reqData, samlIssuerCfg);
// Get the private key from the key store
WSSUtilFactory wssufactory = WSSUtilFactory.getInstance();
java.security.KeyStore ks = wssufactory.getKeyStore("jks","/keystores/myKeystore.ks",
"storepass".toCharArray());
java.security.Key privateKey = ks.getKey("private_key", "keypass".toCharArray());
// Add the private key to the token so that the token can be used to sign
// elements in a SOAP message. ((com.ibm.ws.wssecurity.wssapi.token.impl.SecurityTokenImpl)samlToken).
setKey(SecurityToken.SIGNING_KEY, privateKey); ((com.ibm.ws.wssecurity.wssapi.token.impl.SecurityTokenImpl)samlToken).
setKey(SecurityToken.DECRYPTING_KEY, privateKey);
return samlToken;
}