spring security使用数据库资源
国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上Spring Security并没有给出官方的解决方案,为此我们需要对Spring Security进行扩展。、
数据库表结构
这次我们使用五张表,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);
ER图如下表示
图 5.1. 数据库表关系
我们在已有表结构中插入一些数据。
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);
Spring Security没有提供从数据库获得获取资源信息的方法,实际上Spring Security甚至没有为我们留一个半个的扩展接口,所以我们这次要费点儿脑筋了。
首先,要搞清楚需要提供何种类型的数据,然后,寻找可以让我们编写的代码替换原有功能的切入点,实现了以上两步之后,就可以宣布大功告成了。
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" />
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
完整代码
package com.family168.springsecuritybook.ch005; import java.sql.ResultSet;
import java.sql.SQLException; import java.util.Collection;
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.access.ConfigAttribute;
import org.springframework.security.access.ConfigAttributeEditor;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.RequestKey;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher; public class JdbcFilterInvocationDefinitionSourceFactoryBean
extends JdbcDaoSupport implements FactoryBean {
private String resourceQuery; public boolean isSingleton() {
return true;
} public Class getObjectType() {
return FilterInvocationSecurityMetadataSource.class;
} public Object getObject() {
return new DefaultFilterInvocationSecurityMetadataSource(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<RequestMatcher, Collection<ConfigAttribute>> buildRequestMap() {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap =
null;
requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); Map<String, String> resourceMap = this.findResources(); for (Map.Entry<String, String> entry : resourceMap.entrySet()) {
String key = entry.getKey();
editor.setAsText(entry.getValue());
requestMap.put(new AntPathRequestMatcher(key),
(Collection<ConfigAttribute>) editor.getValue());
} return requestMap;
} 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;
}
}
}
完整的配置文件为
<?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.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http auto-config="true">
<custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</http> <authentication-manager>
<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>
</authentication-manager> <beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor" autowire="byType">
<beans:property name="securityMetadataSource" ref="filterInvocationSecurityMetadataSource" />
<beans:property name="authenticationManager" ref="org.springframework.security.authenticationManager"/>
</beans:bean> <beans:bean id="filterInvocationSecurityMetadataSource"
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 re.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>
目前存在的问题是,系统会在初始化时一次将所有资源加载到内存中,即使在数据库中修改了资源信息,系统也不会再次去从数据库中读取资源信息。这就造成了每次修改完数据库后,都需要重启系统才能时资源配置生效。
解决方案是,如果数据库中的资源出现的变化,需要刷新内存中已加载的资源信息时,使用下面代码:
<%
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);
FactoryBean factoryBean = (FactoryBean) ctx.getBean("&filterInvocationSecurityMetadataSource");
FilterInvocationSecurityMetadataSource fids = (FilterInvocationSecurityMetadataSource) factoryBean.getObject();
FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterceptor");
filter.setSecurityMetadataSource(fids);
%>
spring security使用数据库资源的更多相关文章
- spring boot系列--spring security (基于数据库)登录和权限控制
先说一下AuthConfig.java Spring Security的主要配置文件之一 AuthConfig 1 @Configuration 2 @EnableWebSecurity 3 publ ...
- SPRING SECURITY 拦截静态资源
情景: <security:intercept-url pattern="/**" access="USER"/> 当在spring securti ...
- spring security结合数据库验证用户-XML配置方式
之前的用户信息我们都是使用的内存用户,测试例子可以,实际中使用肯定不行,需要结合数据库进行验证用户.这就是本节的重点: 项目目录如下: 在之前的项目中的依赖中添加两个依赖: <dependen ...
- Spring Security使用数据库数据完成认证--练气后期2
写在前面 没错,这篇文章还是练气后期!但作者我相信筑基指日可待! 在前一篇文章当中,我们简单地分析了一下Spring Security的认证流程,知道了如果想要实现对自己用户数据(账户.角色.权限)的 ...
- Spring Security 使用数据库用户进行认证
本文参考或摘录自:http://haohaoxuexi.iteye.com/blog/2157769 本文使用Spring Security自带的方式连接数据库对用户进行认证. 1.Spring Se ...
- spring security基于数据库表进行认证
我们从研究org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl.class的源码开始 public class JdbcDaoI ...
- spring security动态管理资源结合自定义登录页面
如果想将动态管理资源与自定义登录页面一起使用,最简单的办法就是在数据库中将登录页面对应的权限设置为IS_AUTHENTICATED_ANONYMOUSLY. 因此在数据库中添加一条资源信息. INSE ...
- spring security使用数据库验证的逻辑处理
前面做了多个示例,包括使用jdbc和hibernate两种方式访问数据库获取用户信息和权限信息,其中一些关键步骤如下: 我们在SecurityConfig中配置覆盖configure方法时候,可以 ...
- spring security结合数据库验证用户-注解方式
项目目录结构如下: 首先数据库的建立和数据导入,以及一些类的依赖参考XML配置方式,需要修改一些配置. 一.在AppConfig文件中添加DataSource的配置 @Bean(name = &quo ...
随机推荐
- C++的模板特化 和 STL中iterator_traits模板的偏特化
C++中有类模板和函数模板,它们的定义如下所示: 类模板: template<class T1,class T2> class C { //... }; 函数模板: template< ...
- WebAPI接口调用身份验证
Common public interface ICacheWriter { void AddCache(string key, object value, DateTime expDate); vo ...
- 类中用const限定的成员函数
本文转自http://blog.csdn.net/whyglinux/article/details/602329 类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非 ...
- 廖雪峰js教程笔记11 操作DOM(包含作业)
由于HTML文档被浏览器解析后就是一棵DOM树,要改变HTML的结构,就需要通过JavaScript来操作DOM. 始终记住DOM是一个树形结构.操作一个DOM节点实际上就是这么几个操作: 更新:更新 ...
- css与js后边有?v=20160101
原文地址http://blog.csdn.net/zanychou/article/details/8813076 <span style="font-size:14px;" ...
- 【面试题】BD
一面: 自我介绍,简单介绍项目: /***********发现项目没什么可问的,然后开始各种基础知识o(╯□╰)o************/ 内存结构,低地址,高地址: STL底层实现,set是否有序 ...
- sed 字符串替换
1. sed替换的基本语法为: sed 's/原字符串/替换字符串/' 单引号里面,s表示替换,三根斜线中间是替换的样式,特殊字符需要使用反斜线”\”进行转义. 2. 单引号” ‘ ’”是没有办法用反 ...
- React的第一步
首先了解React中所牵扯到的几个重要的概念 什么是React? 是Facebook的开发团队开发出来的一个用于构建用户界面个js库,最近才开源出来公布于世,它的初衷是用于创建“独立的视图组件”,一个 ...
- WPF DataGrid Control
Introduction Since .NET 4.0, Microsoft is shipping a DataGrid control that provides all the basic fu ...
- SIFT特征提取分析
SIFT特征提取分析 sift 关键点,关键点检测 读'D. G. Lowe. Distinctive Image Features from Scale-Invariant Keypoints[J] ...