ldap 认证 java_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的名称是dc.testdc.com。

0818b9ca8b590ca3270a3433284dd417.png

创建好以后,在Eclipse中创建Java项目,加入以下的需要的Jar包:

0818b9ca8b590ca3270a3433284dd417.png

开始写代码。初始化部分,一些LDAP的参数设置。

static {

LdapContextSource cs = new LdapContextSource();

cs.setCacheEnvironmentProperties(false);

cs.setUrl("ldap://192.168.1.200:389");

cs.setBase("CN=Users,DC=dc,DC=testdc,DC=com");

cs.setAuthenticationSource(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是和上面图中左上角红圈中的dc.testdc.com匹配。后面会更详细说明怎么通过Softerra LDAP Browser来帮忙取得这些值。

3. getPrincipal() 返回的是一个用户,这个用户有权限进行(2)中的查询。这里为了省事用的是Administrator,当然肯定有这个权限了。

4. getCredentials() 这里自然就是(3)中用户的密码了。

进行下一步前用Softerra LDAP Browser来确认一下上面输入的东西。

去下载安装这个东西(Free的就够用),安装有,右键,选择New:

0818b9ca8b590ca3270a3433284dd417.png

下一步,随便起个名

0818b9ca8b590ca3270a3433284dd417.png

下一步,输入AD服务器的IP,端口,点击Fetch Base DN:

0818b9ca8b590ca3270a3433284dd417.png

稍等,会出来一个列表,显示在Base DN下拉框中:

0818b9ca8b590ca3270a3433284dd417.png

这里选择的是DC=dc,DC=testDC,DC=com,下一步

0818b9ca8b590ca3270a3433284dd417.png

这里的用户名是CN=Administrator,CN=Users, DC=dc,DC=testdc,DC=com和Java代码中一致。下面的密码就填他的密码。下一步。如果有问题,会弹出错误。应该是没有错误的。

最后一步,默认的选择即可,点完成。

0818b9ca8b590ca3270a3433284dd417.png

完成后,在界面中显示了各种LDAP的对象。这里有用的是Users下面的,因为我们要对它进行认证:

0818b9ca8b590ca3270a3433284dd417.png

可以看到,Administrator和刚才创建的用户test,test1都在这里:

0818b9ca8b590ca3270a3433284dd417.png

然后继续写代码。

下面这段代码的含义,是调用LDAP的查询,查询给出的用户名/密码是否满足条件。这里说的条件,是一个Filter字符串:

final String filter = "sAMAccountName=" + username;

template.search(

new SearchExecutor() {

public NamingEnumeration executeSearch(final DirContext context) throws NamingException {

return context.search(base, filter, searchControls);

}

},

new NameClassPairCallbackHandler(){

public void handleNameClassPair(final NameClassPair nameClassPair) {

cns.add(nameClassPair.getNameInNamespace());

}

});

说明。最重要的是上面的Filter。这里用的是sAMAccountName=用户名。sAMAccountName是什么东西呢,我理解是系统中对这个用户的唯一标识。在LDAP Browser中点击一个用户,可以看到它的sAMAccountName:

0818b9ca8b590ca3270a3433284dd417.png

换句话说,上面的查询,可以这样理解。首先,我们通过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 (cns.isEmpty()) {

System.out.println("Search for " + filter + " returned 0 results.");

return false;

}

if (cns.size() > 1) {

System.out.println("Search for " + filter + " returned multiple results, which is not allowed.");

return false;

}

不存在,或者多余一个存在,都是错误。返回;

for (final String dn : cns) {

DirContext test = null;

String finalDn = dn;

try {

System.out.println("Performing LDAP bind with credential: " + dn);

test = template.getContextSource().getContext(

finalDn,

password);

if (test != null) {

return true;

}

} catch (final Exception e) {

e.printStackTrace();

} finally {

if (test != null) {

test.close();

}

}

}

如果存在,并且只有一个,则获取这个用户的信息(测试用户名和密码)。如果成功,返回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属性:

0818b9ca8b590ca3270a3433284dd417.png

在测试Filter的时候,可以在LDAP Browser中进行测试(右键New Pfofile -- 属性-- Entry页,点击Filter右侧的漏斗图标,可以在FilterBuilder

中测试)

0818b9ca8b590ca3270a3433284dd417.png

最后有个关于用户被禁用的问题。如果用户被禁用了,上面的代码会抛出一个异常,需要捕捉异常。如果不想捕捉异常,可以使用

(!(userAccountControl:1.2.840.113556.1.4.803:=2))

来过滤出所有没有被禁用的用户。

上面完整的代码放在http://一一五.com/file/be6o06r5#TestSpringLDAP.java上,需要的可以参考,建议使用前重构下,结构比较乱

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值