一对一结束受 WS-security 保护的 SOAP 服务 (Java)。另一端是充当 SOAP 服务器客户端的 HTTP 服务器 (Java)。
我想为具有以下委托链的用户实现单点登录(使用 Negotiate/Kerberos):浏览器 >> HTTP 服务器 >> SOAP 服务器
浏览器 >> HTTP 服务器没有问题并且可以正常工作(通过 HTTP 协商)。
在 HTTP 服务器上模拟用户,第二跳 HTTP 服务器 >> SOAP 服务器更具挑战性,因为我想使用 JNA,并且我还希望使用单个令牌执行身份验证(避免握手)。我读到它应该是可能的,因为我只需要一种方式身份验证。
在信封中将令牌从 HTTP 服务器传递到 SOAP 服务器也可以正常工作,在 SOAP 服务器端有一个拦截器。我唯一的问题是在调用 AcceptSecurityContext 时生成一个不会导致SEC_I_CONTINUE_NEEDED状态的有趣令牌。
获取 SOAP 服务令牌的客户端代码(JNA 由 Waffle 项目包装):
import sun.misc.BASE64Encoder;
import waffle.windows.auth.IWindowsSecurityContext;
import waffle.windows.auth.impl.WindowsSecurityContextImpl;
public class WindowsAuthenticator {
public static final String securityPackage = "Negotiate";
public static String getKrbToken(String targetSPN) {
if (null == targetSPN || targetSPN.trim().isEmpty()) {
return null;
}
IWindowsSecurityContext ctx = WindowsSecurityContextImpl.getCurrent(securityPackage, targetSPN);
byte[] token = ctx.getToken();
return new BASE64Encoder().encode(token);
}
private WindowsAuthenticator() {
super();
}
}
用于验证 Kerberos 令牌的 SOAP 服务器代码
import org.apache.log4j.Logger;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.handler.RequestData;
import org.apache.ws.security.message.token.BinarySecurity;
import org.apache.ws.security.validate.Credential;
import org.apache.ws.security.validate.Validator;
import waffle.windows.auth.IWindowsAuthProvider;
import waffle.windows.auth.IWindowsIdentity;
import waffle.windows.auth.IWindowsSecurityContext;
import waffle.windows.auth.impl.WindowsAuthProviderImpl;
public class KerberosTokenValidator implements Validator {
private static final Logger logger = Logger.getLogger(KerberosTokenValidator.class);
private final IWindowsAuthProvider windowsAuthProvider;
public KerberosTokenValidator() {
this.windowsAuthProvider = new WindowsAuthProviderImpl();
}
@Override
public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
BinarySecurity token = credential.getBinarySecurityToken();
byte[] binaryToken = token.getToken();
String id = token.getID();
String type = token.getValueType();
final IWindowsSecurityContext securityContext;
try {
securityContext = this.windowsAuthProvider.acceptSecurityToken(id, binaryToken, "Negotiate");
this.windowsAuthProvider.acceptSecurityToken(id, binaryToken, "Negotiate");
final IWindowsIdentity windowsIdentity = securityContext.getIdentity();
securityContext.dispose();
} catch (Exception exception) {
logger.warn("error logging in user: " + exception.getMessage());
throw new WSSecurityException("Could not auth....");
}
return null;
}
}
问题是acceptSecurityToken返回一个SEC_I_CONTINUE_NEEDED并且我最终进行了握手(就像第一跳通过 HTTP 完成的那样)。在这里,我真的希望(如果可能的话)服务器对单个令牌感到满意(SOAP API 的当前设计是完全无状态的,希望以这种方式保持)。