Spring下的权限框架 spring security总结
- spring
security总结 - 首先导入spring
security所需要的jar包 - spring-security-core-2.0.5.RELEASE.jar
- spring-security-core-tiger-2.0.5.RELEASE.jar
- 一.配置过滤器
- 在web.xml中定义如下过滤器
<filter> <filter-name>springSecurityFilterChai n</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChai n</filter-name> <url-pattern> - CREATE
TABLE `t_account` ( `id` int(11) NOT NULL, `username` varchar(255) default NULL, `password` varchar(255) default NULL, `enabled` int default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `t_role` ( `id` int(11) NOT NULL, `name` varchar(255) default NULL, `descn` varchar(255) default NULL, PRIMARY KEY (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - CREATE
TABLE `t_account_role` ( `a_id` int(11) NOT NULL, `r_id` int(11) NOT NULL, PRIMARY KEY (`a_id`,`r_id`), KEY `FK1C2BC9332D31C656` (`r_id`), KEY `FK1C2BC93371CCC630` (`a_id`), CONSTRAINT `FK1C2BC93384B0A30E` FOREIGN KEY (`a_id`) REFERENCES `t_account` (`id`), CONSTRAINT `FK1C2BC9332D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - insert
into t_account values(1,'zhangsan','123',1); - insert
into t_account values(2,'lisi','321',1); - insert
into t_role values(1,'系统管理员','ROLE_ADMIN'); - insert
into t_role values(2,'普通用户','ROLE_USER'); - insert
into t_account_role values(1,2); insert into t_account_role values(2,1); - 当用户登录时,spring
security首先判断用户是否可以登录。用户登录后spring security获得该用户的 - 所有权限以判断用户是否可以访问资源。
- spring配置文件中定义
- <authentication-provider>
<jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,enabled from t_account where username=?" authorities-by-username-query="select r.descn from t_account_role ar join t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/> </authentication-provider> users-by-username-query:根据用户名查找用户 authorities-by-username-query:根据用户名查找这个用户所有的角色名,将用户访问的URL地址和 查询结果与<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />标签进行匹配。 匹配成功就允许访问,否则就返回到提示页面。 在<http>标签中添加登录页面等信息 <http auto-config='true'> <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern=" - CREATE
TABLE `t_account` ( `id` int(11) NOT NULL, `username` varchar(255) default NULL, `password` varchar(255) default NULL, `enabled` int default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `t_role` ( `id` int(11) NOT NULL, `name` varchar(255) default NULL, `descn` varchar(255) default NULL, PRIMARY KEY (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - CREATE
TABLE `t_account_role` ( `a_id` int(11) NOT NULL, `r_id` int(11) NOT NULL, PRIMARY KEY (`a_id`,`r_id`), KEY `FK1C2BC9332D31C656` (`r_id`), KEY `FK1C2BC93371CCC630` (`a_id`), CONSTRAINT `FK1C2BC93384B0A30E` FOREIGN KEY (`a_id`) REFERENCES `t_account` (`id`), CONSTRAINT `FK1C2BC9332D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - CREATE
TABLE `t_module` ( `id` int(11) NOT NULL, `name` varchar(255) default NULL, `address` varchar(255) default NULL, PRIMARY KEY (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - CREATE
TABLE `t_module_role` ( `m_id` int(11) NOT NULL, `r_id` int(11) NOT NULL, PRIMARY KEY (`m_id`,`r_id`), KEY `FKA713071E2D31C656` (`r_id`), KEY `FKA713071ED78C9071` (`m_id`), CONSTRAINT `FKA713071ED78C9071` FOREIGN KEY (`m_id`) REFERENCES `t_module` (`id`), CONSTRAINT `FKA713071E2D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - insert
into t_account values(1,'zhangsan','123',1); - insert
into t_account values(2,'lisi','321',1); - insert
into t_role values(1,'系统管理员','ROLE_ADMIN'); - insert
into t_role values(2,'普通用户','ROLE_USER'); - insert
into t_account_role values(1,2); insert into t_account_role values(2,1); insert into t_module values(1,'部门管理','/dept.jsp'); insert into t_module values(2,'人员管理','/emp.jsp'); insert into `t_module_role` values(1,1); insert into `t_module_role` values(1,2); insert into `t_module_role` values(2,1); - 1.在自定义的过滤器中获取资源的URL地址和角色名以取代spring配置文件中原有的<intercept-url
pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/> 过滤器代码: - import
java.sql.ResultSet; - import
java.sql.SQLException; - import
java.util.LinkedHashMap; - import
java.util.List; - import
java.util.Map; - import
javax.sql.DataSource; - import
org.springframework.beans.factory.FactoryBean; - import
org.springframework.jdbc.core.support.JdbcDaoSupport; - import
org.springframework.jdbc.object.MappingSqlQuery; - import
org.springframework.security.ConfigAttributeDefinitio n; - import
org.springframework.security.ConfigAttributeEditor; - import
org.springframework.security.intercept.web.DefaultFilterInvocationD efinitionSource; - import
org.springframework.security.intercept.web.FilterInvocationDefiniti onSource; - import
org.springframework.security.intercept.web.RequestKey; - import
org.springframework.security.util.AntUrlPathMatcher; - import
org.springframework.security.util.UrlMatcher; - public
class JdbcFilterInvocationDefi nitionSourceFactoryBean extends JdbcDaoSupport implements FactoryBean { private String resourceQuery; public boolean isSingleton() { return true; } public Class getObjectType() { return FilterInvocationDefiniti onSource.class; } public Object getObject() { return new DefaultFilterInvocationD efinitionSource(this .getUrlMatcher(), this.buildRequestMap()); } protected Map<String, String> findResources() { ResourceMapping resourceMapping = new ResourceMapping(getDataSource(), resourceQuery); Map<String, String> resourceMap = new LinkedHashMap<String, String>(); for (Resource resource : (List<Resource>) resourceMapping.execute()) { String url = resource.getUrl(); String role = resource.getRole(); if (resourceMap.containsKey(url)) { String value = resourceMap.get(url); resourceMap.put(url, value + "," + role); } else { resourceMap.put(url, role); } } return resourceMap; } protected LinkedHashMap<RequestKey, ConfigAttributeDefinitio n> buildRequestMap() { LinkedHashMap<RequestKey, ConfigAttributeDefinitio n> requestMap = null; requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinitio n>(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); Map<String, String> resourceMap = this.findResources(); for (Map.Entry<String, String> entry : resourceMap.entrySet()) { RequestKey key = new RequestKey(entry.getKey(), null); editor.setAsText(entry.getValue()); requestMap.put(key, (ConfigAttributeDefinitio n) editor.getValue()); } return requestMap; } protected UrlMatcher getUrlMatcher() { return new AntUrlPathMatcher(); } public void setResourceQuery(String resourceQuery) { this.resourceQuery = resourceQuery; } private class Resource { private String url; private String role; public Resource(String url, String role) { this.url = url; this.role = role; } public String getUrl() { return url; } public String getRole() { return role; } } private class ResourceMapping extends MappingSqlQuery { protected ResourceMapping(DataSource dataSource, String resourceQuery) { super(dataSource, resourceQuery); compile(); } protected Object mapRow(ResultSet rs, int rownum) throws SQLException { String url = rs.getString(1); String role = rs.getString(2); Resource resource = new Resource(url, role); return resource; } } - }
- 将自定义的过滤器放入到原有的spring
security过滤器链中(在spring配置文件中配置) - 定义自定义过滤器(sql语句用于查询资源的URL地址
如:/index.jsp 和角色名 如ROLE_USER) - <beans:bean
id="filterInvocationDefiniti onSource" class="com.lovo.JdbcFilterInvocationDefi nitionSourceFactoryBean"> <beans:property name="dataSource" ref="dataSource"/> <beans:property name="resourceQuery" value=" select m.address,r.descn - from
t_module_role mr - join
t_module m on mr.m_id=m.id - join
t_role r on mr.r_id=r.id; "/> </beans:bean> 将自定义过滤器放入过滤器链中 <beans:bean id="filterSecurityIntercepto r" class="org.springframework.security.intercept.web.FilterSecurityIntercepto r" autowire="byType"> <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/> <beans:property name="objectDefinitionSource" ref="filterInvocationDefiniti onSource" /> </beans:bean> 注意:FilterSecurityIntercepto r过滤器会向request中写入一个标记,用于标记是否已经控制了当前请求,以避免对同一请求多次处理,导致第2个FilterSecurityIntercepto r不会再次执行。 在<http>中不需要再定义<intercept-url>,如下: <http auto-config='true'> <form-login login-page="/login.jsp" authentication-failure-url="/error.jsp" default-target-url="/index.jsp" /> </http> 自定义的过滤器就从配置文件中读取sql,查询结果就是角色和资源,用户登录时就在session中保存了用户的角色。 注意:intercept-url的先后顺序,spring security使用第一个能匹配的intercept-url标签进行权限控制。 现在intercept-url来源于数据库,所以在sql查询时注意角色和资源的顺序。 建议在角色和资源的中间表中添加1个字段用于标识顺序,(按从严到宽的顺序) 表结构修改如下: - CREATE
TABLE `t_module_role` ( `m_id` int(11) NOT NULL, `r_id` int(11) NOT NULL, `priority` int(11) default NULL, PRIMARY KEY (`m_id`,`r_id`), KEY `FKA713071E2D31C656` (`r_id`), KEY `FKA713071ED78C9071` (`m_id`), CONSTRAINT `FKA713071ED78C9071` FOREIGN KEY (`m_id`) REFERENCES `t_module` (`id`), CONSTRAINT `FKA713071E2D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`) - )
ENGINE=InnoDB DEFAULT CHARSET=utf8; - 数据如下:
- insert
into t_account values(1,'zhangsan','123',1); - insert
into t_account values(2,'lisi','321',1); - insert
into t_role values(1,'系统管理员','ROLE_ADMIN'); - insert
into t_role values(2,'普通用户','ROLE_USER'); - insert
into t_account_role values(1,2); insert into t_account_role values(2,1); insert into t_module values(1,'部门管理','/dept.jsp'); insert into t_module values(2,'人员管理','/emp.jsp'); insert into `t_module_role` values(1,1,3); insert into `t_module_role` values(1,2,2); insert into `t_module_role` values(2,1,1); 自定义过滤器修改如下: <beans:bean id="filterInvocationDefiniti onSource" class="com.lovo.JdbcFilterInvocationDefi nitionSourceFactoryBean"> <beans:property name="dataSource" ref="dataSource"/> <beans:property name="resourceQuery" value=" select m.address,r.descn - from
t_module_role mr - join
t_module m on mr.m_id=m.id - join
t_role r on mr.r_id=r.id - order
by mr.priority "/> </beans:bean> 如果持久层使用的是hibernate,那么只需要给自定义过滤器注入1个hibernateTemplate. 在自定义过滤器中就不需要再使用mapRow的方式。而是直接获取url和角色名即可。 例如: public class ModuleFilter implements FactoryBean { - private
String resourceQuery; public boolean isSingleton() { return true; } public Class getObjectType() { return FilterInvocationDefiniti onSource.class; } public Object getObject() { return new DefaultFilterInvocationD efinitionSource(this .getUrlMatcher(), this.buildRequestMap()); } protected Map<String, String> findResources() { ResourceMapping resourceMapping = new ResourceMapping(); Map<String, String> resourceMap = new LinkedHashMap<String, String>(); for (Resource resource : (List<Resource>) resourceMapping.execute()) { String url = resource.getUrl(); String role = resource.getRole(); if (resourceMap.containsKey(url)) { String value = resourceMap.get(url); resourceMap.put(url, value + "," + role); } else { resourceMap.put(url, role); } } return resourceMap; } protected LinkedHashMap<RequestKey, ConfigAttributeDefinitio n> buildRequestMap() { LinkedHashMap<RequestKey, ConfigAttributeDefinitio n> requestMap = null; requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinitio n>(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); Map<String, String> resourceMap = this.findResources(); for (Map.Entry<String, String> entry : resourceMap.entrySet()) { RequestKey key = new RequestKey(entry.getKey(), null); editor.setAsText(entry.getValue()); requestMap.put(key, (ConfigAttributeDefinitio n) editor.getValue()); } return requestMap; } protected UrlMatcher getUrlMatcher() { return new AntUrlPathMatcher(); } public void setResourceQuery(String resourceQuery) { this.resourceQuery = resourceQuery; } private class Resource { private String url; private String role; public Resource(String url, String role) { this.url = url; this.role = role; } public String getUrl() { return url; } public String getRole() { return role; } } private class ResourceMapping{ public List<Resource> execute(){ List<Resource> rlist = new ArrayList<Resource>(); List<Role> list = hibernateTemplate.find(resourceQuery); for(int i=0;i<list.size();i++){ Role role = list.get(i); Set<Module> set = role.getModuleSet(); Iterator<Module> it = set.iterator(); while(it.hasNext()){ Module m = it.next(); Resource re = new Resource(m.getUrl(),role.getDescn()); rlist.add(re); } } - return
rlist; } } public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { - this.hibernateTemplate
= hibernateTemplate; - }
- private
HibernateTemplate hibernateTemplate; - }
- 而从灵活性的角度考虑,把hql写在配置文件中
- <beans:bean
id="moduleFilter" class="com.lovo.ModuleFilter"> <beans:property name="resourceQuery" value="from com.lovo.po.Role order by ind"> </beans:property> <beans:property name="hibernateTemplate" ref="hibernateTemplate"> </beans:property> </beans:bean> 问题:系统只会在初始化的时候从数据库中加载信息。无法识别数据库中信息的改变。 解决:每个jsp页面上重新内存 代码:每个jsp页面include如下代码: - <%@page
import="org.springframework.context.ApplicationContext"%> - <%@page
import="org.springframework.web.context.support.WebApplicationContextUti ls"%> - <%@page
import="org.springframework.beans.factory.FactoryBean"%> - <%@page
import="org.springframework.security.intercept.web.FilterSecurityIntercepto r"%> - <%@page
import="org.springframework.security.intercept.web.FilterInvocationDefiniti onSource"%> - <%
ApplicationContext ctx = WebApplicationContextUti ls.getWebApplicationContext (application); FactoryBean factoryBean = (FactoryBean) ctx.getBean("&自定义过滤器的id"); FilterInvocationDefiniti onSource fids = (FilterInvocationDefiniti onSource) factoryBean.getObject(); FilterSecurityIntercepto r filter = (FilterSecurityIntercepto r) ctx.getBean("filterSecurityIntercepto r"); filter.setObjectDefinitionSourc e(fids); - %>
- 控制用户信息
- 用户密码MD5加密:
- <authentication-provider>
<password-encoder hash="md5"/> <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,enabled from t_account where username=?" authorities-by-username-query="select a.username,r.descn from t_account_role ar join t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/> </authentication-provider> 盐值加密 <authentication-provider> <password-encoder hash="md5"> <salt-source user-property="username"/> </password-encoder> <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,enabled from t_account where username=?" authorities-by-username-query="select a.username,r.descn from t_account_role ar join t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/> </authentication-provider> 用户信息缓存,使用spring内置的ehCache实现 <beans:bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBea n"></beans:bean> - <beans:bean
id="userEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> - <beans:property
name="cacheManager" ref="cacheManager"></beans:property> - <beans:property
name="cacheName" value="userCache"></beans:property> - </beans:bean>
- <beans:bean
id="userCache" class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache"> - <beans:property
name="cache" ref="userEhCache"></beans:property> - </beans:bean>
- 在src目录下新建ehcache.xml
- <ehcache>
<diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <cache name="userCache" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="3600" overflowToDisk="true" /> - </ehcache>
- 在程序中获取用户信息
- UserDetails
ud = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - String
name = ud.getUsername(); - String
pwd = ud.getPassword(); - GrantedAuthority[]
ga = ud.getAuthorities(); - System.out.println(name
+ "," + pwd); - for(GrantedAuthority
g : ga){ - System.out.println(g.getAuthority());
- }
- 自定义访问拒绝页面
- <http
auto-config='true' access-denied-page="/error.jsp"> - 访问用户以带参形式访问
- 在资源URL地址后加*
- 如
- insert
into t_module values(1,'部门管理','/dept.jsp*'); insert into t_module values(2,'人员管理','/emp.jsp*'); - 自定义用户接口实现
- 由于将sql写在配置文件中只适用于小型系统,而且不灵活,在大型系统或实体关系复杂时需要自定义用户实现的接口。
- 自定义用户实现需要实现2个接口。
- UserDetails:实体类需要实现的接口。
- UserDetailsService:实体管理类需要实现的接口。
- 配置:
- <!--
实体管理类 --> - <beans:bean
id="userManager" class="com.lovo.UserManager"></beans:bean> - <authentication-provider
user-service-ref="userManager"> <!-- <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,enabled from t_account where username=?" authorities-by-username-query="select a.username,r.descn from t_account_role ar join t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/> --> </authentication-provider> 原有的通过固定sql语句的获取方式可以抛弃,改为使用user-service-ref属性注入的bean来实现对用户及用户所 拥有的资源的查找。 - 用户注销
- 注销功能由过滤器org.springframework.security.ui.logout.LogoutFilter负责完成。
- <a
href="${pageContext.request.contextPath}/j_spring_security_logout">注销</a> - 被注销的用户就是当前session中保存的用户,注销后页面自动重定向到default-target-url所指定的页面
- spring
security过滤器体系 - spring
security的一系列功能都是由一串过滤器来完成的。在spring配置文件中配置的<http>标签实际上就是 - 起到默认的过滤器进行声明和配置的作用。
- 管理会话
- 当项目中要求不能使用同一个账号同时登陆,按以下步骤实施
- 1.web.xml添加1个监听器
- <listener>
<listener-class>org.springframework.security.ui.session.HttpSessionEventPublishe r</listener-class> </listener> 2.spring配置文件中的<http>标签添加子标签<concurrent-session-control/> <http auto-config='true' access-denied-page="/noview.jsp"> <form-login login-page="/login.jsp" authentication-failure-url="/error.jsp" default-target-url="/index.jsp"/> - <concurrent-session-control/>
- </http>
- <concurrent-session-control/>会产生1个
org.springframework.security.concurrent.ConcurrentSessionFilter - 并放在过滤器链的最前面。
- 默认情况下,使用同一账号后登陆的用户会踢出先登陆的用户。
- 如果想禁止第2个用户登录,则设置
- <concurrent-session-control
exception-if-maximum-exceeded="true"/> - 对方法级的权限控制
- 1.添加依赖包
cglib-nodep-2.1_3.jar aspectjweaver.jar aspectjrt.jar - 2.利用<global-method-security>设置需要保护的方法及可以调用的权限
- <global-method-security>
- <protect-pointcut
access="ROLE_ADMIN,ROLE_USER" expression="execution(* com.lovo.bo.AccountBo.get*(..))"/> - <protect-pointcut
access="ROLE_ADMIN" expression="execution(* com.lovo.bo.AccountBo.create*(..))"/> - </global-method-security>
- 利用注解同样可以实现方法级的保护
- 需要spring-security-core-tiger.jar包
- 启用注解保护
- <global-method-security
secured-annotations="enabled"/> - @Secured({"ROLE_ADMIN","ROLE_USER"})
- public
void getOneAccount(); - 拥护ROLE_ADMIN或ROLE_USER权限的用户可以调用该方法
- SecurityContext安全上下文
- SecurityContext
securityContext = SecurityContextHolder.getContext(); - SecurityContext中保存着实现了Authentication
接口的对象,如果用户尚未通过 - 认证,那么SecurityContext.getAuthenticaiton()方法就会返回null。
注意,如果使用了匿名用户,SecurityContext.getAuthenticaiton()返回的不是null. 只有在未启用过滤器链的情况下,SecurityContext.getAuthenticaiton()才返回空。 验证管理器 验证管理器用来识别用户的身份。使用命名空间会自动注册一个验证管理器的bean. 是org.springframework.security.providers.ProviderManager类的一个对象。 如果要在其他的bean中要引用这个验证管理器,则给这个验证管理器取一个别名。 <authentication-manager alias="authenticationManager"/> 如果要采用其他的类来完成验证管理器的功能,可以使用以下标签 <beans:bean id="abc" class="org.springframework.security.providers.dao.DaoAuthenticationProvide r"> <custom-authentication-provider/> </beans:bean> 访问决策管理器 当使用命名空间配置时,默认的AccessDecisionManager实例会自动注册。 默认的策略是使用一个AffirmativeBased作为AccessDecisionManager(访问决策管理器),投票者是RoleVoter 和 - AuthenticatedVote
- <global-method-security
secured-annotations="enabled" access-decision-manager-ref="accessDecisionManager"> - </global-method-security>
- 利用访问决策管理器对方法进行保护
- <beans:bean
id="filterSecurityIntercepto r" class="org.springframework.security.intercept.web.FilterSecurityIntercepto r" autowire="byType"> <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/> <beans:property name="objectDefinitionSource" ref="moduleFilter" /> <beans:property name="accessDecisionManager" ref="accessDecisionManager"></beans:property> </beans:bean> 向过滤器注入访问决策管理器 不管是MethodSecurityIntercepto r还是FilterSecurityIntercepto r都使用 authenticationManager和accessDecisionManager属性用于验证用户,并且都是通过使用 objectDefinitionSource属性来定义受保护的资源。不同的是过滤器安全拦截器将URL资源与权限关联,而方法安全拦截器将业务方法与权限关联。 objectDefinitionSource属性需要注入的就是 org.springframework.security.intercept.ObjectDefinitionSource这个接口的实现类。 该接口中有一个方法是: ConfigAttributeDefinitio n getAttributes(Object object) throws IllegalArgumentException ; 参数实际类型是org.springframework.security.intercept.web.FilterInvocation 这个方法用于获取保护资源对应的权限信息,返回一个ConfigAttributeDefinitio n对象。 ConfigAttributeDefinitio n对象内部维护1个列表,安全拦截器就通过调用getAttributes方法来获取 ConfigAttributeDefinitio n对象,并将该对象和当前用户拥有的Authentication对象传递给 accessDecisionManager(访问决策管理器) 访问决策管理器在将其传递给具体实现类维护的投票者,这些投票者从ConfigAttributeDefinitio n对象中获取这个存放了访问保护资源需要的权限信息的列表,然后遍历这个列表并与 Authentication对象中GrantedAuthority[]数据中的用户权限信息进行匹配,如果匹配成功,投票者就会投赞成票,否则就投反对票,最后访问决策管理器来统计这些投票决定用户是否能访问该资源。 FilterInvocationDefiniti onSource接口和MethodDefinitionSource接口继承自 ObjectDefinitionSource接口,并提供了2个默认实现类用以从配置文件读取权限信息。 是DefaultFilterInvocationD efinitionSource和 DelegatingMethodDefiniti onSource两个类,如果需要从其他数据来源读取则需要实现 FilterInvocationDefiniti onSource接口和MethodDefinitionSource接口。 自定义的过滤器必须注入 objectDefinitionSource,accessDecisionManager,authenticationManager3个属性。 FilterInvocationDefiniti onSource接口getAttributes实现思路 1.获取客户端访问的url地址。 2.将该url地址和数据库中存储的url地址匹配,找到所有有权访问该地址的权限(角色)名字。 3.把所有的权限名利用ConfigAttributeEditor类封装成ConfigAttributeDefinitio n对象 - 简单例子:
- public
class MyFilter extends HibernateDaoSupport implements FilterInvocationDefiniti onSource,FactoryBean{ - //保存权限信息(能够访问当前资源的角色名)
- private
List<String> roleNameList = new ArrayList<String>(); - public
ConfigAttributeDefinitio n getAttributes(Object object) throws IllegalArgumentException { - FilterInvocation
fi = (FilterInvocation)object; - String
requeatUrl = fi.getRequestUrl(); //获取客户端访问的url地址 - requeatUrl
= requeatUrl.toLowerCase(); //将url地址全部转换成小写 - if(requeatUrl.indexOf("?")
!= -1){ //过滤请求参数 - requeatUrl
= requeatUrl.substring(0,requeatUrl.indexOf("?")); - }
- //在数据库中查找能够访问此url地址的角色
- List<Module>
list = this.getHibernateTemplate().find("from com.lovo.po.Module where url like ?",requeatUrl+"%"); - if(list.size()
<= 0){ - return
null; - }
- String
s = ""; - Module
module = (Module)list.get(0); - Set<Role>
set = module.getRoleSet(); - roleNameList.clear();
- for(Role
role : set){ - roleNameList.add(role.getDescn());
- s
+= role.getDescn() + ","; - }
- s
= s.substring(0,(s.length() - 1)); //将所有的角色名(ROLE_开头)拼接为一个字符串。 - ConfigAttributeEditor
editer = new ConfigAttributeEditor(); - editer.setAsText(s);
- return
(ConfigAttributeDefinitio n) editer.getValue(); //将包含所有角色名(ROLE_开头)的字符串转换为ConfigAttributeDefinitio n对象。 - }
- public
Collection getConfigAttributeDefini tions() { return Collections.unmodifiableCollection(this.roleNameList); - }
- public
boolean supports(Class clazz) { - return
FilterInvocation.class.isAssignableFrom(clazz); - }
- public
Object getObject() throws Exception { - return
this; - }
- public
Class getObjectType() { - return
FilterInvocationDefiniti onSource.class; - }
- public
boolean isSingleton() { - //
TODO Auto-generated method stub - return
true; - }
- }
- 方法保护的原理是通过aop来实现的。
- <aop:config>
- <aop:pointcut
expression="execution(* com.lovo.bo.face.*.*(..))" id="me"/> - <aop:advisor
advice-ref="methodSecurityIntercepto r" pointcut-ref="me"/> - </aop:config>
- 通知是1个实现了MethodSecurityIntercepto
r接口的类 - methodSecurityIntercepto
r是spring中的一个类 - <beans:bean
id="methodSecurityIntercepto r" class="org.springframework.security.intercept.method.aopalliance.MethodSecurityIntercepto r"> <beans:property name="objectDefinitionSource" ref="methodFilter" /> <beans:property name="accessDecisionManager" ref="accessDecisionManager"></beans:property> <beans:property name="authenticationManager" ref="authenticationManager"></beans:property> - </beans:bean>
- accessDecisionManager属性注入访问决策管理器,authenticationManager属性注入验证管理器。
- objectDefinitionSource属性注入1个实现了MethodDefinitionSource接口的类。
- <beans:bean
id="methodFilter" class="com.lovo.method.MethodFilter"> - <beans:property
name="hibernateTemplate" ref="hibernateTemplate"></beans:property> - </beans:bean>
这个类的作用是根据用户调用的方法,找到相应的角色, - public
class MethodFilter extends HibernateDaoSupport implements MethodDefinitionSource{ - private
List<String> roleList = new ArrayList<String>(); - public
ConfigAttributeDefinitio n getAttributes(Method method, Class targetClass) { - return
null; - }
- public
ConfigAttributeDefinitio n getAttributes(Object object) - throws
IllegalArgumentException { - ConfigAttributeEditor
editor = new ConfigAttributeEditor(); - String
s = ""; - ReflectiveMethodInvocati
on rmi = (ReflectiveMethodInvocati on)object; - String
methodName = rmi.getThis().getClass().getName() + "." + rmi.getMethod().getName(); - System.out.println(methodName);
- List<Module>
list = this.getHibernateTemplate().find("from com.lovo.po.Module where url like ?",methodName); - if(list.size()
== 0){ - return
null; - }
- roleList.clear();
- for(int
i=0;i<list.size();i++){ - Module
module = list.get(i); - Set<Role>
roleSet = module.getRoleSet(); - Iterator<Role>
it = roleSet.iterator(); - while(it.hasNext()){
- Role
role = it.next(); - s
+= role.getDescn() + ","; - roleList.add(role.getDescn());
- }
- }
- s
= s.substring(0,(s.length() - 1)); - editor.setAsText(s);
- editor.getValue();
- return
(ConfigAttributeDefinitio n) editor.getValue(); - }
- public
Collection getConfigAttributeDefini tions() { - return
roleList; - }
- public
boolean supports(Class clazz) { - return
true; - }
- }