没有合适的资源?快使用搜索试试~ 我知道了~
首页Spring Security教程:权限管理与Web保护详解
收藏
Spring Security 是一个强大的开源框架,用于在 Java EE 和 Spring 框架的应用程序中实现安全权限管理和身份验证功能。本文档是一份详细的 Spring Security 安全权限管理手册,版本为 0.1.0,由 Lingo 在 2009 年发布。它涵盖了从基础入门到高级应用的多个主题,包括但不限于:
1. **基础篇**
- **HelloWorld示例**:介绍了如何配置基本的 Spring Security 配置,如设置过滤器和使用命名空间来实现简单的安全性。
- **数据库用户权限管理**:指导如何通过数据库存储和管理用户的权限信息,涉及配置文件修改、数据库表结构设计以及用户登录和权限验证。
- **自定义登录界面**:演示如何定制登录页面,包括前端实现、配置文件调整和参数设置。
2. **Web保护篇**
- **过滤器详解**:深入剖析了各种关键过滤器的作用,如 HttpSessionContextIntegrationFilter、LogoutFilter 等,它们在处理会话管理、认证流程、匿名访问和异常处理等方面起着核心作用。
- **管理会话**:讲解了如何通过监听器和过滤器来管理会话,确保安全性和性能优化。
3. **高级特性**
- **密码加密**:介绍了 MD5 和盐值加密技术,确保用户密码的安全存储。
- **用户信息管理**:涵盖用户信息缓存和获取当前用户信息的方法。
- **访问拒绝与自定义页面**:如何创建自定义的访问拒绝页面,以及动态管理资源结合自定义登录页面的实现。
这份文档对于学习和实施 Spring Security 在 Web 应用中的安全策略非常有帮助,适合有一定 Spring 和 Java 开发经验的开发者参考。虽然文档可能有些过时,但由于 Spring Security 的核心概念和架构相对稳定,许多内容依然具有参考价值。如果你正在寻找关于 Spring Security 的最新指南,建议查阅官方文档或更新的教程以获取最新最佳实践。

