客户要求提供Active Directory登录验证.原因是他们公司是在美国的上市公司,要求上市公司的软件必须提供统一登录认证.他们想到的解决方案就是Active Directory.呵呵.而我们开发的系统使用的是acegi登录验证.嗯,这个,首先想到的就是ldap,没的想,网上搜吧,资料还不算少.那就配吧,配好了需要测试吧,那就找服务器咧.这公司的域服务器不让用,那就openldap吧.下好了又一通查资料,openldap总得配是吧.总算给配好了,不容易呀.就拿到客户环境去试吧,嘿嘿,客户说了不可能给我们管理员帐号密码,只有用户登录帐号跟密码,傻了.acegi的ldap验证就要这个啊.
回来继续搞咧,咱也没办法不是?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="filterChainProxy"
class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<!-- 自己写一个认证提供者类 我加的-->
<ref local="activeDirectoryProvider" />
<ref local="daoAuthenticationProvider" />
<ref local="anonymousAuthenticationProvider" />
<ref local="rememberMeAuthenticationProvider" />
</list>
</property>
</bean>
<!-- 认证提供者类的配置 我加的-->
<bean id="activeDirectoryProvider"
class="net.omw.utility.AcegiTestProvider">
<property name="url" value="ldap://172.108.4.2"> </property>
<property name="port" value="389"> </property>
<!--domain取值域服务器的配置-->
<property name="domain" value="SUNTECH"> </property>
<!-- <property name="sessionController" ref="concurrentSessionController"></property> -->
</bean>
<bean id="jdbcDaoImpl"
class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource">
<ref bean="coreDataSource" />
</property>
<property name="usersByUsernameQuery">
<value>
select C_OPER_ID,C_PASSWORD,1 from Operator where
C_OPER_ID = ? and C_STATUS='Y'
</value>
</property>
<property name="authoritiesByUsernameQuery">
<value>
select C_OPER_ID,C_PASSWORD,1 from Operator where
C_OPER_ID = ? and C_STATUS='Y'
</value>
</property>
</bean>
<bean id="passwordEncoder"
class="org.acegisecurity.providers.encoding.Md5PasswordEncoder">
<property name="encodeHashAsBase64" value="false"></property>
</bean>
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref local="jdbcDaoImpl" />
</property>
<!-- <property name="userCache">
<ref local="userCache" />
</property>-->
<property name="passwordEncoder">
<ref local="passwordEncoder" />
</property>
</bean>
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
<bean id="userCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager" />
</property>
<property name="cacheName">
<value>userCache</value>
</property>
</bean>
<bean id="userCache"
class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache">
<ref local="userCacheBackend" />
</property>
</bean>
<bean id="loggerListener"
class="org.acegisecurity.event.authentication.LoggerListener" />
<bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key">
<value>foobar</value>
</property>
<property name="userAttribute">
<value>anonymousUser,ROLE_ANONYMOUS</value>
</property>
</bean>
<bean id="anonymousAuthenticationProvider"
class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key">
<value>foobar</value>
</property>
</bean>
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
</bean>
<bean id="rememberMeProcessingFilter"
class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager">
<ref local="authenticationManager" />
</property>
<property name="rememberMeServices">
<ref local="rememberMeServices" />
</property>
</bean>
<bean id="rememberMeServices"
class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService">
<ref local="jdbcDaoImpl" />
</property>
<property name="key">
<value>springRocks</value>
</property>
</bean>
<bean id="rememberMeAuthenticationProvider"
class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key">
<value>springRocks</value>
</property>
</bean>
<bean id="logoutFilter"
class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/login/loginPage.jsp" />
<constructor-arg>
<list>
<ref bean="rememberMeServices" />
<bean
class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
</list>
</constructor-arg>
</bean>
<bean id="securityContextHolderAwareRequestFilter"
class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter" />
<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<ref local="authenticationProcessingFilterEntryPoint" />
</property>
<property name="accessDeniedHandler">
<bean
class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage"
value="/common/AccessDenied.jsp" />
</bean>
</property>
</bean>
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager">
<ref bean="authenticationManager" />
</property>
<property name="authenticationFailureUrl">
<value>/login/Login.action?login_msg=1</value>
</property>
<property name="defaultTargetUrl">
<value>/login/Login.action?login_msg=0</value>
</property>
<property name="filterProcessesUrl">
<value>/j_acegi_security_check</value>
</property>
<property name="rememberMeServices">
<ref local="rememberMeServices" />
</property>
</bean>
<bean id="authenticationProcessingFilterEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl">
<value>/login/loginPage.jsp</value>
</property>
<property name="forceHttps">
<value>false</value>
</property>
<property name="serverSideRedirect" value="false"></property>
</bean>
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions">
<value>false</value>
</property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="validateConfigAttributes" value="true" />
<property name="authenticationManager">
<ref bean="authenticationManager" />
</property>
<property name="accessDecisionManager">
<ref local="httpRequestAccessDecisionManager" />
</property>
<property name="objectDefinitionSource"
ref="rdbmsFilterInvocationDefinitionSource" />
</bean>
<bean id="rdbmsFilterInvocationDefinitionSource"
class="net.omw.utility.acegi.interceptor.RdbmsFilterInvocationDefinitionSource">
<property name="dataSource">
<ref bean="coreDataSource" />
</property>
<property name="webresdbCache" ref="webresCacheBackend" />
</bean>
<bean id="webresCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager" />
</property>
<property name="cacheName">
<value>webresdbCache</value>
</property>
</bean>
<!--
<bean id="switchUserProcessingFilter" class="org.acegisecurity.ui.switchuser.SwitchUserProcessingFilter">
<property name="userDetailsService" ref="jdbcDaoImpl" />
<property name="switchUserUrl"><value>/j_acegi_switch_user</value></property>
<property name="exitUserUrl"><value>/j_acegi_exit_user</value></property>
<property name="targetUrl"><value>/secure/index.htm</value></property>
</bean>
-->
<bean id="authenticationLoggerListener"
class="org.acegisecurity.event.authentication.LoggerListener" />
<bean id="authorizationLoggerListener"
class="org.acegisecurity.event.authorization.LoggerListener" />
</beans>
AcegiTestProvider类从AbstractUserDetailsAuthenticationProvider继承,有两个方法必须实现
additionalAuthenticationChecks()和retrieveUser()方法.retrieveUser返回UserDetails,UserDetails的实现可以包装更多的信息.但在本例中几乎没什么太大的作用,仅仅是为了返回而重新定义了一个类
真正的验证逻辑发生在additionalAuthenticationChecks方法里抛出异常就算用户登录失败
package net.omw.utility;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
public class AcegiTestProvider extends
AbstractUserDetailsAuthenticationProvider {
private String url;
private String port;
private String domain;
/*
* 验证逻辑
* @see org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider#additionalAuthenticationChecks(org.acegisecurity.userdetails.UserDetails, org.acegisecurity.providers.UsernamePasswordAuthenticationToken)
*
*/
@Override
protected void additionalAuthenticationChecks(UserDetails arg0,
UsernamePasswordAuthenticationToken arg1)
throws AuthenticationException {
// TODO Auto-generated method stub
String URL = url+":"+port;
String username=domain+"\\"+arg0.getUsername();
String password = arg0.getPassword();
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,"ldap://172.18.0.42:389");
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.SECURITY_PRINCIPAL,
username);
env.put(Context.SECURITY_CREDENTIALS,password);
env.put("com.sun.jndi.ldap.connect.pool","true");
env.put("java.naming.referral","follow");
try{
new InitialLdapContext(env,null);
}
catch(NamingException e){
// Authentication failed
throw new UsernameNotFoundException(e.toString());
}
}
@Override
protected UserDetails retrieveUser(String arg0,
UsernamePasswordAuthenticationToken arg1)
throws AuthenticationException {
// TODO Auto-generated method stub
GrantedAuthority[] authorities = new GrantedAuthority[1];
authorities[0] = new GrantedAuthorityImpl("ROLE_SUPERVISOR");
String password = (String) arg1.getCredentials();
/*String username = "";
Object obj = arg1.getPrincipal();
if (obj instanceof UserDetails) {
username = ((UserDetails) obj).getUsername();
} else {
username = obj.toString();
}
*/
UserDetails userDetails = new UserDetailsImpl(authorities, password,
arg1.getName(), true, true, true, true);
//if(true)
// throw new AuthenticationCredentialsNotFoundException("t");
return userDetails;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
}
package net.omw.utility;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.userdetails.UserDetails;
public class UserDetailsImpl implements UserDetails {
GrantedAuthority[] authorities;
String password;
String username;
boolean isAccountNonExpired;
boolean isAccountNonLocked;
boolean isCredentialsNonExpired;
boolean isEnabled;
UserDetailsImpl(GrantedAuthority[] authorities, String password,
String username, boolean isAccountNonExpired,
boolean isCredentialsNonExpired, boolean isEnabled,
boolean isAccountNonLocked) {
this.authorities = authorities;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isEnabled = isEnabled;
this.password = password;
this.username = username;
this.isCredentialsNonExpired = isCredentialsNonExpired;
}
@Override
public GrantedAuthority[] getAuthorities() {
// TODO Auto-generated method stub
return authorities;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return username;
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return isAccountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return isAccountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return isCredentialsNonExpired;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return isEnabled;
}
}