<!-- * set authenticationManager , an important manager--> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref bean="daoAuthenticationProvider"/> <bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> <property name="key" value="anonymousUser"/> </bean> <bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> <property name="key" value="rememberMeUser"/> </bean> </list> </property> <!-- here you can set sessionController for user can't repeat (dulplicate)--> <property name="sessionController" ref="concurrentSessionController"/> </bean> |
三种认证提供者
daoAuthenticationProvider通过数据库方式来进行认证,也可以通过配置文件实现
AnonymousAuthenticationProvider,匿名用户认证,key值必须与 anonymouseProcessingFilter的key值一样,否则无法获取匿名用户的信息.
RememberMeAuthenticationProvider通过cookie认证,key值和rememberMeServices
的key值相同,否则无法取得相应的cookie信息.
sessionController,限制同一用户的登录次数,也就是同一用户在不同httpSession
中登录的次数
<!-- set concurrentSessionControllter --> <bean id="concurrentSessionController" class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl"> <property name="maximumSessions" value="1"/> <property name="exceptionIfMaximumExceeded" value="true"/> <property name="sessionRegistry" ref="sessionRegistryImpl"/> </bean> <!-- set sessionRegistryImpl --> <bean id="sessionRegistryImpl" class="org.acegisecurity.concurrent.SessionRegistryImpl"/> |
这样可以限制同一用户在不同session中的次数,sessionRegistry对用户与sessionId以map的结构进行记录,但出现的一个问题就是,在用户logout时,或者session已经invalidate或expired时,他不会移除在sessionRegistry中保存的信息,
虽然sessionRegistry也提供了removeSessionInformation(String sessionID)这个方法.
结果就是,在下次登录的时候,认证管理器先在sessionRegistry判断有此用户没,
如果有,并超过maximumSessions,就抛出异常,并无法认证.而sessionRegistry以map
结构,并用static final形式存放信息,在不重启服务器的情况下,用户session注销后是没办法removeSessionInformation的.
我的解决办法是,通过acegi提供的listenter,在用户session destroy,执行removeSessionInformation.
public class HttpSessionEventListener extends HttpSessionEventPublisher { /** SessionRegistry sessionRegistry = null; } } |
缺点是:wac.getBean("sessionRegistryImpl");bean name 写死了,缺乏灵活
web.xml添加如下:
<!-- httpSessionEvent ,listener session state --> <listener> <listener-class>com.runsa.components.acegi.support.HttpSessionEventListener</listener-class> </listener> |
虽然解决了当前问题,但仍然避免不了有些用户,不通过安全退出,直接关闭浏览器,
在session没有destroy时,再次登录,也会出错.在javaeye也看到类似有人提出这样的问题,捕捉window.close()事件,利用ajax在关闭浏览器的时候,进行session.invalidate(),但是,window.close()不那么容易捕捉的吧,而且个浏览器差异也是有的。
通过db认证用户
<!-- set daoAuthenticationProvider --> <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> <property name="userDetailsService" ref="hibernateDaoImpl"/> <property name="userCache" ref="userCache"/> <!-- here you can set passwordEncoder --> </bean> |
userDetailsService,保存userContext的,也就是用户的信息和相应的权限.acegi默认提供jdbcDaoImpl的实现,这里我使用hibernate自己实现了
<!-- set hibernateDaoImpl,and you can implements daoImpl with Hibernate for yourself --> <bean id="hibernateDaoImpl" class="com.runsa.components.acegi.support.HibernateDaoImpl"> <property name="userService" ref="usersService"/> <property name="usernameBasedPrimaryKey" value="false"/> </bean> |
下边是实现的code:
public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT new Users(u.userName,u.userPass,u.enabled) FROM Users u WHERE userName = ?";
public HibernateDaoImpl() {
protected void addCustomRole(String username, List roles) {} @SuppressWarnings("unchecked") if (users.size() == 0) { UserDetails user = users.get(0); // contains no GrantedAuthority[] List dbRoles = roleByUsername(user.getUsername()); addCustomRole(user.getUsername(), dbRoles); if (dbRoles.size() == 0) { GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbRoles.toArray(new GrantedAuthority[dbRoles.size()]); String returnUsername = user.getUsername(); if (!usernameBasedPrimaryKey) { return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths); /** /** |
主要实现的方法public UserDetails loadUserByUsername(String username)通过用户名查询用户资料,然后再通过用户用查询用户的具有角色(权限).
也可以设置passwordEncoder对密码加密,acegi提供md5,sha,等好几种加密方式.
显然,在这里认证设置了加密方式,在获取用户信息的时候,对用户的密码也需要相同的加密方式.
acegi的资源文件默认提供E文的,要自己实现本地化
<!-- set load message resource --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="/WEB-INF/classes/messages_zh"/> </bean> |
下边设置资源从数据库获取,并实现动态更新
<!-- * set filterDefinitionSource for myself --> <bean id="filterDefinitionSource" class="com.runsa.components.acegi.inteceptor.web.DBFilterInvocationDefinationSource"> <property name="convertUrlToLowercaseBeforeComparison" value="true"/> <property name="useAntPath" value="true"/> <property name="acegiCacheManager" ref="acegiCacheManager"/> </bean> |
....next page