第
4
章
自定义登陆页面
Spring Security
虽然默认提供了一个登陆页面,但是这个页面实在太简陋了,只有在快速演示时才有可能它做系统的登陆页面,实际开发时无论是
从美观还是实用性角度考虑,我们都必须实现自定义的登录页面。
4.1.
实现自定义登陆页面
自己实现一个
login.jsp
,放在
src/main/webapp/
目录下。
+ ch004/
+ src/
+ main/
+ resources/
* applicationContext.xml
+ webapp/
+ WEB-INF/
* web.xml
* admin.jsp
* index.jsp
* login.jsp
+ test/
+ resources/
* pom.xml
4.2.
修改配置文件
在
xml
中的
http
标签中添加一个
form-login
标签。
<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="/login.jsp?error=true"
default-target-url="/" />
</http>
4.3.
登陆页面中的参数配置
以下是我们创建的
login.jsp
页面的主要代码。
<div class="error ${param.error == true ? '' : 'hide'}">
登陆失败
<br>
${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}
</div>
<form action="${pageContext.request.contextPath}
/j_spring_security_check
" style="width:260px;text-a
<fieldset>
<legend>
登陆
</legend>
用户:
<input type="text" name="
j_username
" style="width:150px;" value="${sessionScope['SPRING_SE
密码:
<input type="password" name="
j_password
" style="width:150px;" /><br />
<input type="checkbox" name="
_spring_security_remember_me
" />
两周之内不必登陆
<br />
<input type="submit" value="
登陆
"/>
<input type="reset" value="
重置
"/>
</fieldset>
</form>
以上介绍了自定义页面上
Spring Security
所需的基本元素,这些参数名称都采用了
Spring Security
中默认的配置值,如果有特殊需要还可以通过配置
文件进行修改。
4.4.
测试一下
经过以上配置,我们终于使用了一个自己创建的登陆页面替换了原来
Spring Security
默认提供的登录页面了。我们不仅仅是做个样子,而是实际配
置了各个
Spring Security
所需的参数,真正将自定义登陆页面与
Spring Security
紧紧的整合在了一起。以下是使用自定义登陆页面实际运行时的截
图。
图
4.1.
进入登录页面
图
4.2.
用户登陆失败
实例见
ch004
。
[2]
有关匿名用户的知识,我们会在之后的章节中进行讲解。
[3]
登陆成功后跳转策略的知识,我们会在之后的章节中进行讲解。
[4]
关于绝对路径和相对路径的详细讨论,请参考
http://family168.com/tutorial/jsp/html/jsp-ch-03.html#jsp-ch-03-04-01
第
4
章
自定义登陆页面
上一页
部分
I.
基础篇
下一页
让没登陆的用户也可以访问
login.jsp
。
[
2
]
这是因为配置文件中的
“
/**
”
配置,要求用户访问任意一个系统资源时,必须拥有
ROLE_USER
角色,
/login.jsp
也不例外,如果我们不
为
/login.jsp
单独配置访问权限,会造成用户连登陆的权限都没有,这是不正确的。
login-page
表示用户登陆时显示我们自定义的
login.jsp
。
这时我们访问系统显示的登陆页面将是我们上面创建的
login.jsp
。
authentication-failure-url
表示用户登陆失败时,跳转到哪个页面。
当用户输入的登录名和密码不正确时,系统将再次跳转到
/login.jsp
,并添加一个
error=true
参数作为登陆失败的标示。
default-target-url
表示登陆成功时,跳转到哪个页面。
[
3
]
/j_spring_security_check
,提交登陆信息的
URL
地址。
自定义
form
时,要把
form
的
action
设置为
/j_spring_security_check
。注意这里要使用绝对路径,避免登陆页面存放的页面可能带来的问题。
[
4
]
j_username
,输入登陆名的参数名称。
j_password
,输入密码的参数名称
_spring_security_remember_me
,选择是否允许自动登录的参数名称。
可以直接把这个参数设置为一个
checkbox
,无需设置
value
,
Spring Security
会自行判断它是否被选中。
上一页
上一级
下一页
第
3
章
自定义数据库表结构
起始页
第
5
章
使用数据库管理资源
Saved with website2PDF - www.website2pdf.com

第
4
章
自定义登陆页面
Spring Security
虽然默认提供了一个登陆页面,但是这个页面实在太简陋了,只有在快速演示时才有可能它做系统的登陆页面,实际开发时无论是
从美观还是实用性角度考虑,我们都必须实现自定义的登录页面。
4.1.
实现自定义登陆页面
自己实现一个
login.jsp
,放在
src/main/webapp/
目录下。
+ ch004/
+ src/
+ main/
+ resources/
* applicationContext.xml
+ webapp/
+ WEB-INF/
* web.xml
* admin.jsp
* index.jsp
* login.jsp
+ test/
+ resources/
* pom.xml
4.2.
修改配置文件
在
xml
中的
http
标签中添加一个
form-login
标签。
<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="/login.jsp?error=true"
default-target-url="/" />
</http>
4.3.
登陆页面中的参数配置
以下是我们创建的
login.jsp
页面的主要代码。
<div class="error ${param.error == true ? '' : 'hide'}">
登陆失败
<br>
${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}
</div>
<form action="${pageContext.request.contextPath}
/j_spring_security_check
" style="width:260px;text-a
<fieldset>
<legend>
登陆
</legend>
用户:
<input type="text" name="
j_username
" style="width:150px;" value="${sessionScope['SPRING_SE
密码:
<input type="password" name="
j_password
" style="width:150px;" /><br />
<input type="checkbox" name="
_spring_security_remember_me
" />
两周之内不必登陆
<br />
<input type="submit" value="
登陆
"/>
<input type="reset" value="
重置
"/>
</fieldset>
</form>
以上介绍了自定义页面上
Spring Security
所需的基本元素,这些参数名称都采用了
Spring Security
中默认的配置值,如果有特殊需要还可以通过配置
文件进行修改。
4.4.
测试一下
经过以上配置,我们终于使用了一个自己创建的登陆页面替换了原来
Spring Security
默认提供的登录页面了。我们不仅仅是做个样子,而是实际配
置了各个
Spring Security
所需的参数,真正将自定义登陆页面与
Spring Security
紧紧的整合在了一起。以下是使用自定义登陆页面实际运行时的截
图。
图
4.1.
进入登录页面
图
4.2.
用户登陆失败
实例见
ch004
。
[2]
有关匿名用户的知识,我们会在之后的章节中进行讲解。
[3]
登陆成功后跳转策略的知识,我们会在之后的章节中进行讲解。
[4]
关于绝对路径和相对路径的详细讨论,请参考
http://family168.com/tutorial/jsp/html/jsp-ch-03.html#jsp-ch-03-04-01
第
4
章
自定义登陆页面
上一页
部分
I.
基础篇
下一页
让没登陆的用户也可以访问
login.jsp
。
[
2
]
这是因为配置文件中的
“
/**
”
配置,要求用户访问任意一个系统资源时,必须拥有
ROLE_USER
角色,
/login.jsp
也不例外,如果我们不
为
/login.jsp
单独配置访问权限,会造成用户连登陆的权限都没有,这是不正确的。
login-page
表示用户登陆时显示我们自定义的
login.jsp
。
这时我们访问系统显示的登陆页面将是我们上面创建的
login.jsp
。
authentication-failure-url
表示用户登陆失败时,跳转到哪个页面。
当用户输入的登录名和密码不正确时,系统将再次跳转到
/login.jsp
,并添加一个
error=true
参数作为登陆失败的标示。
default-target-url
表示登陆成功时,跳转到哪个页面。
[
3
]
/j_spring_security_check
,提交登陆信息的
URL
地址。
自定义
form
时,要把
form
的
action
设置为
/j_spring_security_check
。注意这里要使用绝对路径,避免登陆页面存放的页面可能带来的问题。
[
4
]
j_username
,输入登陆名的参数名称。
j_password
,输入密码的参数名称
_spring_security_remember_me
,选择是否允许自动登录的参数名称。
可以直接把这个参数设置为一个
checkbox
,无需设置
value
,
Spring Security
会自行判断它是否被选中。
上一页
上一级
下一页
第
3
章
自定义数据库表结构
起始页
第
5
章
使用数据库管理资源
Saved with website2PDF - www.website2pdf.com

第
5
章
使用数据库管理资源
国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上
Spring Security
并没有给出官方的解决方案,为此我们
需要对
Spring Security
进行扩展。
5.1.
数据库表结构
这次我们使用五张表,
user
用户表,
role
角色表,
resc
资源表相互独立,它们通过各自之间的连接表实现多对多关系。
--
资源
create table resc(
id bigint,
name varchar(50),
res_type varchar(50),
res_string varchar(200),
priority integer,
descn varchar(200)
);
alter table resc add constraint pk_resc primary key(id);
alter table resc alter column id bigint generated by default as identity(start with 1);
--
角色
create table role(
id bigint,
name varchar(50),
descn varchar(200)
);
alter table role add constraint pk_role primary key(id);
alter table role alter column id bigint generated by default as identity(start with 1);
--
用户
create table user(
id bigint,
username varchar(50),
password varchar(50),
status integer,
descn varchar(200)
);
alter table user add constraint pk_user primary key(id);
alter table user alter column id bigint generated by default as identity(start with 1);
--
资源角色连接表
create table resc_role(
resc_id bigint,
role_id bigint
);
alter table resc_role add constraint pk_resc_role primary key(resc_id, role_id);
alter table resc_role add constraint fk_resc_role_resc foreign key(resc_id) references resc(id);
alter table resc_role add constraint fk_resc_role_role foreign key(role_id) references role(id);
--
用户角色连接表
create table user_role(
user_id bigint,
role_id bigint
);
alter table user_role add constraint pk_user_role primary key(user_id, role_id);
alter table user_role add constraint fk_user_role_user foreign key(user_id) references user(id);
alter table user_role add constraint fk_user_role_role foreign key(role_id) references role(id);
user
表中包含用户登陆信息,
role
角色表中包含授权信息,
resc
资源表中包含需要保护的资源。
ER
图如下所示:
图
5.1.
数据库表关系
5.2.
初始化数据
创建的两个用户分别对应
“
管理员
”
角色和
“
用户
”
角色。而
“
管理员
”
角色可以访问
“
/admin.jsp
”
和
“
/**
”
,
“
用户
”
角色只能访问
“
/**
”
。
insert into user(id,username,password,status,descn) values(1,'admin','admin',1,'
管理员
');
insert into user(id,username,password,status,descn) values(2,'user','user',1,'
用户
');
insert into role(id,name,descn) values(1,'ROLE_ADMIN','
管理员角色
');
insert into role(id,name,descn) values(2,'ROLE_USER','
用户角色
');
insert into resc(id,name,res_type,res_string,priority,descn) values(1,'','URL','/admin.jsp',1,'');
insert into resc(id,name,res_type,res_string,priority,descn) values(2,'','URL','/**',2,'');
insert into resc_role(resc_id,role_id) values(1,1);
insert into resc_role(resc_id,role_id) values(2,1);
insert into resc_role(resc_id,role_id) values(2,2);
insert into user_role(user_id,role_id) values(1,1);
insert into user_role(user_id,role_id) values(1,2);
insert into user_role(user_id,role_id) values(2,2);
5.3.
实现从数据库中读取资源信息
Spring Security
没有提供从数据库获得获取资源信息的方法,实际上
Spring Security
甚至没有为我们留一个半个的扩展接口,所以我们这次要费点儿
脑筋了。
首先,要搞清楚需要提供何种类型的数据,然后,寻找可以让我们编写的代码替换原有功能的切入点,实现了以上两步之后,就可以宣布大功
告成了。
5.3.1.
需要何种数据格式
从配置文件上可以看到,
Spring Security
所需的数据应该是一系列
URL
网址和访问这些网址所需的权限:
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
Spring Security
所做的就是在系统初始化时,将以上
XML
中的信息转换为特定的数据格式,而框架中其他组件可以利用这些特定格式的数据,用
于控制之后的验证操作。
现在这些资源信息都保存在数据库中,我们可以使用上面介绍的
SQL
语句从数据中查询。
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by re.priority
下面要开始编写实现代码了。
1.
搜索数据库获得资源信息。
我们通过定义一个
MappingSqlQuery
实现数据库操作。
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;
}
}
这样我们可以执行它的
execute()
方法获得所有资源信息。
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;
}
2.
使用获得的资源信息组装
requestMap
。
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;
}
3.
使用
urlMatcher
和
requestMap
创建
DefaultFilterInvocationDefinitionSource
。
public Object getObject() {
return new DefaultFilterInvocationDefinitionSource(this
.getUrlMatcher(), this.buildRequestMap());
}
这样我们就获得了
DefaultFilterInvocationDefinitionSource
,剩下的只差把这个我们自己创建的类替换掉原有的代码了。
完整代码如下所示:
package com.family168.springsecuritybook.ch005;
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;
}
}
}
5.3.2.
替换原有功能的切入点
在
spring
中配置我们编写的代码。
<beans:bean id="filterInvocationDefinitionSource"
class="com.family168.springsecuritybook.ch005.JdbcFilterInvocationDefinitionSourceFactoryBean">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="resourceQuery" value="
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by priority
"/>
</beans:bean>
下一步使用这个
filterInvocationDefinitionSource
创建
filterSecurityInterceptor
,并使用它替换系统原来创建的那个过滤器。
<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>
注意这个
custom-filter
标签,它表示将
filterSecurityInterceptor
放在框架原来的
FILTER_SECURITY_INTERCEPTOR
过滤器之前,这样我们的过滤器会
先于原来的过滤器执行,因为它的功能与老过滤器完全一样,所以这就等于把原来的过滤器替换掉了。
完整的配置文件如下所示:
<?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">
<http auto-config="true"/>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password,status as enabled
from user
where username=?"
authorities-by-username-query="select u.username,r.name as authority
from user u
join user_role ur
on u.id=ur.user_id
join role r
on r.id=ur.role_id
where u.username=?"/>
</authentication-provider>
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor" autowire="byTyp
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" />
<beans:property name="objectDefinitionSource" ref="filterInvocationDefinitionSource" />
</beans:bean>
<beans:bean id="filterInvocationDefinitionSource"
class="com.family168.springsecuritybook.ch05.JdbcFilterInvocationDefinitionSourceFactoryBean
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="resourceQuery" value="
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by priority
"/>
</beans:bean>
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<beans:property name="url" value="jdbc:hsqldb:res:/hsqldb/test"/>
<beans:property name="username" value="sa"/>
<beans:property name="password" value=""/>
</beans:bean>
</beans:beans>
实例见
ch05
。
目前存在的问题是,系统会在初始化时一次将所有资源加载到内存中,即使在数据库中修改了资源信息,系统也不会再次去从数据库中读取资
源信息。这就造成了每次修改完数据库后,都需要重启系统才能时资源配置生效。
解决方案是,如果数据库中的资源出现的变化,需要刷新内存中已加载的资源信息时,使用下面代码:
<%@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("&filterInvocationDefinitionSource");
FilterInvocationDefinitionSource fids = (FilterInvocationDefinitionSource) factoryBean.getObject
FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterc
filter.setObjectDefinitionSource(fids);
%>
<jsp:forward page="/"/>
目前还不支持对方法调用和
ACL
资源的动态管理,相关讨论请参考手册后面的部分第
36
章 动态资源管理。
第
5
章
使用数据库管理资源
上一页
部分
I.
基础篇
下一页
上一页
上一级
下一页
第
4
章
自定义登陆页面
起始页
第
6
章
控制用户信息
Saved with website2PDF - www.website2pdf.com

第
5
章
使用数据库管理资源
国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上
Spring Security
并没有给出官方的解决方案,为此我们
需要对
Spring Security
进行扩展。
5.1.
数据库表结构
这次我们使用五张表,
user
用户表,
role
角色表,
resc
资源表相互独立,它们通过各自之间的连接表实现多对多关系。
--
资源
create table resc(
id bigint,
name varchar(50),
res_type varchar(50),
res_string varchar(200),
priority integer,
descn varchar(200)
);
alter table resc add constraint pk_resc primary key(id);
alter table resc alter column id bigint generated by default as identity(start with 1);
--
角色
create table role(
id bigint,
name varchar(50),
descn varchar(200)
);
alter table role add constraint pk_role primary key(id);
alter table role alter column id bigint generated by default as identity(start with 1);
--
用户
create table user(
id bigint,
username varchar(50),
password varchar(50),
status integer,
descn varchar(200)
);
alter table user add constraint pk_user primary key(id);
alter table user alter column id bigint generated by default as identity(start with 1);
--
资源角色连接表
create table resc_role(
resc_id bigint,
role_id bigint
);
alter table resc_role add constraint pk_resc_role primary key(resc_id, role_id);
alter table resc_role add constraint fk_resc_role_resc foreign key(resc_id) references resc(id);
alter table resc_role add constraint fk_resc_role_role foreign key(role_id) references role(id);
--
用户角色连接表
create table user_role(
user_id bigint,
role_id bigint
);
alter table user_role add constraint pk_user_role primary key(user_id, role_id);
alter table user_role add constraint fk_user_role_user foreign key(user_id) references user(id);
alter table user_role add constraint fk_user_role_role foreign key(role_id) references role(id);
user
表中包含用户登陆信息,
role
角色表中包含授权信息,
resc
资源表中包含需要保护的资源。
ER
图如下所示:
图
5.1.
数据库表关系
5.2.
初始化数据
创建的两个用户分别对应
“
管理员
”
角色和
“
用户
”
角色。而
“
管理员
”
角色可以访问
“
/admin.jsp
”
和
“
/**
”
,
“
用户
”
角色只能访问
“
/**
”
。
insert into user(id,username,password,status,descn) values(1,'admin','admin',1,'
管理员
');
insert into user(id,username,password,status,descn) values(2,'user','user',1,'
用户
');
insert into role(id,name,descn) values(1,'ROLE_ADMIN','
管理员角色
');
insert into role(id,name,descn) values(2,'ROLE_USER','
用户角色
');
insert into resc(id,name,res_type,res_string,priority,descn) values(1,'','URL','/admin.jsp',1,'');
insert into resc(id,name,res_type,res_string,priority,descn) values(2,'','URL','/**',2,'');
insert into resc_role(resc_id,role_id) values(1,1);
insert into resc_role(resc_id,role_id) values(2,1);
insert into resc_role(resc_id,role_id) values(2,2);
insert into user_role(user_id,role_id) values(1,1);
insert into user_role(user_id,role_id) values(1,2);
insert into user_role(user_id,role_id) values(2,2);
5.3.
实现从数据库中读取资源信息
Spring Security
没有提供从数据库获得获取资源信息的方法,实际上
Spring Security
甚至没有为我们留一个半个的扩展接口,所以我们这次要费点儿
脑筋了。
首先,要搞清楚需要提供何种类型的数据,然后,寻找可以让我们编写的代码替换原有功能的切入点,实现了以上两步之后,就可以宣布大功
告成了。
5.3.1.
需要何种数据格式
从配置文件上可以看到,
Spring Security
所需的数据应该是一系列
URL
网址和访问这些网址所需的权限:
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
Spring Security
所做的就是在系统初始化时,将以上
XML
中的信息转换为特定的数据格式,而框架中其他组件可以利用这些特定格式的数据,用
于控制之后的验证操作。
现在这些资源信息都保存在数据库中,我们可以使用上面介绍的
SQL
语句从数据中查询。
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by re.priority
下面要开始编写实现代码了。
1.
搜索数据库获得资源信息。
我们通过定义一个
MappingSqlQuery
实现数据库操作。
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;
}
}
这样我们可以执行它的
execute()
方法获得所有资源信息。
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;
}
2.
使用获得的资源信息组装
requestMap
。
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;
}
3.
使用
urlMatcher
和
requestMap
创建
DefaultFilterInvocationDefinitionSource
。
public Object getObject() {
return new DefaultFilterInvocationDefinitionSource(this
.getUrlMatcher(), this.buildRequestMap());
}
这样我们就获得了
DefaultFilterInvocationDefinitionSource
,剩下的只差把这个我们自己创建的类替换掉原有的代码了。
完整代码如下所示:
package com.family168.springsecuritybook.ch005;
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;
}
}
}
5.3.2.
替换原有功能的切入点
在
spring
中配置我们编写的代码。
<beans:bean id="filterInvocationDefinitionSource"
class="com.family168.springsecuritybook.ch005.JdbcFilterInvocationDefinitionSourceFactoryBean">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="resourceQuery" value="
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by priority
"/>
</beans:bean>
下一步使用这个
filterInvocationDefinitionSource
创建
filterSecurityInterceptor
,并使用它替换系统原来创建的那个过滤器。
<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>
注意这个
custom-filter
标签,它表示将
filterSecurityInterceptor
放在框架原来的
FILTER_SECURITY_INTERCEPTOR
过滤器之前,这样我们的过滤器会
先于原来的过滤器执行,因为它的功能与老过滤器完全一样,所以这就等于把原来的过滤器替换掉了。
完整的配置文件如下所示:
<?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">
<http auto-config="true"/>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password,status as enabled
from user
where username=?"
authorities-by-username-query="select u.username,r.name as authority
from user u
join user_role ur
on u.id=ur.user_id
join role r
on r.id=ur.role_id
where u.username=?"/>
</authentication-provider>
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor" autowire="byTyp
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" />
<beans:property name="objectDefinitionSource" ref="filterInvocationDefinitionSource" />
</beans:bean>
<beans:bean id="filterInvocationDefinitionSource"
class="com.family168.springsecuritybook.ch05.JdbcFilterInvocationDefinitionSourceFactoryBean
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="resourceQuery" value="
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by priority
"/>
</beans:bean>
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<beans:property name="url" value="jdbc:hsqldb:res:/hsqldb/test"/>
<beans:property name="username" value="sa"/>
<beans:property name="password" value=""/>
</beans:bean>
</beans:beans>
实例见
ch05
。
目前存在的问题是,系统会在初始化时一次将所有资源加载到内存中,即使在数据库中修改了资源信息,系统也不会再次去从数据库中读取资
源信息。这就造成了每次修改完数据库后,都需要重启系统才能时资源配置生效。
解决方案是,如果数据库中的资源出现的变化,需要刷新内存中已加载的资源信息时,使用下面代码:
<%@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("&filterInvocationDefinitionSource");
FilterInvocationDefinitionSource fids = (FilterInvocationDefinitionSource) factoryBean.getObject
FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterc
filter.setObjectDefinitionSource(fids);
%>
<jsp:forward page="/"/>
目前还不支持对方法调用和
ACL
资源的动态管理,相关讨论请参考手册后面的部分第
36
章 动态资源管理。
第
5
章
使用数据库管理资源
上一页
部分
I.
基础篇
下一页
上一页
上一级
下一页
第
4
章
自定义登陆页面
起始页
第
6
章
控制用户信息
Saved with website2PDF - www.website2pdf.com

第
5
章
使用数据库管理资源
国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上
Spring Security
并没有给出官方的解决方案,为此我们
需要对
Spring Security
进行扩展。
5.1.
数据库表结构
这次我们使用五张表,
user
用户表,
role
角色表,
resc
资源表相互独立,它们通过各自之间的连接表实现多对多关系。
--
资源
create table resc(
id bigint,
name varchar(50),
res_type varchar(50),
res_string varchar(200),
priority integer,
descn varchar(200)
);
alter table resc add constraint pk_resc primary key(id);
alter table resc alter column id bigint generated by default as identity(start with 1);
--
角色
create table role(
id bigint,
name varchar(50),
descn varchar(200)
);
alter table role add constraint pk_role primary key(id);
alter table role alter column id bigint generated by default as identity(start with 1);
--
用户
create table user(
id bigint,
username varchar(50),
password varchar(50),
status integer,
descn varchar(200)
);
alter table user add constraint pk_user primary key(id);
alter table user alter column id bigint generated by default as identity(start with 1);
--
资源角色连接表
create table resc_role(
resc_id bigint,
role_id bigint
);
alter table resc_role add constraint pk_resc_role primary key(resc_id, role_id);
alter table resc_role add constraint fk_resc_role_resc foreign key(resc_id) references resc(id);
alter table resc_role add constraint fk_resc_role_role foreign key(role_id) references role(id);
--
用户角色连接表
create table user_role(
user_id bigint,
role_id bigint
);
alter table user_role add constraint pk_user_role primary key(user_id, role_id);
alter table user_role add constraint fk_user_role_user foreign key(user_id) references user(id);
alter table user_role add constraint fk_user_role_role foreign key(role_id) references role(id);
user
表中包含用户登陆信息,
role
角色表中包含授权信息,
resc
资源表中包含需要保护的资源。
ER
图如下所示:
图
5.1.
数据库表关系
5.2.
初始化数据
创建的两个用户分别对应
“
管理员
”
角色和
“
用户
”
角色。而
“
管理员
”
角色可以访问
“
/admin.jsp
”
和
“
/**
”
,
“
用户
”
角色只能访问
“
/**
”
。
insert into user(id,username,password,status,descn) values(1,'admin','admin',1,'
管理员
');
insert into user(id,username,password,status,descn) values(2,'user','user',1,'
用户
');
insert into role(id,name,descn) values(1,'ROLE_ADMIN','
管理员角色
');
insert into role(id,name,descn) values(2,'ROLE_USER','
用户角色
');
insert into resc(id,name,res_type,res_string,priority,descn) values(1,'','URL','/admin.jsp',1,'');
insert into resc(id,name,res_type,res_string,priority,descn) values(2,'','URL','/**',2,'');
insert into resc_role(resc_id,role_id) values(1,1);
insert into resc_role(resc_id,role_id) values(2,1);
insert into resc_role(resc_id,role_id) values(2,2);
insert into user_role(user_id,role_id) values(1,1);
insert into user_role(user_id,role_id) values(1,2);
insert into user_role(user_id,role_id) values(2,2);
5.3.
实现从数据库中读取资源信息
Spring Security
没有提供从数据库获得获取资源信息的方法,实际上
Spring Security
甚至没有为我们留一个半个的扩展接口,所以我们这次要费点儿
脑筋了。
首先,要搞清楚需要提供何种类型的数据,然后,寻找可以让我们编写的代码替换原有功能的切入点,实现了以上两步之后,就可以宣布大功
告成了。
5.3.1.
需要何种数据格式
从配置文件上可以看到,
Spring Security
所需的数据应该是一系列
URL
网址和访问这些网址所需的权限:
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
Spring Security
所做的就是在系统初始化时,将以上
XML
中的信息转换为特定的数据格式,而框架中其他组件可以利用这些特定格式的数据,用
于控制之后的验证操作。
现在这些资源信息都保存在数据库中,我们可以使用上面介绍的
SQL
语句从数据中查询。
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by re.priority
下面要开始编写实现代码了。
1.
搜索数据库获得资源信息。
我们通过定义一个
MappingSqlQuery
实现数据库操作。
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;
}
}
这样我们可以执行它的
execute()
方法获得所有资源信息。
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;
}
2.
使用获得的资源信息组装
requestMap
。
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;
}
3.
使用
urlMatcher
和
requestMap
创建
DefaultFilterInvocationDefinitionSource
。
public Object getObject() {
return new DefaultFilterInvocationDefinitionSource(this
.getUrlMatcher(), this.buildRequestMap());
}
这样我们就获得了
DefaultFilterInvocationDefinitionSource
,剩下的只差把这个我们自己创建的类替换掉原有的代码了。
完整代码如下所示:
package com.family168.springsecuritybook.ch005;
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;
}
}
}
5.3.2.
替换原有功能的切入点
在
spring
中配置我们编写的代码。
<beans:bean id="filterInvocationDefinitionSource"
class="com.family168.springsecuritybook.ch005.JdbcFilterInvocationDefinitionSourceFactoryBean">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="resourceQuery" value="
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by priority
"/>
</beans:bean>
下一步使用这个
filterInvocationDefinitionSource
创建
filterSecurityInterceptor
,并使用它替换系统原来创建的那个过滤器。
<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>
注意这个
custom-filter
标签,它表示将
filterSecurityInterceptor
放在框架原来的
FILTER_SECURITY_INTERCEPTOR
过滤器之前,这样我们的过滤器会
先于原来的过滤器执行,因为它的功能与老过滤器完全一样,所以这就等于把原来的过滤器替换掉了。
完整的配置文件如下所示:
<?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">
<http auto-config="true"/>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password,status as enabled
from user
where username=?"
authorities-by-username-query="select u.username,r.name as authority
from user u
join user_role ur
on u.id=ur.user_id
join role r
on r.id=ur.role_id
where u.username=?"/>
</authentication-provider>
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor" autowire="byTyp
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" />
<beans:property name="objectDefinitionSource" ref="filterInvocationDefinitionSource" />
</beans:bean>
<beans:bean id="filterInvocationDefinitionSource"
class="com.family168.springsecuritybook.ch05.JdbcFilterInvocationDefinitionSourceFactoryBean
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="resourceQuery" value="
select re.res_string,r.name
from role r
join resc_role rr
on r.id=rr.role_id
join resc re
on re.id=rr.resc_id
order by priority
"/>
</beans:bean>
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<beans:property name="url" value="jdbc:hsqldb:res:/hsqldb/test"/>
<beans:property name="username" value="sa"/>
<beans:property name="password" value=""/>
</beans:bean>
</beans:beans>
实例见
ch05
。
目前存在的问题是,系统会在初始化时一次将所有资源加载到内存中,即使在数据库中修改了资源信息,系统也不会再次去从数据库中读取资
源信息。这就造成了每次修改完数据库后,都需要重启系统才能时资源配置生效。
解决方案是,如果数据库中的资源出现的变化,需要刷新内存中已加载的资源信息时,使用下面代码:
<%@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("&filterInvocationDefinitionSource");
FilterInvocationDefinitionSource fids = (FilterInvocationDefinitionSource) factoryBean.getObject
FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterc
filter.setObjectDefinitionSource(fids);
%>
<jsp:forward page="/"/>
目前还不支持对方法调用和
ACL
资源的动态管理,相关讨论请参考手册后面的部分第
36
章 动态资源管理。
第
5
章
使用数据库管理资源
上一页
部分
I.
基础篇
下一页
上一页
上一级
下一页
第
4
章
自定义登陆页面
起始页
第
6
章
控制用户信息
Saved with website2PDF - www.website2pdf.com
剩余150页未读,继续阅读
相关推荐

135 浏览量






153 浏览量


kkhui
- 粉丝: 0
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助

最新资源
- C语言实现LED灯控制的源码教程及使用说明
- zxingdemo实现高效条形码扫描技术解析
- Android项目实践:RecyclerView与Grid View的高效布局
- .NET分层架构的优势与实战应用
- Unity中实现百度人脸识别登录教程
- 解决ListView和ViewPager及TabHost的触摸冲突
- 轻松实现ASP购物车功能的源码及数据库下载
- 电脑刷新慢的快速解决方法
- Condor Framework: 构建高性能Node.js GRPC服务的Alpha框架
- 社交媒体图像中的抗议与暴力检测模型实现
- Android Support Library v4 安装与配置教程
- Android中文API合集——中文翻译组出品
- 暗组计算机远程管理软件V1.0 - 远程控制与管理工具
- NVIDIA GPU深度学习环境搭建全攻略
- 丰富的人物行走动画素材库
- 高效汉字拼音转换工具TinyPinYin_v2.0.3发布
安全验证
文档复制为VIP权益,开通VIP直接复制
