# 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>springSecurityFilterChain</filter-name>
# <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
# </filter>
# <filter-mapping>
# <filter-name>springSecurityFilterChain</filter-name>
# <url-pattern>/*</url-pattern>
# </filter-mapping>
# 二.在spring配置文件中添加security的命名空间
# <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">
# 三.在spring配置文中中定义需要保护的资源
# <http auto-config='true'>
# <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
# <intercept-url pattern="/**" access="ROLE_USER" />
# </http>
# 注意intercept-url的先后顺序,spring security使用第一个能匹配的intercept-url标签进行权限控制。
# 四.使用数据库获取用户权限
# <!-- 数据源 -->
# <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
# <beans:property name="driverClassName" 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数据库)
## 将资源信息放入数据库中
# 表结构
# /* 用户表 */
# 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);
#
# 当用户登录时,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" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
# <beans:property name="driverClassName" 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生成的异常
#
# 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"
# class="com.lovo.JdbcFilterInvocationDefinitionSourceFactoryBean">
# <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="filterSecurityInterceptor"
# class="org.springframework.security.intercept.web.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"
# class="com.lovo.JdbcFilterInvocationDefinitionSourceFactoryBean">
# <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 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" 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.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);
# %>
#
或者加入
// 刷新SpringSecurity权限信息
private void flushSpringSecurity() {
try {
FactoryBean factoryBean = (FactoryBean)SpringUtil.getBean("&userSecurityDefinitionSource");
FilterInvocationDefinitionSource filterInvocationDefinitionSource = (FilterInvocationDefinitionSource) factoryBean.getObject();
FilterSecurityInterceptor filterSecurityInterceptor = (FilterSecurityInterceptor) SpringUtil.getBean("filterSecurityInterceptor");
filterSecurityInterceptor.setObjectDefinitionSource(filterInvocationDefinitionSource);
} catch (Exception e) {
e.printStackTrace();
}
}