Spring MVC 项目搭建 -6- spring security 使用自定义Filter实现验证扩展资源验证,使用数据库进行配置
Spring MVC 项目搭建 -6- spring security使用自定义Filter实现验证扩展url验证,使用数据库进行配置
实现的主要流程
1.创建一个Filter 继承 AbstractSecurityInterceptor
- FilterSecurityInterceptor extends AbstractSecurityInterceptor
- 是过滤链中最后一个Filter
- 使用SecurityContext 和 Authentication 对象,来进行辨别用户的 GrantedAuthority。
- 为了实现自定义过滤,需要Authentication和SecurityContext,所以继承AbstractSecurityInterceptor,并且放在FilterSecurityInterceptor之前
2.该过滤器需要注入FilterInvocationSecurityMetadataSource,AccessDecisionManager 和 UserDetailsService
- FilterInvocationSecurityMetadataSource 在spring初始化时获取用户权限资源
- AccessDecisionManager 用于判断当前用户是否有权限
- UserDetailsService 用于根据当前用户获取其权限
3.扩展 FilterInvocationSecurityMetadataSource,AccessDecisionManager 和 UserDetailsService,使其使用数据库的数据
自定义Filter-AppFilterSecurityInterceptor
AppFilterSecurityInterceptor
/**
* 该拦截器用以添加资源拦截
*
* 添加一个拦截器,配置在 FILTER_SECURITY_INTERCEPTOR之前
* 继承本来最后的 AbstractSecurityInterceptor以实现 登录过程
*
*
* 过滤器依赖于 SecurityContext 和 Authentication 对象,来进行辨别用户的 GrantedAuthority。
* 所以,我们要将这个过滤器的位置放在 FilterSecurityInterceptor 之前
*
*/
public class AppFilterSecurityInterceptor extends AbstractSecurityInterceptor
implements Filter {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
//拦截器入口
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
/**
* fi里面有一个被拦截的url
* 调用MyInvocationSecurityMetadataSource的getAttributes(Object object)
* 这个方法获取fi对应的所有权限
* 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
*/
public void invoke(FilterInvocation fi) throws IOException,
ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// 执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
//实现接口的方法
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
}
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void destroy() {
}
public void init(FilterConfig arg0) throws ServletException {
}
//用以注入securityMetadataSource
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource newSource) {
this.securityMetadataSource = newSource;
}
}
配置Filter - spring-sample-security.xml
<!--忽略http的其他配置,此处只用于说明如何配置Filter->
<http auto-config="true" use-expressions="true" >
<custom-filter ref="appFilter" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<!--自定义一个拦截器 -->
<beans:bean id="appFilter"
class="security.filter.AppFilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="appAuthenticationManager" />
<beans:property name="accessDecisionManager" ref="appAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="appSecurityMetadataSource" />
</beans:bean>
扩展FilterInvocationSecurityMetadataSource
AppInvocationSecurityMetadataSource
/**
* 配置用户角色
*/
public class AppInvocationSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource {
private ISecurityResourcesDao resourcesDao;
// resourceMap 为 资源权限的集合 key-url,value-Collection<ConfigAttribute>
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public AppInvocationSecurityMetadataSource(ISecurityResourcesDao resourcesDao) {
this.resourcesDao = resourcesDao;
loadResourceDefine();
}
// 初始化 所有资源与权限的关系
private void loadResourceDefine() {
if (resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
//1.查找所有资源
List<SecurityResource> resources = resourcesDao.findAll();
//2.查找资源需要角色
for (SecurityResource resource : resources) {
Collection<ConfigAttribute> roles = resourcesDao.loadRoleByResource(resource.getResource());
System.out.println("权限=" + roles+" 资源:"+resource.getResource());
//缓存数据
resourceMap.put(resource.getResource(), roles);
}
}
}
// 核心方法,获取url 所需要的权限(角色)
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
FilterInvocation filterInvocation = ((FilterInvocation) object);
// 将参数转为url
String url = ((FilterInvocation) object).getRequestUrl();
System.out.println(url);
//循环已有的角色配置对象 进行url匹配
Iterator<String> ite = resourceMap.keySet().iterator();
while (ite.hasNext()) {
String resURL = ite.next();
RequestMatcher urlMatcher = new AntPathRequestMatcher(resURL);
if (urlMatcher.matches(filterInvocation.getHttpRequest())) {
System.out.println("map:"+resURL + "need:" +resourceMap.get(resURL));
return resourceMap.get(resURL);
}
}
return null;
}
//接口必须实现的方法
public boolean supports(Class<?> clazz) {
return true;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
}
配置appSecurityMetadataSource - spring-sample-security.xml
<!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问-->
<beans:bean id="appSecurityMetadataSource"
class="security.filter.properties.AppInvocationSecurityMetadataSource" >
<beans:constructor-arg name="resourcesDao" ref="securityResourcesDao"/>
</beans:bean>
扩展AccessDecisionManager
AppAccessDecisionManager
/**
* Filter 的资源决策管理器
*
* 用以决定是否有权限访问该请求
*/
public class AppAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null) {
return;
}
//所请求的资源拥有的权限(一个资源对多个权限)
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
//访问所请求资源所需要的权限
String needRole = ((SecurityConfig) ca).getAttribute();
System.out.println("needRole is " + needRole);
// ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (needRole.trim().equals(ga.getAuthority().trim())) {
return;
}
}
}
//没有权限
throw new AccessDeniedException("没有权限访问!");
}
public boolean supports(ConfigAttribute attribute) {
return true;
}
public boolean supports(Class<?> clazz) {
return true;
}
}
配置appAccessDecisionManager -spring-sample-security.xml
<!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="appAccessDecisionManagerBean"
class="security.filter.properties.AppAccessDecisionManager">
</beans:bean>
扩展UserDetailsService
AppUserDetailService(在项目搭建5已经修改)
配置(在项目搭建5已经修改)
添加数据库的表格(其他表 在项目搭建5已经添加)
--添加资源列表SQL
CREATE TABLE security_resource (
id int NOT NULL auto_increment,
name varchar(50) DEFAULT NULL,
type varchar(50) DEFAULT NULL,
resource varchar(200) DEFAULT NULL,
description varchar(200) DEFAULT NULL,
PRIMARY KEY (id)
) ;
CREATE TABLE security_resource_role (
resource_id int not NULL,
role_id int not NULL,
PRIMARY KEY (resource_id,role_id),
CONSTRAINT security_resource_role_ibfk_1 FOREIGN KEY (resource_id) REFERENCES security_resource (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT security_resource_role_ibfk_2 FOREIGN KEY (role_id) REFERENCES security_role (id) ON DELETE CASCADE ON UPDATE CASCADE
) ;
INSERT INTO security_resource (name,type,resource,description) VALUES ('', 'URL', '/app/*', 'app controller');
INSERT INTO security_resource (name,type,resource,description) VALUES ('', 'URL', '/test/*', 'test controller');
INSERT INTO security_resource (name,type,resource,description) VALUES ('', 'URL', '/**', 'all resources');
INSERT INTO security_resource_role (resource_id,role_id) VALUES ('1', '1');
INSERT INTO security_resource_role (resource_id,role_id) VALUES ('2', '1');
INSERT INTO security_resource_role (resource_id,role_id) VALUES ('1', '2');
INSERT INTO security_resource_role (resource_id,role_id) VALUES ('2', '3');
INSERT INTO security_resource_role (resource_id,role_id) VALUES ('3', '3');
vo&&dao
Class
public class SecurityResource{
private int id;
private String name;
private String type;
private String resource;
private String description;
}
@Repository
public class SecurityResourcesDao implements ISecurityResourcesDao {
@Autowired
private JdbcTemplate jdbcTemplate;
private static final Log log = LogFactory.getLog(SecurityUserDao.class);
@Override
public List<SecurityResource> findAll() {
try {
List<SecurityResource> resourceList = new ArrayList<SecurityResource>();
List<Map<String, Object>> resources = jdbcTemplate
.queryForList("select * from security_resource");
for (Map<String, Object> map : resources) {
SecurityResource r = new SecurityResource();
r.setId(Integer.valueOf(map.get("id").toString()));
r.setName(map.get("name").toString());
r.setType(map.get("type").toString());
r.setResource(map.get("resource").toString());
r.setDescription(map.get("description").toString());
resourceList.add(r);
}
return resourceList;
} catch (RuntimeException re) {
log.error("find all resource failed " + re);
throw re;
}
}
// 加载资源与对应的权限
/**
* ConfigAttribute interface
* Class SecurityConfig -> attribute : role
*/
@Override
public Collection<ConfigAttribute> loadRoleByResource(String url) {
try {
String sql = "select r.name as role,re.resource as url "
+ "from security_role r join security_resource_role rr on r.id=rr.role_id "
+ "join security_resource re on re.id=rr.resource_id "
+ "where re.resource='" + url + "'";
List<Map<String, Object>> authList = jdbcTemplate.queryForList(sql);
Collection<ConfigAttribute> auths = new ArrayList<ConfigAttribute>();
for(Map<String, Object> map:authList){
ConfigAttribute auth = new SecurityConfig(map.get("role").toString());
auths.add(auth);
}
return auths;
} catch (RuntimeException re) {
log.error("find roles by url failed " + re);
throw re;
}
}
private final String INSERT_ROLE_RESOURCES_SQL =
"INSERT security_resource_role(resource_id,role_id)" +
"VALUES(?,?)";
@Override
public int addResourcesToRole(SecurityRole role, SecurityResource resource) {
return jdbcTemplate.update( INSERT_ROLE_RESOURCES_SQL,resource.getId(),role.getId());
}
@Override
public List<SecurityResource> getResourceByRole(SecurityRole role) {
String sql = "SELECT res.name,res.description,res.id ,res.resource,res.type FROM security_role r "
+ "JOIN security_resource_role rr ON r.id ='" + role.getId() +"' AND r.id = rr.role_id "
+ "JOIN security_resource res on rr.resource_id = res.id";
RowMapper<SecurityResource> mapper = new RowMapper<SecurityResource>() {
public SecurityResource mapRow(ResultSet rs, int rowNum) throws SQLException {
SecurityResource resource = new SecurityResource();
resource.setId((int) rs.getLong("id"));
resource.setName(rs.getString("name"));
resource.setType(rs.getString("type"));
resource.setResource(rs.getString("resource"));
resource.setDescription(rs.getString("description"));
return resource;
}
};
List<SecurityResource> result = jdbcTemplate.query(sql, mapper);
log.info(result);
return result;
}
}
配置DAO-spring-sample-security.xml
<context:component-scan base-package="security.dao.impl" />
项目改善
经过上述配置,验证流程已经可以完成,但是只有拥有ROLE_TEST的用户可以成功登陆
原因是登陆成功处理器和登陆失败处理器的url被拦截了 ,
url为 test/mytest/loginSuccess
所以要对登陆成功失败处理器的url进行修改
附配置文件-spring-sample-security.xml
<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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置 security 管理 控制器-->
<context:component-scan base-package="security" />
<context:component-scan base-package="security.service.impl" />
<context:component-scan base-package="security.dao.impl" />
<!-- 去除不需要拦截的url -->
<http pattern="/libs/**" security="none"/>
<http pattern="/login.html" security="none" />
<http pattern="/resources/**" security="none" />
<http pattern="/test/mytest/loginSuccess" security="none" />
<!-- 配置一层拦截,需要输入正确用户名密码才能访问网站 -->
<http auto-config="true" use-expressions="true" >
<!-- 拦截所有不是ROLE_USER的请求 -->
<intercept-url pattern="/*" access="hasAnyRole('ROLE_ADMIN','ROLE_DEV','ROLE_TEST')" />
<!-- 登录配置 -->
<form-login login-page="/login.html"
default-target-url="/test/mytest/loginSuccess"
authentication-failure-url="/test/mytest/loginFaild"/>
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/j_spring_security_logout"/>
<custom-filter ref="appFilter" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<beans:bean id="appFilter"
class="security.filter.AppFilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="appAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="appSecurityMetadataSource" />
</beans:bean>
<!--默认拦截器 -->
<authentication-manager alias="authenticationManager">
<!--验证配置,认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-provider user-service-ref="appUserDetailService">
<password-encoder ref="passwordEncoder">
<salt-source ref="saltSource"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
<!--在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 -->
<beans:bean id="appUserDetailService" class="security.service.impl.AppUserDetailService" />
<!--访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="appAccessDecisionManagerBean"
class="security.filter.properties.AppAccessDecisionManager">
</beans:bean>
<!--资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问-->
<beans:bean id="appSecurityMetadataSource"
class="security.filter.properties.AppInvocationSecurityMetadataSource" >
<beans:constructor-arg name="resourcesDao" ref="securityResourcesDao"/>
</beans:bean>
<!--加密密码 -->
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" />
<!--为密码添加 saltSource -->
<beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource" >
<beans:property name="userPropertyToUse" value="salt"/>
</beans:bean>
</beans:beans>
Spring MVC 项目搭建 -6- spring security 使用自定义Filter实现验证扩展资源验证,使用数据库进行配置的更多相关文章
- Spring MVC 项目搭建 -5- spring security 使用数据库进行验证
Spring MVC 项目搭建 -5- spring security 使用数据库进行验证 1.创建数据表格(这里使用的是mysql) CREATE TABLE security_role ( id ...
- Spring MVC 项目搭建 -3- 快速 添加 spring security
Spring MVC 项目搭建 -3- 快速 添加 spring security 1.添加 spring-sample-security.xml <!-- 简单的安全检验实现 --> & ...
- Spring MVC 项目搭建 -4- spring security-添加自定义登录页面
Spring MVC 项目搭建 -4- spring security-添加自定义登录页面 修改配置文件 <!--spring-sample-security.xml--> <!-- ...
- Spring MVC 项目搭建 -2- 添加service ,dao,junit
Spring MVC 项目搭建 -2- 添加service ,dao,junit 1.dao public class Hero { private String name; public Strin ...
- Spring MVC 项目搭建 -1- 创建项目
Spring MVC 项目搭建 -1- 创建项目 1.创建 Dynamic Web project (SpringDemo),添加相关jar包 2.创建一个简单的controller类 package ...
- IDEA 创建Spring MVC项目搭建
概述 IntelliJ IDEA是一款更加集成智能的开发工具,相对Myeclipse开发而言,使用起来相对更加的方便:初步手动使用IDEA搭建Spring MVC项目,现将操作流程整理记录如下. 环境 ...
- 简单Spring MVC项目搭建
1.新建Project 开发环境我使用的是IDEA,其实使用什么都是大同小异的,关键是自己用的顺手. 首先,左上角File→New→Project.在Project页面选择Maven,然后勾上图中所示 ...
- Java Spring MVC项目搭建(二)——项目配置
1.站点配置文件web.xml 每一个Spring MVC 项目都必须有一个站点配置文件web.xml,他的主要功能吗....有一位大哥已经整理的很好,我借来了,大家看看: 引用博客地址: http: ...
- Java Spring MVC项目搭建(一)——Spring MVC框架集成
1.Java JDK及Tomcat安装 我这里安装的是JDK 1.8 及 Tomcat 8,安装步骤详见:http://www.cnblogs.com/eczhou/p/6285248.html 2. ...
随机推荐
- 用户登录安全框架shiro—用户的认证和授权(一)
ssm整合shiro框架,对用户的登录操作进行认证和授权,目的很纯粹就是为了增加系统的安全线,至少不要输在门槛上嘛. 这几天在公司独立开发一个供公司内部人员使用的小管理系统,客户不多但是登录一直都是 ...
- 使用javascript生成当前博文地址的二维码图片
前面的话 在电脑端发现一篇好的博文,想在手机上访问.这时,就必须打开手机浏览器输入长长的URL地址才行,非常不方便.如果在博客标题的后面跟一张小的图片,点击该图片后,出现一张二维码的大图,然后再通过手 ...
- 6. Java 加解密技术系列之 3DES
Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES ...
- 模拟实现简化版List迭代器&嵌入List
1.迭代器(iterators)概念(1)迭代器是一种抽象的设计概念,其定义为:提供一种方法,使他能够按顺序遍历某个聚合体(容器)所包含的所有元素,但又不需要暴露该容器的内部表现方式. (2)迭代器是 ...
- mysql之 mysqldump 备份恢复详解
mysqldump是MySQL用于转存储数据库的客户端程序.转储包含创建表和/或装载表的SQL语句 ,用来实现轻量级的快速迁移或恢复数据库,是mysql数据库实现逻辑备份的一种方式. mysqldum ...
- LiteIDE灰调配色方案
说明 本文写于2017-04-03,使用LiteIDE X31(基于Qt 4.8.5),操作系统为Windows. 使用 LiteIDE下载后解压即可使用.配色方案的所有配置文件都位于liteide/ ...
- 云计算之路-阿里云上:14:20-14:55博客后台2台服务器都CPU 100%引发的故障
非常抱歉,今天下午14:20-14:55期间,由于同一个负载均衡中的2台服务器都出现CPU 100%问题,造成博客后台无法正常访问,由此给您带来了很大很大的麻烦,请您谅解. 博客后台是CPU消耗很低的 ...
- 初识Tensorboard
1.什么是Tensorboard? PPT设计原则中有这样一条,叫"文不如表,表不如图",可见图表在表达中更为直观.明确.程序设计中也是一样,我们经常用图表来描述程序的结构和流程, ...
- MyBB 18 SQL Injection Vulnerability
<?php error_reporting(0); ?> <form method="post" action=""> Input a ...
- Linux SSH安全技巧
SSH服务器配置文件是/etc/ssh/sshd_conf.在你对它进行每一次改动后都需要重新启动SSH服务,以便让改动生效. 1.修改SSH监听端口默认情况下,SSH监听连接端口22,攻击者使用端口 ...