2023年6月21日发(作者:)

Java实现LDAP认证(上)Baidu脑残,把原来的空间改得不伦不类。所以把⼀些技术的东西挪到这⾥。我找到两种⽅法,⼤同⼩异,第⼀种是通过Spring,适合已经采⽤Spring的项⽬。⼀般来说⽤户名和密码都是保存在数据库中。现在有这个需求,⽤户名和密码是保存在M$的AD中。那么如何进⾏认证。我找到的⽅法有两种,⼀种是jcifs,另⼀种是Spring Security。这⾥⽤了Spring Security,参考了CAS SSO中LDAP验证的实现。⾸先建⼀个测试环境。在Server 2003中安装Domain,这个没啥说的,⽹上⽂章⼀⼤把。安装好以后,创建测试⽤的⽤户组(Team1)和两个⽤户(test,test1)。test⽤户属于组Team1,test1则不属于。如下图。Domain的名称是。创建好以后,在Eclipse中创建Java项⽬,加⼊以下的需要的Jar包:开始写代码。初始化部分,⼀些LDAP的参数设置。static { LdapContextSource cs = new LdapContextSource(); heEnvironmentProperties(false); ("ldap://192.168.1.200:389"); e("CN=Users,DC=dc,DC=testdc,DC=com"); henticationSource(new AuthenticationSource() { public String getCredentials() { return ""; } public String getPrincipal() { return "CN=Administrator,CN=Users,DC=dc,DC=testdc,DC=com"; } }); template = new LdapTemplate(cs);}说明。1. 其中 ldap://192.168.1.200:389 是上⾯的Server 2003的地址。389是AD默认的端⼝,⼀般不⽤改;2. setBase("CN=Users,DC=dc,DC=testdc,DC=com") 是查找的基础。按我⾃⼰的理解,这相当于⼀个查询,会查出若⼲结果。实际进⾏认证的⽤户必须在这些结果中。这个字符串怎么得到的呢,前⾯的CN=Users表明是进⾏⽤户查询;后⾯的DC=dc,DC=testdc,DC=com是和上⾯图中左上⾓红圈中的匹配。后⾯会更详细说明怎么通过Softerra LDAP Browser来帮忙取得这些值。3. getPrincipal() 返回的是⼀个⽤户,这个⽤户有权限进⾏(2)中的查询。这⾥为了省事⽤的是Administrator,当然肯定有这个权限了。4. getCredentials() 这⾥⾃然就是(3)中⽤户的密码了。进⾏下⼀步前⽤Softerra LDAP Browser来确认⼀下上⾯输⼊的东西。去下载安装这个东西(Free的就够⽤),安装有,右键,选择New:下⼀步,随便起个名下⼀步,输⼊AD服务器的IP,端⼝,点击Fetch Base DN:稍等,会出来⼀个列表,显⽰在Base DN下拉框中:这⾥选择的是DC=dc,DC=testDC,DC=com,下⼀步这⾥的⽤户名是CN=Administrator,CN=Users, DC=dc,DC=testdc,DC=com和Java代码中⼀致。下⾯的密码就填他的密码。下⼀步。如果有问题,会弹出错误。应该是没有错误的。最后⼀步,默认的选择即可,点完成。完成后,在界⾯中显⽰了各种LDAP的对象。这⾥有⽤的是Users下⾯的,因为我们要对它进⾏认证:可以看到,Administrator和刚才创建的⽤户test,test1都在这⾥:然后继续写代码。下⾯这段代码的含义,是调⽤LDAP的查询,查询给出的⽤户名/密码是否满⾜条件。这⾥说的条件,是⼀个Filter字符串:final String filter = "sAMAccountName=" + username;( new SearchExecutor() { public NamingEnumeration executeSearch(final DirContext context) throws NamingException { return (base, filter, searchControls); } }, new NameClassPairCallbackHandler(){ public void handleNameClassPair(final NameClassPair nameClassPair) { (eInNamespace()); } });说明。最重要的是上⾯的Filter。这⾥⽤的是sAMAccountName=⽤户名。sAMAccountName是什么东西呢,我理解是系统中对这个⽤户的唯⼀标识。在LDAPBrowser中点击⼀个⽤户,可以看到它的sAMAccountName:

换句话说,上⾯的查询,可以这样理解。⾸先,我们通过Search Base的设置(setBase("CN=Users,DC=dc,DC=testdc,DC=com"))设置了查询的范围。然后上⾯的filter = "sAMAccountName=" + username; 设置了查询的条件。例如sAMAccountName=test的意思就是在"CN=Users,DC=dc,DC=testdc,DC=com"所表⽰的这么多对象中,查找sAMAccountName=test的对象。换句话说,就是查找登录的⽤户,在AD服务器中是否存在? 然后程序中继续判断: if (y()) { n("Search for " + filter + " returned 0 results."); return false; } if (() > 1) { n("Search for " + filter + " returned multiple results, which is not allowed."); return false; }不存在,或者多余⼀个存在,都是错误。返回;

