[英]Active Directory Authentication using services account
I'm working on Web Services
which authenticates users against Active Directory
. 我正在研究
Web Services
,该Web Services
可根据Active Directory
对用户进行身份验证。 My current solution is working, however, I'm trying to take a different approach. 我当前的解决方案正在工作,但是,我正在尝试采用另一种方法。
I have an Active Directory
(production) which is sitting behind the firewall. 我的
Active Directory
(生产版本)位于防火墙后面。 I also installed an Active Directory
in DMZ
. 我还在
DMZ
安装了Active Directory
。 There's a one way relationship between them. 它们之间存在一种单向关系。 DMZ trusts production and production doesn't care about
DMZ
. DMZ相信生产和生产无关
DMZ
。
What I'm trying to accomplish is Authenticate everyone through DMZ Active Directory
. 我要完成的工作是通过
DMZ Active Directory
每个人进行身份验证。 Currently, based on the username I know which AD
server to authenticate against. 目前,基于用户名,我知道要针对哪个
AD
服务器进行身份验证。
For example, my production Active Directory (let say domain domain.local ) and my DMZ Active directory (let say domain domain.public ). 例如,我的生产Active Directory(比如说domain domain.local )和DMZ Active目录(比如说domain domain.public )。 Before I do authentication against any AD server, I check if the username provided exists in one of the server.
在对任何AD服务器进行身份验证之前,我会检查提供的用户名是否存在于其中一台服务器中。 Then, I check if the user is active and only then I do authentication.
然后,我检查用户是否处于活动状态,然后才进行身份验证。 (I'm having issues in the first function. It never reaches to the second or third functions).
(我在第一个功能中遇到了问题。它从来没有达到第二个或第三个功能)。
UPDATE: ADDED EVERYTHING: 更新:添加了所有内容:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.DirectoryServices;
using System.Security.Principal;
using System.DirectoryServices.AccountManagement;
namespace ActiveDirectory
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
public class Service1 : IService1
{
#region Does User Exist in AD
public string local = string.Empty;
public string ldappath = string.Empty;
public string userNameToUse = string.Empty;
public string domain = string.Empty;
public bool DoesUserExist(string userName)
{
string _userName = userName;
bool exist = true;
using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
{
using (var foundUser = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userNameToUse))
{
if (foundUser == null)
{
exist = false;
}
else
{
return exist;
}
}
}
return exist;
}
#endregion
#region Check if User Active
public bool isActive (string userName)
{
string _userNameToBeSearched = userNameToUse;
string _username = string.Empty;
string _pwd = string.Empty;
if (local == "YES")
{
_username = "xx";
_pwd = "xx";
ldappath = "LDAP://xxx/DC=xx, DC=local";
}
else
{
_username = "xx";
_pwd = "xx";
ldappath = "LDAP://xxx/DC=xx, DC=public";
}
bool isActive = true;
try
{
DirectoryEntry entry = new DirectoryEntry(ldappath, _username, _pwd);
DirectorySearcher search = new DirectorySearcher(entry);
entry.AuthenticationType = AuthenticationTypes.Secure;
search.SearchRoot = entry;
search.Filter = "(SAMAccountName=" + _userNameToBeSearched + ")";
SearchResult results = search.FindOne();
if (results.ToString() != "")
{
int flags = Convert.ToInt32(results.Properties["userAccountControl"][0].ToString());
//CHECK IF THE ACCOUNT IS DISABLED
if (flags == 66050)
{
isActive = false;
}
}
}
catch (DirectoryServicesCOMException ex)
{
ex.ToString();
}
return isActive;
}
#endregion
#region Is user authenticated
public string isAuthenticated (string userName, string pwd)
{
string _userName, _pwd, message;
_userName = userName;
_pwd = pwd;
char[] splitchar = { '@' };
string[] strSplit = _userName.Split(splitchar);
string z = strSplit[0];
if (strSplit.Length == 2)
{
domain = "x.public";
userNameToUse = z.ToString();
local = "NO";
}
else
{
domain = "x.local";
userNameToUse = z.ToString();
local = "YES";
}
if (DoesUserExist (userNameToUse) == true)
{
if (isActive(userNameToUse) == true)
{
try
{
DirectoryEntry entry = new DirectoryEntry(ldappath, userNameToUse, _pwd);
object nativeObject = entry.NativeObject;
var GUIDID = "";
using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
{
using (var user = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userNameToUse))
{
if (user != null)
{
GUIDID = user.Guid.ToString();
}
}
message = "Successfully authenticated:" + GUIDID;
}
}
catch (DirectoryServicesCOMException)
{
message = "Invalid password.";
}
}
else
{
message = "Account is disabled";
}
}
else
{
message = "There's an issue with your account.";
}
return message;
}
#endregion
}
}
If username exists in DMZ
AD it will return true else it will return false. 如果
DMZ
AD中存在用户名,它将返回true,否则将返回false。 However, there're going to be users who will only exists in production AD, but will not have any entry in DMZ. 但是,将会有一些用户仅存在于生产AD中,而在DMZ中没有任何条目。 Since, I built ONE WAY trust I should be able to do this:
由于建立了单向信任,因此我应该能够做到这一点:
username@domain.local
for production and username@domain.public
However, even though I specify fully username if entry doesn't exists in DMZ AD it will return null although it exists in production AD. username@domain.local
生产和username@domain.public
然而,即使我完全指定用户名称,条目没有在DMZ AD存在,它将返回null虽然它在生产AD存在。
Any suggestion, on how I can authenticate everybody through DMZ AD
using webservices account which has full permission to production AD? 关于如何使用拥有生产AD完全权限的webservices帐户通过
DMZ AD
验证每个人的任何建议?
Note if needed I can provide the rest of the code...* 请注意,如果需要,我可以提供其余代码... *
Thanks 谢谢
What is the value of domain in the PrincipalContext? PrincipalContext中domain的值是什么? If you aren't already, you'll need to use the proper domain for the user you're looking up to user your current code.
如果尚未注册,则需要为要查找的用户使用正确的域以使用当前代码。 PrincipalContext does not support searches across domains in your forest.
PrincipalContext不支持在林中跨域搜索。
Here is an example of using a DirectorySearcher to do it. 这是使用DirectorySearcher进行操作的示例 。
The code has several issues, I only talk about major ones. 该代码有几个问题,我只谈论主要的问题。
In method DoesUserExist
you use service account (no credential is passed to PrincipalContext
) to query both domains. 在方法
DoesUserExist
您使用服务帐户(没有凭据传递给PrincipalContext
)来查询两个域。 But in method isActive
you have different credentials for different domains. 但是在方法
isActive
您对不同的域具有不同的凭据。 So we have 3 credentials for 2 domains... 因此,我们拥有2个域的3个凭据...
At least use a consistent way for both methods. 至少对于两种方法都使用一致的方式。
For 1-way trust(eg A trusts B), accounts in B should be able to access both domain A and B. 对于单向信任(例如,A信任B),B中的帐户应能够访问域A和B。
May be you can simply use an account in trusted domain as service account. 可能您可以简单地将受信任域中的帐户用作服务帐户。 And then use service account (null to username and password) to perform all AD access.
然后使用服务帐户(用户名和密码为空)执行所有AD访问。 So you don't even need to put password in your code.
因此,您甚至无需在代码中输入密码。
In isActive
, where does the "66050" come from? 在
isActive
,“ 66050”来自哪里?
66060 is 10202 in hex, which means (1)user, (2)disabled and (3)don't expire password. 66060是十六进制的10202,表示(1)用户,(2)禁用和(3)不过期密码。
To check if an account is enabled, check only the ACCOUNTDISABLE bit (0x0002). 要检查是否启用了帐户,请仅检查ACCOUNTDISABLE位(0x0002)。 It should be 0 for enabled.
启用时应为0。
You already get the UserPrincipal in DoesUserExist
. 您已经在DoesUserExist中获得了
DoesUserExist
。 Can simply check enabled by looking at UserPrincipal.Enabled
. 只需查看
UserPrincipal.Enabled
可以检查启用UserPrincipal.Enabled
。
I need to do code review in my daily work. 我需要在日常工作中进行代码审查。 Now I'm doing it also on Stack Overflow... :)
现在,我也在堆栈溢出... :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.