首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

施用acegi 控制用户权限

2012-10-28 
使用acegi 控制用户权限????? 公司现有一项目需要使用acegi控制身份验证,所以这几天都在学习acegi,经过网

使用acegi 控制用户权限

????? 公司现有一项目需要使用acegi控制身份验证,所以这几天都在学习acegi,经过网上查找资料,个人比较推崇网友zhanjia写的教程,发现比较完善的(http://zhanjia.iteye.com/category/43399)。

????? acegi配置与spring security配置比较相似,现在我也把自己经过修改后的并且可以运行的项目放在此blog中,以供大家交流。

????? 首先我们需要在web.xml中定义相关设置(Filter、Listener、contextConfigLocation)。

?

<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:/com/javaeye/sunjiesh/jeestudydemo/spring/spring-common.xml,classpath:/com/javaeye/sunjiesh/jeestudydemo/spring/spring-hibernate.xml,classpath:/com/javaeye/sunjiesh/jeestudydemo/spring/spring-services.xml,classpath:/com/javaeye/sunjiesh/jeestudydemo/spring/spring-actions.xml,classpath:/com/javaeye/sunjiesh/jeestudydemo/spring/spring-acegi2.xml </param-value></context-param><filter><filter-name>Acegi Filter Chain Proxy</filter-name><filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class><init-param><param-name>targetClass</param-name><param-value>org.acegisecurity.util.FilterChainProxy</param-value></init-param></filter><filter-mapping><filter-name>Acegi Filter Chain Proxy</filter-name><url-pattern>*.html</url-pattern></filter-mapping><filter-mapping><filter-name>Acegi Filter Chain Proxy</filter-name><url-pattern>*.do</url-pattern></filter-mapping><filter-mapping><filter-name>Acegi Filter Chain Proxy</filter-name><url-pattern>*.jsp</url-pattern></filter-mapping><filter-mapping><filter-name>Acegi Filter Chain Proxy</filter-name><url-pattern>/j_acegi_security_check</url-pattern></filter-mapping><!-- Listener --><listener><listener-class> org.springframework.web.context.ContextLoaderListener </listener-class></listener><!--AcegiSecurity能够限定次数防止一个principal多次并行认证到同一个应用。许多ISV利用这一点来加强授权管理,网管也喜欢这个特性因为可以防止一个用户名被重复使用。例如,你可以限制“Batman”用户从两个不同的session登录系统。--><listener><listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class></listener>

? 在配置完web.xml后,接下来就需要配置acegi的配置文件了,我把很多说明直接放在了xml文件的注释中。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"><!--FilterChainProxy过滤器,需要把需要的过滤器按照顺序放入,这样filterChainProxy会按照过滤器写入的顺序调用。--><bean id="filterChainProxy" ref="sessionRegistry"></property><!--如果concurrentSessionController的exceptionIfMaximumExceeded属性设置为true,那么一旦并发HttpSession数量超过限额,将会重定向到expiredUrl指定的路径--><property name="expiredUrl"><value>/concurrentError.jsp</value></property></bean><!--注意,我们的程序一般并不用直接与SessionRegistryImpl打交道,你只需在Spring的配置文件定义一个Bean就行了--><bean id="sessionRegistry" ref="authenticationManager" /><property name="authenticationFailureUrl"><value>/loginerr.html</value></property><property name="defaultTargetUrl"><value>/afterLogin.do</value></property><property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property><!--  <property name="rememberMeServices" ref="rememberMeServices" />--></bean><!-- 该Filter会在用户登录后,在本地机器上记录用户cookies信息,这样下次访问就不用再登录了。 --><bean id="rememberMeProcessingFilter"ref="authenticationManager" /><property name="rememberMeServices" ref="rememberMeServices" /></bean><!--匿名过滤器。 该过滤器是用来对匿名用户的处理。如果用户尚未登录,将生成一个匿名用户的Authentication存放到ContextHolder中。 即当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限。--><bean id="anonymousProcessingFilter"value="changeThis" /><property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" /></bean><!--exceptionTranslationFilter异常转换过滤器,主要是处理AccessDeniedException和AuthenticationException,将给每个异常找到合适的"去向"。在此配置中。--><bean id="exceptionTranslationFilter" value="/login.jsp" /><property name="forceHttps" value="false" /></bean></property><property name="accessDeniedHandler"><bean value="/accessDenied.jsp" /></bean></property></bean><!-- 采用数据库读取URL保护配置的形式 --><bean id="filterSecurityInterceptor"ref="authenticationManager" /><property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" /><!--<property name="objectDefinitionSource">--><!--<value><![CDATA[--><!--                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON--><!--                PATTERN_TYPE_APACHE_ANT--><!--                /secure.jsp=ROLE_SUPERVISOR--><!--                /admin/index.jsp=ROLE_SUPERVISOR--><!--                /protect*.html=ROLE_SUPERVISOR--><!--            ]]></value>--><!--</property>--><property name="objectDefinitionSource" ref="urlFilterInvocationDefinitionSource"></property></bean><!-- 注销过滤器 --><bean id="logoutFilter" /><constructor-arg><list><!--  <ref bean="rememberMeServices" />--><bean /></list></constructor-arg></bean><!--起到认证管理的作用,它将验证的功能委托给多个Provider, 并通过遍历Providers,以保证获取不同来源的身份认证,若某个Provider能成功确认当前用户的身份,authenticate()方法会返回一个完整的包含用户授权信息的Authentication对象,否则会抛出一个AuthenticationException。 Acegi提供了不同的AuthenticationProvider的实现。如: DaoAuthenticationProvider 从数据库中读取用户信息验证身份AnonymousAuthenticationProvider 匿名用户身份认证RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证AuthByAdapterProvider 使用容器的适配器验证身份 CasAuthenticationProvider根据Yale中心认证服务验证身份, 用于实现单点登陆 JaasAuthenticationProvider从JASS登陆配置中获取用户信息验证身份 RemoteAuthenticationProvider 根据远程服务验证用户身份RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证X509AuthenticationProvider 从X509认证中获取用户信息验证身份TestingAuthenticationProvider 单元测试时使用每个认证者会对自己指定的证明信息进行认证,如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。--><bean id="authenticationManager" /><ref local="anonymousAuthenticationProvider" /><!--<ref local="rememberMeAuthenticationProvider" />  --></list></property><property name="sessionController" ref="concurrentSessionController"></property></bean><bean id="rememberMeServices"value="_acegi_security_remember_me"></property><property name="userDetailsService" ref="userDetailsService" /><!-- cookie中的键值, 防止保存到客户端的cookie中的加密串被恶意篡改 --><property name="key" value="foobar" /><!-- cookie有效时间, 单位为秒, 这里设定为5天内不用再登陆 --><property name="tokenValiditySeconds" value="432000" /></bean><!--DaoAuthenticationProvider 从数据库中读取用户信息验证身份。 进行简单的基于数据库的身份验证。DaoAuthenticationProvider获取数据库中的账号密码并进行匹配,若成功则在通过用户身份的同时返回一个包含授权信息的Authentication对象,否则身份验证失败,抛出一个AuthenticatiionException。--><bean id="daoAuthenticationProvider"ref="userDetailsService" /><!--  <property name="passwordEncoder" ref="passwordEncoder" />--><property name="userCache"><ref local="userCache" /></property></bean><!-- 密名验证Provider --><bean id="anonymousAuthenticationProvider"value="changeThis" /></bean><bean id="rememberMeAuthenticationProvider"value="foobar" /><!--key必须和rememberMeServices中的key一致--></bean><!--exceptionIfMaximumExceeded一般设置为false. 为true时, 如果已有一个该用户登录了,那么在另一个地方登录该用户将抛出异常 如果设置为false, 那么, 如果已有一个该用户登录了系统, 那么在另一个地方也可以登录,登录后前者会被逼退出系统--><bean id="concurrentSessionController"value="1"></property><property name="sessionRegistry" ref="sessionRegistry"></property><property name="exceptionIfMaximumExceeded" value="true"></property></bean><bean id="userDetailsService"ref="userDao"></property></bean><!--httpRequestAccessDecisionManager(投票通过策略管理器)用于管理投票通过策略。Acegi提供三种投票通过策略的实现: AffirmativeBased(至少一个投票者同意方可通过),ConsensusBased(多数投票者同意方可通过), UnanimousBased(所有投票者同意方可通过)。本程序采用AffirmativeBased策略,并且禁止“没人反对就通过”的投票策略。--><bean id="httpRequestAccessDecisionManager" value="false" /><property name="decisionVoters"><list><bean /></list></property></bean><!-- FilterInvocationDefinitionSource的自定义实现类,通过数据库读取Web资源保护配置的形式 --><bean id="urlFilterInvocationDefinitionSource"ref="urlInvocationDefinition"></property></bean><bean id="urlInvocationDefinition"ref="urlService"></property></bean><!--EhCacheBasedUserCache是EhCache的一个缓存实现,提供了向缓存中放入、取得和删除用户明细信息的功能,Acegi需要用它来管理缓存。--><bean id="userCache"ref="userCacheBackend" /></bean><!-- EhCacheFactoryBean是用于维护Cache实例的工厂Bean,Cache需要依赖于CacheManager而存在 --><bean id="userCacheBackend" ref="cacheManager" /><property name="cacheName" value="userCache" /></bean>   <!-- 缓存管理器,一个CacheManager能够创建和维护多个Cache实例 --><bean id="cacheManager"/></beans>

?接下来就是在配置文件中所提及的几个Java类了。

package com.javaeye.sunjiesh.jeestudydemo.acegi;import java.io.Serializable;import org.acegisecurity.ConfigAttributeDefinition;/** * 存放URL与对应角色集合的实体类 *  * @author sunjie * @since 2009-6-9 *  */public class UrlEntryHolder implements Serializable {private static final long serialVersionUID = 2317309106087370323L;/** * 保护的URL模式 */private String url;/** * 要求的角色集合 */private ConfigAttributeDefinition cad;public String getUrl() {return url;}public ConfigAttributeDefinition getCad() {return cad;}public void setUrl(String url) {this.url = url;}public void setCad(ConfigAttributeDefinition cad) {this.cad = cad;}}

?

package com.javaeye.sunjiesh.jeestudydemo.acegi;import java.util.LinkedList;import java.util.List;import java.util.Set;import org.acegisecurity.ConfigAttributeDefinition;import org.acegisecurity.SecurityConfig;import com.javaeye.sunjiesh.jeestudydemo.po.Authority;import com.javaeye.sunjiesh.jeestudydemo.po.Url;import com.javaeye.sunjiesh.jeestudydemo.service.iface.IUrlService;/** *  * @author sunjie * @since 2009-6-9 *  */public class UrlSecuredUrlDefinition {private IUrlService urlService;/** * 得到所有URL资源保护配置。 *  * @return */public List<UrlEntryHolder> getAllUrlEntryHolder() {List<UrlEntryHolder> urlEHs = new LinkedList<UrlEntryHolder>();List<Url> urls = urlService.getAllUrls();for (int i = 0; i < urls.size(); i++) {Url url = urls.get(i);UrlEntryHolder urlEH = new UrlEntryHolder();urlEH.setUrl(url.getUrl());System.out.println("受保护的资源为"+url.getUrl());ConfigAttributeDefinition cad = new ConfigAttributeDefinition();Set<Authority> auths = url.getAuths();for (Authority auth : auths) {cad.addConfigAttribute(new SecurityConfig(auth.getName()));}urlEH.setCad(cad);urlEHs.add(urlEH);}return urlEHs;}public IUrlService getUrlService() {return urlService;}public void setUrlService(IUrlService urlService) {this.urlService = urlService;}}

?

package com.javaeye.sunjiesh.jeestudydemo.acegi;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;import net.sf.ehcache.Ehcache;import org.acegisecurity.ConfigAttributeDefinition;import org.acegisecurity.intercept.web.FilterInvocation;import org.acegisecurity.intercept.web.FilterInvocationDefinitionSource;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.util.AntPathMatcher;import org.springframework.util.PathMatcher;/** * FilterInvocationDefinitionSource实现类 *  * @author sunjie * @since 2009-6-9 *  */public class UrlFilterInvocationDefinitionSource implementsFilterInvocationDefinitionSource {protected static final Log logger = LogFactory.getLog(UrlFilterInvocationDefinitionSource.class);private UrlSecuredUrlDefinition urlInvocationDefinition;private PathMatcher pathMatcher = new AntPathMatcher();@SuppressWarnings("unchecked")public ConfigAttributeDefinition getAttributes(Object object)throws IllegalArgumentException {if ((object == null) || !this.supports(object.getClass())) {throw new IllegalArgumentException("抱歉,目标对象不是FilterInvocation类型");}// TODO 抽取出待请求的URLString url = ((FilterInvocation) object).getRequestUrl();// TODO 获取所有UrlEntryHolder列表(url与角色集合对应列表),如果没有,则返回null。List<UrlEntryHolder> urlEHs = getEntryHolderList();if (urlEHs == null || urlEHs.size() == 0) {return null;}// TODO 去掉待请求url参数信息,数据库中的url信息不带有参数int firstQuestionMarkIndex = url.indexOf("?");if (firstQuestionMarkIndex != -1) {url = url.substring(0, firstQuestionMarkIndex);}Iterator iter = urlEHs.iterator();// TODO 循环判断用户是否有权限访问当前url,// 有则返回ConfigAttributeDefinition(角色集合)while (iter.hasNext()) {UrlEntryHolder entryHolder = (UrlEntryHolder) iter.next();// TODO 判断当前访问的url是否符合entryHolder.getUrl()模式,// 即判断用户是否有权限访问当前url,如url="/secure/index.jsp",// entryHolder.getUrl()="/secure/**", 则有权限访问boolean matched = pathMatcher.match(entryHolder.getUrl(), url);if (logger.isDebugEnabled()) {logger.debug("匹配到如下URL: '" + url + ";模式为 "+ entryHolder.getUrl() + ";是否被匹配:" + matched);}// TODO 如果在用户所有被授权的URL中能找到匹配的,// 则返回该ConfigAttributeDefinition(角色集合)if (matched) {return entryHolder.getCad();}}return null;}@SuppressWarnings("unchecked")public Iterator getConfigAttributeDefinitions() {Set set = new HashSet();Iterator iter = this.getEntryHolderList().iterator();while (iter.hasNext()) {UrlEntryHolder entryHolder = (UrlEntryHolder) iter.next();set.add(entryHolder.getCad());}return set.iterator();}public boolean supports(Class clazz) {if (FilterInvocation.class.isAssignableFrom(clazz)) {return true;} else {return false;}}public List<UrlEntryHolder> getEntryHolderList() {//urlInvocationDefinition = new UrlSecuredUrlDefinition();return urlInvocationDefinition.getAllUrlEntryHolder();}public UrlSecuredUrlDefinition getUrlInvocationDefinition() {return urlInvocationDefinition;}public void setUrlInvocationDefinition(UrlSecuredUrlDefinition urlInvocationDefinition) {this.urlInvocationDefinition = urlInvocationDefinition;}}
?
package com.javaeye.sunjiesh.jeestudydemo.service;import java.util.ArrayList;import java.util.List;import java.util.Set;import org.acegisecurity.GrantedAuthority;import org.acegisecurity.GrantedAuthorityImpl;import org.acegisecurity.userdetails.UserDetails;import org.acegisecurity.userdetails.UserDetailsService;import org.acegisecurity.userdetails.UsernameNotFoundException;import org.apache.log4j.Logger;import org.springframework.dao.DataAccessException;import com.javaeye.sunjiesh.jeestudydemo.dao.iface.IUserDAO;import com.javaeye.sunjiesh.jeestudydemo.po.Authority;import com.javaeye.sunjiesh.jeestudydemo.po.Role;import com.javaeye.sunjiesh.jeestudydemo.po.User;/** * 实现SpringSecurity的UserDetailsService接口,获取用户Detail信息 *  * @author sunjie */public class UserDetailServiceImpl implements UserDetailsService {private static Logger LOG = Logger.getLogger(UserDetailServiceImpl.class);private IUserDAO userDao;public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException, DataAccessException {if (username == null) {LOG.error("username is null");}if (userDao == null) {LOG.error("userDao is null");}User user = userDao.getUserByUserName(username);// TOTO 判断用户存在,如果不存在,则抛出异常。if (user == null) {LOG.error(username + " is not exist",new UsernameNotFoundException(username + " is not exist"));}List<GrantedAuthority> authsList = new ArrayList<GrantedAuthority>();Set<Role> roles=user.getRoles();System.out.println("user.getRoles().size()=" + user.getRoles().size());for (Role role : user.getRoles()) {for (Authority authority : role.getAuths()) {authsList.add(new GrantedAuthorityImpl(authority.getName()));}}// TODO org.acegisecurity.userdetails.User userdetail = new org.acegisecurity.userdetails.User(user.getLoginName(), user.getPassword(), true, true, true,true, authsList.toArray(new GrantedAuthority[authsList.size()]));return userdetail;}public void setUserDao(IUserDAO userDao) {this.userDao = userDao;}public IUserDAO getUserDao() {return userDao;}}

备注: 其它像DAO类及Service相对简单,所以不在此占用空间,可以再附件中查看。

?

?

?

1 楼 cencai09 2009-06-10   请教一个问题: 如果用户不是注销了,而是直接关闭浏览器或是其他操作,那么他想再次登陆的话怎么解决? 2 楼 guoxu1231 2009-06-11   cencai09 写道请教一个问题: 如果用户不是注销了,而是直接关闭浏览器或是其他操作,那么他想再次登陆的话怎么解决?

重新输入用户名/密码...~~~~~

浏览器关闭后  浏览器中用于识别用户的jsessionid就会expire掉, 需要重新登录验证获取了...

或者利用acegi中的remember me功能  在客户端cookie中保存信息,再次打开浏览器就可通过rememberMeProcessingFilter自动登录了.




LZ有几个定义了  但为什么没用啊? 
像concurrentSessionFilter rememberMeProcessingFilter
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor,logoutFilter 3 楼 sunjiesh 2009-06-13   guoxu1231 写道
cencai09 写道请教一个问题: 如果用户不是注销了,而是直接关闭浏览器或是其他操作,那么他想再次登陆的话怎么解决?

重新输入用户名/密码...~~~~~

浏览器关闭后&nbsp; 浏览器中用于识别用户的jsessionid就会expire掉, 需要重新登录验证获取了...

或者利用acegi中的remember me功能&nbsp; 在客户端cookie中保存信息,再次打开浏览器就可通过rememberMeProcessingFilter自动登录了.




LZ有几个定义了&nbsp; 但为什么没用啊?&nbsp;
像concurrentSessionFilter rememberMeProcessingFilter
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor,logoutFilter

首先非常感谢能够回复,因为我当时很多情况没有考虑到,只是试着写了一下代码,主要用来测试权限的,所以其它功能没有写,错误肯定很多,毕竟属于初学者。 4 楼 adamzzww 2009-07-28   因为不太懂,所以下了看看,怎么com.javaeye.sunjiesh.sjutils没有?不过注释写的很详细啊,我能看懂 。。 5 楼 sunjiesh 2009-07-28   adamzzww 写道因为不太懂,所以下了看看,怎么com.javaeye.sunjiesh.sjutils没有?不过注释写的很详细啊,我能看懂 。。
因为com.javaeye.sunjiesh.sjutils属于个人写的一些工具类,如果要的话可以再联系。 6 楼 mcl09 2009-09-27   楼主可以把用户权限的代码发我一份吗?非常感谢,初学acegi.      mcl09@126.com 7 楼 freepig 2009-09-29   这个东东现在叫Spring Security了。。。可以去看看springside3的源码,很容易懂滴。。。。

热点排行