我们需要通过Active Directory进行身份验证。我们希望域内的Windows用户可以在不输入用户名和密码(单点登录)的情况下进行身份验证,但外部用户(或不使用Internet Explorer的用户)也可以插入用户名和密码并登录。
我们还需要在用户所属的群组中发挥作用,因为这将改变用户在我们网站上可以看到的内容。
我们使用Java和Jetty作为我们的应用程序服务器,并在Windows中开发,但我们的服务器将是Linux。
谢谢!
答案 0 :(得分:3)
根据@Akber的建议,您可以使用IP范围。您需要一个使用远程地址或X-Forwarded-For标头的公共端点,如果它在Intranet范围内,您可以测试IP,这是10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/12
。
如果IP在Intranet范围内,您可以重定向到Apache代理(稍后会详细介绍)。如果IP超出范围,您将重定向到具有漂亮外观的端点。
ProxyPass / http://localhost:9005/ #your backend
ProxyPassReverse / http://localhost:9005/ #your backend
ProxyPreserveHost On
## Rewrite rules
RewriteEngine On
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1]
## Request header rules
## as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#requestheader
RequestHeader set X-Forwarded-User %{RU}e
<Location />
AuthName "Kerberos Login"
AuthType Kerberos
Krb5Keytab /path/to your keytab/HTTP.keytab
KrbAuthRealm DOMAIN.LOC
KrbMethodNegotiate on
KrbSaveCredentials off
KrbVerifyKDC off
KrbServiceName HTTP/YOURAPP.AD2008R2.LOC
Require valid-user
</Location>
然后你的后端会收到X-Forwarded-User
,你可以使用LDAP来递归地获取完整的个人资料和群组。
请注意,有/path/to your keytab/HTTP.keytab
,此文件应该从绑定到域的Windows计算机生成。
这是由您的应用程序直接处理的,一旦您收到用户名和密码,您将不得不尝试使用LDAP协议“绑定”到AD,然后您必须递归地获取用户配置文件和组。
这可能看起来很简单,但它实际上涉及到很多工作,不仅是代码,还包括维护。还有其他两种解决方案可能适用于您的情况,但需要部署另一种产品;
免责声明:我为Auth0工作。
答案 1 :(得分:0)
对于群组个性化,Web应用程序需要知道用户来自哪里(Intranet或Internet),并且可以通过标头,Apache中的IP范围配置等来完成 - 这取决于您的详细配置和代码。
对于Intranet SSO,您似乎需要SPNEGO,链接如下。对于Intranet SSO,您公司的管理策略和Internet Explorer设置将发挥重要作用,因此我将从页面复制清单:
答案 2 :(得分:0)
如果可以对外部用户进行身份验证,则可能需要在应用程序数据库中存储用户名(至少)。
您可以使用JNDI通过AD实现LDAP获取用户组信息(这也需要为您的web.xml提供一些标准条目)。
您可以通过JCIFS对Intranet上的内部AD用户使用NTLM身份验证。
基于这个假设:
使用在步骤1中创建的此NTLMFilter实现,通过JCIFS使用NTLM身份验证为Intranet中的用户进行单点登录:像这样
public static Boolean authenticateUsingJCIF(String username,String password){
UniAddress uniaddress = null;
String _methodName = "authenticateUsingJCIF";
try {
uniaddress = UniAddress.getByName(PropertyUtils
.getProperty(AUTHENTICATION_SERVER_URL));
NtlmPasswordAuthentication ntlmpasswordauthentication = new NtlmPasswordAuthentication(PropertyUtils.getProperty(AUTHENTICATION_SERVER_DOMAIN),username, password); //You can have your own method to read properties, I've just delegated to a generic utils
SmbSession.logon(uniaddress, ntlmpasswordauthentication);
logger.info("INTERNAL User authenticated successfully against AD");
} catch (UnknownHostException e) {
logger.error(e.toString(), e);
return false;
} catch (SmbException e) {
logger.error(e.toString(), e);
return false;
} catch (Exception e) {
logger.error(e.toString(), e);
return false;
}
return true;
}
对于外部用户,有一个正常的用户名/密码验证:像这样
private String authenticateExternalUser(String id, YourUserNamePasswordAuthenticationSource authSource){ String statusMessage = null;
logger
.info("Performing credential verification for external user against INTERNAL DB");
//Create or utilize your own handlers to validate plain/encoded credentials against internal db
CredentialVerificationResult returnCode = _handler
.verifyInternalCredential(id, authSource.getPassword());
logger.info("Logging value of return code" + returnCode.getMessage());
if (returnCode == CredentialVerificationResult.SUCCESS) {
statusMessage = CREDENTIAL_VERIFICATION_SUCCESS;
} else if (returnCode == CredentialVerificationResult.BAD_USER_ID) {
statusMessage = BAD_USER_ID;
} else if (returnCode == CredentialVerificationResult.WAIT_TO_RETRY) {
statusMessage = WAIT_TO_RETRY;
} else if (returnCode == CredentialVerificationResult.CREDENTIAL_LOCKED) {
statusMessage = CREDENTIAL_LOCKED;
} else if (returnCode == CredentialVerificationResult.PASSWORD_MISMATCH) {
statusMessage = PASSWORD_MISMATCH;
}
return statusMessage;
}
您可能希望使用一种通用方法来验证内部/外部用户(以调用上述两种方法)并相应地返回结果。
希望它有所帮助,祝你好运!