for (final String dn : cns) { DirContext test = null; String finalDn = dn; try { n("Performing LDAP bind with credential: " + dn); test = textSource().getContext( finalDn, password); if (test != null) { return true; } } catch (final Exception e) { tackTrace(); } finally { if (test != null) { (); } } }如果存在,并且只有⼀个,则获取这个⽤户的信息(测试⽤户名和密码)。如果成功,返回true。若有异常出现(⽐如⽤户存在但是密码错,或者被禁⽤了,返回false。

到这⾥基本部分的验证结束了。功能上,可以验证整个AD中存在并且合法的⽤户。那么,⽤户组的需求怎么实现?⽐如只有⽤户组Team1中的⽤户可以登录?答案是上⾯的Filter。上⾯我们⽤了⼀个最简单的Filter,filter = "sAMAccountName=" + username。⾃然⽽然,可以就可以想到更改这个Filter,让它返回"属于⽤户组Team1并且⽤户名是xxxx"的⽤户。写法很简单,只需要改成final String filter = "(&(CN=" + username + ")(memberof=CN=Team1,CN=Users,DC=dc,DC=testdc,DC=com))";即可。上⾯的filter,有两个条件:1. CN=⽤户名2. memberof=CN=Team1,CN=Users,DC=dc,DC=testdc,DC=com,也就是⽤户是(CN=Team1,CN=Users,DC=dc,DC=testdc,DC=com)的⼀个成员。最前⾯的&表⽰这两个条件是相与的关系。memberof的值是怎么来的呢,可以在LDAP Browser中查看⼀个Team1组中的⽤户,其memberof属性:在测试Filter的时候,可以在LDAP Browser中进⾏测试(右键New Pfofile -- 属性-- Entry页,点击Filter右侧的漏⽃图标,可以在FilterBuilder中测试)最后有个关于⽤户被禁⽤的问题。如果⽤户被禁⽤了,上⾯的代码会抛出⼀个异常,需要捕捉异常。如果不想捕捉异常,可以使⽤(!(userAccountControl:1.2.840.113556.1.4.803:=2))来过滤出所有没有被禁⽤的⽤户。

2023年6月21日发(作者:)

Java实现LDAP认证(上)Baidu脑残,把原来的空间改得不伦不类。所以把⼀些技术的东西挪到这⾥。我找到两种⽅法,⼤同⼩异,第⼀种是通过Spring,适合已经采⽤Spring的项⽬。⼀般来说⽤户名和密码都是保存在数据库中。现在有这个需求,⽤户名和密码是保存在M$的AD中。那么如何进⾏认证。我找到的⽅法有两种,⼀种是jcifs,另⼀种是Spring Security。这⾥⽤了Spring Security,参考了CAS SSO中LDAP验证的实现。⾸先建⼀个测试环境。在Server 2003中安装Domain,这个没啥说的,⽹上⽂章⼀⼤把。安装好以后,创建测试⽤的⽤户组(Team1)和两个⽤户(test,test1)。test⽤户属于组Team1,test1则不属于。如下图。Domain的名称是。创建好以后,在Eclipse中创建Java项⽬,加⼊以下的需要的Jar包:开始写代码。初始化部分,⼀些LDAP的参数设置。static { LdapContextSource cs = new LdapContextSource(); heEnvironmentProperties(false); ("ldap://192.168.1.200:389"); e("CN=Users,DC=dc,DC=testdc,DC=com"); henticationSource(new AuthenticationSource() { public String getCredentials() { return ""; } public String getPrincipal() { return "CN=Administrator,CN=Users,DC=dc,DC=testdc,DC=com"; } }); template = new LdapTemplate(cs);}说明。1. 其中 ldap://192.168.1.200:389 是上⾯的Server 2003的地址。389是AD默认的端⼝,⼀般不⽤改;2. setBase("CN=Users,DC=dc,DC=testdc,DC=com") 是查找的基础。按我⾃⼰的理解,这相当于⼀个查询,会查出若⼲结果。实际进⾏认证的⽤户必须在这些结果中。这个字符串怎么得到的呢,前⾯的CN=Users表明是进⾏⽤户查询;后⾯的DC=dc,DC=testdc,DC=com是和上⾯图中左上⾓红圈中的匹配。后⾯会更详细说明怎么通过Softerra LDAP Browser来帮忙取得这些值。3. getPrincipal() 返回的是⼀个⽤户,这个⽤户有权限进⾏(2)中的查询。这⾥为了省事⽤的是Administrator,当然肯定有这个权限了。4. getCredentials() 这⾥⾃然就是(3)中⽤户的密码了。进⾏下⼀步前⽤Softerra LDAP Browser来确认⼀下上⾯输⼊的东西。去下载安装这个东西(Free的就够⽤),安装有,右键,选择New:下⼀步,随便起个名下⼀步,输⼊AD服务器的IP,端⼝,点击Fetch Base DN:稍等,会出来⼀个列表,显⽰在Base DN下拉框中:这⾥选择的是DC=dc,DC=testDC,DC=com,下⼀步这⾥的⽤户名是CN=Administrator,CN=Users, DC=dc,DC=testdc,DC=com和Java代码中⼀致。下⾯的密码就填他的密码。下⼀步。如果有问题,会弹出错误。应该是没有错误的。最后⼀步,默认的选择即可,点完成。完成后,在界⾯中显⽰了各种LDAP的对象。这⾥有⽤的是Users下⾯的,因为我们要对它进⾏认证:可以看到,Administrator和刚才创建的⽤户test,test1都在这⾥:然后继续写代码。下⾯这段代码的含义,是调⽤LDAP的查询,查询给出的⽤户名/密码是否满⾜条件。这⾥说的条件,是⼀个Filter字符串:final String filter = "sAMAccountName=" + username;( new SearchExecutor() { public NamingEnumeration executeSearch(final DirContext context) throws NamingException { return (base, filter, searchControls); } }, new NameClassPairCallbackHandler(){ public void handleNameClassPair(final NameClassPair nameClassPair) { (eInNamespace()); } });说明。最重要的是上⾯的Filter。这⾥⽤的是sAMAccountName=⽤户名。sAMAccountName是什么东西呢,我理解是系统中对这个⽤户的唯⼀标识。在LDAPBrowser中点击⼀个⽤户,可以看到它的sAMAccountName:

换句话说,上⾯的查询,可以这样理解。⾸先,我们通过Search Base的设置(setBase("CN=Users,DC=dc,DC=testdc,DC=com"))设置了查询的范围。然后上⾯的filter = "sAMAccountName=" + username; 设置了查询的条件。例如sAMAccountName=test的意思就是在"CN=Users,DC=dc,DC=testdc,DC=com"所表⽰的这么多对象中,查找sAMAccountName=test的对象。换句话说,就是查找登录的⽤户,在AD服务器中是否存在? 然后程序中继续判断: if (y()) { n("Search for " + filter + " returned 0 results."); return false; } if (() > 1) { n("Search for " + filter + " returned multiple results, which is not allowed."); return false; }不存在,或者多余⼀个存在,都是错误。返回;

for (final String dn : cns) { DirContext test = null; String finalDn = dn; try { n("Performing LDAP bind with credential: " + dn); test = textSource().getContext( finalDn, password); if (test != null) { return true; } } catch (final Exception e) { tackTrace(); } finally { if (test != null) { (); } } }如果存在,并且只有⼀个,则获取这个⽤户的信息(测试⽤户名和密码)。如果成功,返回true。若有异常出现(⽐如⽤户存在但是密码错,或者被禁⽤了,返回false。

到这⾥基本部分的验证结束了。功能上,可以验证整个AD中存在并且合法的⽤户。那么,⽤户组的需求怎么实现?⽐如只有⽤户组Team1中的⽤户可以登录?答案是上⾯的Filter。上⾯我们⽤了⼀个最简单的Filter,filter = "sAMAccountName=" + username。⾃然⽽然,可以就可以想到更改这个Filter,让它返回"属于⽤户组Team1并且⽤户名是xxxx"的⽤户。写法很简单,只需要改成final String filter = "(&(CN=" + username + ")(memberof=CN=Team1,CN=Users,DC=dc,DC=testdc,DC=com))";即可。上⾯的filter,有两个条件:1. CN=⽤户名2. memberof=CN=Team1,CN=Users,DC=dc,DC=testdc,DC=com,也就是⽤户是(CN=Team1,CN=Users,DC=dc,DC=testdc,DC=com)的⼀个成员。最前⾯的&表⽰这两个条件是相与的关系。memberof的值是怎么来的呢,可以在LDAP Browser中查看⼀个Team1组中的⽤户,其memberof属性:在测试Filter的时候,可以在LDAP Browser中进⾏测试(右键New Pfofile -- 属性-- Entry页,点击Filter右侧的漏⽃图标,可以在FilterBuilder中测试)最后有个关于⽤户被禁⽤的问题。如果⽤户被禁⽤了,上⾯的代码会抛出⼀个异常,需要捕捉异常。如果不想捕捉异常,可以使⽤(!(userAccountControl:1.2.840.113556.1.4.803:=2))来过滤出所有没有被禁⽤的⽤户。