ACEGI结合LDAP进行统一用户管理

用acegi进行用户登陆和权限判断已有好几个项目的经验了,但是用户密码和权限信息都是存储在数据库里面的,各套系统之间的用户数据彼此都互相独立,这个系统一套用户名密码,那个系统则是另一套,每个用户要同时记住好几个密码,比较麻烦。但是现在的发展趋势都是用LDAP对用户进行统一管理,即在LDAP服务器中进行认证管理,在业务系统里进行授权管理。
这样子,只要各业务系统都练到这一个LDAP服务器上,大家的账号和密码就都是统一的。

以下是acegi与Windows AD进行结合,实现用户统一管理的例子。

实现的思路是,在LDAP进行用户认证,在数据库中进行权限鉴别。

acegi也有针对LDAP进行认证和鉴权的全部代码和推荐配置,但是比较单纯,要么是全部数据库,要么是全部LDAP,对于LDAP+DB,还没有看到网上有例子,只有一个台湾的家伙,还觉得满保密,不想公开他的代码。

Windows AD,就是Windows Active Directory,是微软的域控制器,与windows操作系统进行了集成,支持LDAP协议。

1、将daoAuthenticationProvider换成ldapAuthenticationProvider

<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="ldapAuthenticationProvider"/>
</list>
</property>
<property name="sessionController">
<ref bean="concurrentSessionController"/>
</property>
</bean>

2、添加ldapAuthenticationProvider的bean定义:

<!--
LDAP+DB进行登陆判断
LDAP中进行用户名认证(authenticator)
DB中进行权限判断(daoPopulator)
-->
<bean id="ldapAuthenticationProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg><ref local="authenticator"/></constructor-arg>
<constructor-arg><ref local="daoPopulator"/></constructor-arg>
<property name="userCache" ref="userCache"/>
</bean>

3、添加authenticator的bean定义

<!--
在LDAP Server中判断用户和密码的有效性
initialDirContextFactory:LDAP上下文环境
userDnPatterns:用户匹配模式,其中sAMAccountName是AD中用户名的字段名称
-->
<bean id="authenticator" class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userDnPatterns">
<list>
<value>sAMAccountName={0},ou=BroadText_SH,DC=broadtext,DC=local</value>
</list>
</property>
<property name="userSearch"><ref local="userSearch"/></property>
</bean>

其中userDnPatterns是根据不同的LDAP服务有着不同的配置,sAMAccountName是AD中存储用户账号的字段名称,后面那一串是账号路径的一部分。
4、编写自己的DaoAuthoritiesPopulator,从数据库中查询权限,这里有两个参数:数据源和查询SQL,数据源可以直接利用已经定义的dataSource,查询SQL就是以前在jdbcDaoImpl中用过的authoritiesByUsernameQuery。类中的代码如下(省去了dataSource和authoritiesByUsernameQuery的setter和getter):

private static final Log logger = LogFactory.getLog(DaoAuthoritiesPopulator.class);
protected MappingSqlQuery authoritiesByUsernameMapping;
private String authoritiesByUsernameQuery;
private String rolePrefix = "";
private DataSource dataSource;
/* (non-Javadoc)
* @see org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator#getGrantedAuthorities(org.acegisecurity.userdetails.ldap.LdapUserDetails)
*/
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails user){
this.authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());

if (dbAuths.size() == 0) {
throw new PermissionNotEnoughException("User has no GrantedAuthority");
}

GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);

if (logger.isDebugEnabled()) {
logger.debug("Getting authorities for user " + user.getUsername());
logger.debug("授权成功 :" + user.getUsername());
}
return arrayAuths;
}
/**
* Query object to look up a user's authorities.
*/
protected class AuthoritiesByUsernameMapping extends MappingSqlQuery {
protected AuthoritiesByUsernameMapping(DataSource ds) {
super(ds, authoritiesByUsernameQuery);
declareParameter(new SqlParameter(Types.VARCHAR));
compile();
}

protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
String roleName = rolePrefix + rs.getString(2);
GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);

return authority;
}
}

bean定义XML文件如下:

<!--
从数据库中获取权限
dataSource:数据源
authoritiesByUsernameQuery:根据用户名查询权限的sql
-->
<bean id="daoPopulator" class="com.broadtext.eim.security.ldap.DaoAuthoritiesPopulator">
<property name="dataSource" ref="dataSource"/>
<property name="authoritiesByUsernameQuery">
<value>select u.username,p.name from sys_user u,sys_role r,sys_permission p,sys_user_role ur,sys_role_permis rp where u.id=ur.user_id and r.id=ur.role_id and p.id=rp.permission_id and
r.id=rp.role_id and p.open_flag='1' and u.username=?</value>
</property>
</bean>

5、添加LDAP服务器上下文的bean配置:

<!--
LDAP Server的配置信息
构造函数的参数:LDAP服务器地址和端口
managerDn:管理员账号在LDAP中的地址
managerPassword:管理员密码
-->
<bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://你的LDAP服务器地址:端口"/>
<property name="managerDn"><value>yourAdministratorName</value></property>
<property name="managerPassword"><value>yourAdministratorPassword</value></property>
</bean>

这样定义以后,大概相当于指定了进行用户认证的数据库schema。

在这里,推荐一个LDAP的浏览工具,叫LDAP Browser,下载地址是[url]http://file1.softsea.net/30190/ldapbrowser25ce.zip[/url],可以用这个工具来判断你所拿到的上面三个参数的值是否正确。
6、添加userSearch的bean定义:

<!--
在LDAP服务器校验账号的时候,定义搜索账号的规则
-->
<bean id="userSearch" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg>
<value>ou=BroadText_SH,DC=broadtext,DC=local</value>
</constructor-arg>
<constructor-arg>
<value>(sAMAccountName={0})</value>
</constructor-arg>
<constructor-arg>
<ref local="initialDirContextFactory"/>
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>


第一个构造参数表示搜索路径(指定了用户认证的数据表),第二个构造参数表示用怎么的查询条件去搜索,第三个构造参数是要搜索的上下文环境(指定了用户认证的数据库schema),,属性searchSubtree为true表示可以在子节点中进行搜索。

大致配置就是这些了。
阅读更多

没有更多推荐了,返回首页