spring security总结
? ? ? ? <intercept-url pattern="/**" access="ROLE_USER" />?? ? </http>?? ? 注意intercept-url的先后顺序,spring security使用第一个能匹配的intercept-url标签进行权限控制。?四.使用数据库获取用户权限?? <!-- 数据源 -->?? ? <beans:bean id="dataSource" value="com.mysql.jdbc.Driver"></beans:property>?<beans:property name="url" value="jdbc:mysql://localhost:3306/sp"></beans:property>?<beans:property name="username" value="root"></beans:property>?<beans:property name="password" value="root"></beans:property>?? ? </beans:bean>?? ? <!-- 定义用户的权限根据注入的数据源获得 -->?? ? <authentication-provider>?? ? ? ? <jdbc-user-service data-source-ref="dataSource"/>?? ? </authentication-provider>?
定义权限管理模块的表结构(mysql数据库)?alter table `t_account_role` drop foreign key `FK1C2BC93384B0A30E`;?alter table `t_account_role` drop foreign key `FK1C2BC9332D31C656`;?drop table if exists `t_account_role`;?drop table if exists `t_account`;?drop table if exists `t_role`;?/* 用户表 */?CREATE TABLE `t_account` (?? `id` int(11) NOT NULL,?? `username` varchar(255) default NULL,?? `password` varchar(255) default NULL,?? `enabled` int default NULL, /* 用户是否禁用 0:禁用 非0:可用*/?? 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, /* 角色在spring配置文件中的名字 如ROLE_ADMIN,ROLE_USER*/?? 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="/**" access="ROLE_USER" />?? ? ? ? <form-login login-page="/login.jsp"?? ? ? ? ? ? ? ? ? ? authentication-failure-url="/error.jsp"?? ? ? ? ? ? ? ? ? ? default-target-url="/index.jsp" />?? ? </http> ? ?? ??? ? <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>让没有登录的用户也可以访问登录页面?? ? <form-login login-page="/login.jsp"?? ? ? ? ? ? ? ? ? ? authentication-failure-url="/error.jsp"?? ? ? ? ? ? ? ? ? ? default-target-url="/index.jsp" />?? ? login-page:当用户登录时显示自定义登录页面?? ? authentication-failure-url:登录失败时跳转到哪个页面?? ? default-target-url:登录成功后跳转到哪个页面?? ??? ? 注意:users-by-username-query指定的查询,必须至少按顺序返回3列,列名必须是username,password,enabled?? ? ? ? ? authorities-by-username-query指定的查询,必须至少按顺序返回2列,第一列列名必须是username?? ? ? ? ? 第2列必须是权限的名字,与<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />中的?? ? ? ? ? access匹配。?? ? ? ? ? 不能使用select *?? ? 完成的配置文件:?? ? <?xml version="1.0" encoding="UTF-8"?>?<beans:beans xmlns="http://www.springframework.org/schema/security"?? ? xmlns:beans="http://www.springframework.org/schema/beans"?? ? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?? ? xsi:schemaLocation="http://www.springframework.org/schema/beans?? ? http://www.springframework.org/schema/beans/spring-beans-2.0.xsd?? ? http://www.springframework.org/schema/security?? ? http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">?? ??? ? <!-- 数据源 -->?? ? <beans:bean id="dataSource" value="com.mysql.jdbc.Driver"></beans:property>?<beans:property name="url" value="jdbc:mysql://localhost:3306/sp"></beans:property>?<beans:property name="username" value="root"></beans:property>?<beans:property name="password" value="root"></beans:property>?? ? </beans:bean>?
? ? <http auto-config='true'>?? ? <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>?? ? ? ? <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />?? ? ? ? <intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />?? ? ? ? <form-login login-page="/login.jsp"?? ? ? ? ? ? ? ? ? ? authentication-failure-url="/error.jsp"?? ? ? ? ? ? ? ? ? ? default-target-url="/index.jsp" />?? ? </http>?
? ? <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 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>?
</beans:beans>?
登录页面?<form action="${pageContext.request.contextPath}/j_spring_security_check" method="post">?? ? ? ? 用户: <input type="text" name="j_username" value="${SPRING_SECURITY_LAST_USERNAME}"/><br />?? ? ? ? 密码: <input type="password" name="j_password"/><br />?? ? ? ? <input type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆<br />?? ? ? ? <input type="submit" value="登陆"/><input type="reset" value="重置"/>?? </form>?? 页面中输入控件的name属性和form的action地址必须符合spring security的规定?????登录失败页面?? ${SPRING_SECURITY_LAST_EXCEPTION.message} ? ?获取spring生成的异常???将资源信息放入数据库中?表结构?/* 用户表 */?CREATE TABLE `t_account` (?? `id` int(11) NOT NULL,?? `username` varchar(255) default NULL,?? `password` varchar(255) default NULL,?? `enabled` int default NULL, /* 用户是否禁用 0:禁用 非0:可用*/?? 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, /* 角色在spring配置文件中的名字 如ROLE_ADMIN,ROLE_USER*/?? 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.ConfigAttributeDefinition;?import org.springframework.security.ConfigAttributeEditor;?import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;?import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;?import org.springframework.security.intercept.web.RequestKey;?import org.springframework.security.util.AntUrlPathMatcher;?import org.springframework.security.util.UrlMatcher;?
public class JdbcFilterInvocationDefinitionSourceFactoryBean?? ? extends JdbcDaoSupport implements FactoryBean {?? ? private String resourceQuery;?
? ? public boolean isSingleton() {?? ? ? ? return true;?? ? }?
? ? public Class getObjectType() {?? ? ? ? return FilterInvocationDefinitionSource.class;?? ? }?
? ? public Object getObject() {?? ? ? ? return new DefaultFilterInvocationDefinitionSource(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, ConfigAttributeDefinition> buildRequestMap() {?? ? ? ? LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;?? ? ? ? requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();?
? ? ? ? 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,?? ? ? ? ? ? ? ? (ConfigAttributeDefinition) 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="filterInvocationDefinitionSource"?? ? ? ? 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="filterSecurityInterceptor"?? ? ? ? autowire="byType">?? ? ? ? <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>?? ? ? ? <beans:property name="objectDefinitionSource" ref="filterInvocationDefinitionSource" />?? ? </beans:bean>?? ? 注意:FilterSecurityInterceptor过滤器会向request中写入一个标记,用于标记是否已经控制了当前请求,以避免对同一请求多次处理,导致第2个FilterSecurityInterceptor不会再次执行。?? ? 在<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="filterInvocationDefinitionSource"?? ? ? ? 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 FilterInvocationDefinitionSource.class;?? ? }?
? ? public Object getObject() {?? ? ? ? return new DefaultFilterInvocationDefinitionSource(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, ConfigAttributeDefinition> buildRequestMap() {?? ? ? ? LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;?? ? ? ? requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();?
? ? ? ? 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,?? ? ? ? ? ? ? ? (ConfigAttributeDefinition) 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" ref="hibernateTemplate">?? ? ? ? ?</beans:property>?? ? </beans:bean>?? ??? ? 问题:系统只会在初始化的时候从数据库中加载信息。无法识别数据库中信息的改变。?? ? 解决:每个jsp页面上重新内存?? ? 代码:每个jsp页面include如下代码:?<%@page import="org.springframework.context.ApplicationContext"%>?<%@page import="org.springframework.web.context.support.WebApplicationContextUtils"%>?<%@page import="org.springframework.beans.factory.FactoryBean"%>?<%@page import="org.springframework.security.intercept.web.FilterSecurityInterceptor"%>?<%@page import="org.springframework.security.intercept.web.FilterInvocationDefinitionSource"%>?<%?? ? ApplicationContext ctx = ?WebApplicationContextUtils.getWebApplicationContext(application);?? ? FactoryBean factoryBean = (FactoryBean) ctx.getBean("&自定义过滤器的id");?? ? FilterInvocationDefinitionSource fids = (FilterInvocationDefinitionSource) factoryBean.getObject();?? ? FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterceptor");?? ? filter.setObjectDefinitionSource(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" ref="cacheManager"></beans:property>?<beans:property name="cacheName" value="userCache"></beans:property>?</beans:bean>?<beans:bean id="userCache" 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" 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" access-decision-manager-ref="accessDecisionManager">?</global-method-security>?利用访问决策管理器对方法进行保护?
<beans:bean id="filterSecurityInterceptor" autowire="byType">?? ? ? ? <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>?? ? ? ? <beans:property name="objectDefinitionSource" ref="moduleFilter" />?? ? ? ? <beans:property name="accessDecisionManager" ref="accessDecisionManager"></beans:property>?? ? </beans:bean>?? 向过滤器注入访问决策管理器?????? 不管是MethodSecurityInterceptor还是FilterSecurityInterceptor都使用 authenticationManager和accessDecisionManager属性用于验证用户,并且都是通过使用 objectDefinitionSource属性来定义受保护的资源。不同的是过滤器安全拦截器将URL资源与权限关联,而方法安全拦截器将业务方法与权限关联。?? objectDefinitionSource属性需要注入的就是org.springframework.security.intercept.ObjectDefinitionSource这个接口的实现类。?? 该接口中有一个方法是:?? ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException;?? 参数实际类型是org.springframework.security.intercept.web.FilterInvocation?? 这个方法用于获取保护资源对应的权限信息,返回一个ConfigAttributeDefinition对象。?? ConfigAttributeDefinition对象内部维护1个列表,安全拦截器就通过调用getAttributes方法来获取ConfigAttributeDefinition对象,并将该对象和当前用户拥有的Authentication对象传递给accessDecisionManager(访问决策管理器)?? 访问决策管理器在将其传递给具体实现类维护的投票者,这些投票者从ConfigAttributeDefinition对象中获取这个存放了访问保护资源需要的权限信息的列表,然后遍历这个列表并与 Authentication对象中GrantedAuthority[]数据中的用户权限信息进行匹配,如果匹配成功,投票者就会投赞成票,否则就投反对票,最后访问决策管理器来统计这些投票决定用户是否能访问该资源。???? FilterInvocationDefinitionSource接口和MethodDefinitionSource接口继承自ObjectDefinitionSource接口,并提供了2个默认实现类用以从配置文件读取权限信息。?? 是DefaultFilterInvocationDefinitionSource和DelegatingMethodDefinitionSource两个类,如果需要从其他数据来源读取则需要实现FilterInvocationDefinitionSource接口和MethodDefinitionSource接口。???? ?自定义的过滤器必须注入objectDefinitionSource,accessDecisionManager,authenticationManager3个属性。???? FilterInvocationDefinitionSource接口getAttributes实现思路?? ? ? 1.获取客户端访问的url地址。?? ? ? 2.将该url地址和数据库中存储的url地址匹配,找到所有有权访问该地址的权限(角色)名字。?? ? ? 3.把所有的权限名利用ConfigAttributeEditor类封装成ConfigAttributeDefinition对象?
简单例子:?
public class MyFilter extends HibernateDaoSupport implements FilterInvocationDefinitionSource,FactoryBean{?//保存权限信息(能够访问当前资源的角色名)?private List<String> roleNameList = new ArrayList<String>();?
public ConfigAttributeDefinition 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 (ConfigAttributeDefinition) editer.getValue(); ?//将包含所有角色名(ROLE_开头)的字符串转换为ConfigAttributeDefinition对象。?
}?
public Collection getConfigAttributeDefinitions() {?? ? ? ? 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 FilterInvocationDefinitionSource.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="methodSecurityInterceptor" pointcut-ref="me"/> ? ?</aop:config>?通知是1个实现了MethodSecurityInterceptor接口的类?methodSecurityInterceptor是spring中的一个类?<beans:bean id="methodSecurityInterceptor" 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" ref="hibernateTemplate"></beans:property>?</beans:bean>?? 这个类的作用是根据用户调用的方法,找到相应的角色,?public class MethodFilter extends HibernateDaoSupport implements MethodDefinitionSource{?
private List<String> roleList = new ArrayList<String>();?public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {?return null;?}?
public ConfigAttributeDefinition getAttributes(Object object)?throws IllegalArgumentException {?
ConfigAttributeEditor editor = new ConfigAttributeEditor();?String s = "";?ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation)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 (ConfigAttributeDefinition) editor.getValue();?}?
public Collection getConfigAttributeDefinitions() {?
return roleList;?}?
public boolean supports(Class clazz) {?return true;?}?
}?相应的方法名保存在数据库中。