DROP TABLE IF EXISTS `resc`;
CREATE TABLE `resc` (
`id` bigint(20) NOT NULL DEFAULT '0',
`name` varchar(50) DEFAULT NULL,
`res_type` varchar(50) DEFAULT NULL,
`res_string` varchar(200) DEFAULT NULL,
`descn` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of resc
-- ----------------------------
INSERT INTO `resc` VALUES ('1', '', 'URL', '/adminPage.jsp', '管理员页面');
INSERT INTO `resc` VALUES ('2', '', 'URL', '/index.jsp', '');
INSERT INTO `resc` VALUES ('3', null, 'URL', '/test.jsp', '测试页面');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` bigint(20) NOT NULL DEFAULT '0',
`name` varchar(50) DEFAULT NULL,
`descn` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'ROLE_ADMIN', '管理员角色');
INSERT INTO `role` VALUES ('2', 'ROLE_USER', '用户角色');
INSERT INTO `role` VALUES ('3', 'ROLE_TEST', '测试角色');
-- ----------------------------
-- Table structure for resc_role
-- ----------------------------
DROP TABLE IF EXISTS `resc_role`;
CREATE TABLE `resc_role` (
`resc_id` bigint(20) NOT NULL DEFAULT '0',
`role_id` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`resc_id`,`role_id`),
KEY `fk_resc_role_role` (`role_id`),
CONSTRAINT `fk_resc_role_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `fk_resc_role_resc` FOREIGN KEY (`resc_id`) REFERENCES `resc` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of resc_role
-- ----------------------------
INSERT INTO `resc_role` VALUES ('1', '1');
INSERT INTO `resc_role` VALUES ('2', '1');
INSERT INTO `resc_role` VALUES ('2', '2');
INSERT INTO `resc_role` VALUES ('3', '3');
-- ----------------------------
-- Table structure for t_c3p0
-- ----------------------------
DROP TABLE IF EXISTS `t_c3p0`;
CREATE TABLE `t_c3p0` (
`a` char(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_c3p0
-- ----------------------------
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL DEFAULT '0',
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`descn` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'admin', 'admin', '1', '管理员');
INSERT INTO `user` VALUES ('2', 'user', 'user', '1', '用户');
INSERT INTO `user` VALUES ('3', 'test', 'test', '1', '测试');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` bigint(20) NOT NULL DEFAULT '0',
`role_id` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`,`role_id`),
KEY `fk_user_role_role` (`role_id`),
CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1');
INSERT INTO `user_role` VALUES ('1', '2');
INSERT INTO `user_role` VALUES ('2', '2');
INSERT INTO `user_role` VALUES ('3', '3');
1
select re.res_string,r.name
from role r,resc re,resc_role rr
where r.id=rr.role_id and re.id=rr.resc_id
1、ResourceMapping类代码如下:
package cn.quan.ssm.sec.entity;
import org.springframework.jdbc.object.MappingSqlQuery;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @auther zhangsq on 2017-9-6.
*/
public class ResourceMapping extends MappingSqlQuery {
public ResourceMapping(DataSource dataSource, String resourceQuery) {
super(dataSource,resourceQuery);
compile();
}
/**
* //对结果集进行封装处理
* @param rs
* @param i
* @return
* @throws SQLException
*/
protected Object mapRow(ResultSet rs, int i) throws SQLException {
String url = rs.getString(1);
String role = rs.getString(2);
Resource resource = new Resource(url,role);
return resource;
}
}
2、Resource类代码如下:
package cn.quan.ssm.sec.entity;
/**
* @auther zhangsq on 2017-9-6.
* 资源信息类
*/
public 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 void setUrl(String url) {
this.url = url;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
3、JdbcRequestMapBulider类代码如下:
package cn.quan.ssm.sec;
import cn.quan.ssm.sec.entity.Resource;
import cn.quan.ssm.sec.entity.ResourceMapping;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
/**
* @auther zhangsq on 2017-9-6.
* 查询资源和角色,并构建RequestMap
*/
public class JdbcRequestMapBulider extends JdbcDaoSupport{
//查询数据的sql语句,该属性在配置bean的时候传入即可。
private String resourceQuery = "";
public String getResourceQuery() {
return resourceQuery;
}
public void setResourceQuery(String resourceQuery) {
this.resourceQuery = resourceQuery;
}
//查询资源的方法
public List findResourceData(){
ResourceMapping resourceMapping = new ResourceMapping(getDataSource(),resourceQuery);
return resourceMapping.execute();
}
/**
* 拼接RequestMap
* buildRequestMap方法用来最后拼接成LinkedHashMap>共security使用。
* @return
*/
public LinkedHashMap> buildRequestMap(){
LinkedHashMap> requestMap =
new LinkedHashMap>();
List resourcesList = this.findResourceData();//查询资源信息
for(Resource resource : resourcesList){
RequestMatcher requestMatcher = this.getRequestMatcher(resource.getUrl());
List list = new ArrayList();
list.add(new SecurityConfig(resource.getRole()));
requestMap.put(requestMatcher,list);
}
return requestMap;
}
//通过一个字符串地址构建一个AntPathRequestMatcher对象
//getRequestMatcher方法就是用来创建RequestMatcher对象的
protected RequestMatcher getRequestMatcher(String url){
return new AntPathRequestMatcher(url);
}
}
package cn.quan.ssm.sec.dao;
import cn.quan.ssm.sec.JdbcRequestMapBulider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* security的运行过程,security实现控制的功能其实就是通过一系列的拦截器来实现的,当用户登陆的时候,
* 会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现类,
* 同时AuthenticationManager会调用ProviderManager来获取用户验证信息,其中不同的Provider调用的服务不同,
* 因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等,这个例子中就是为数据库;
* 如果验证通过后会将用户的权限信息放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。
* 当访问资源,访问url时,会通过AbstractSecurityInterceptor拦截器拦截,
* 其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,
* 其中FilterInvocationSecurityMetadataSource的常用的实现类为DefaultFilterInvocationSecurityMetadataSource,
* 这个类中有个很关键的东西就是requestMap,也就是我们上面所得到的数据,在调用授权管理器AccessDecisionManager,
* 这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,
* 还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略,如果权限足够,则返回,
* 权限不够则报错并调用权限不足页面。
* 根据源码debug跟踪得出,其实资源权限关系就放在DefaultFilterInvocationSecurityMetadataSource的requestMap,中的,
* 这个requestMap就是我们JdbcRequestMapBulider.buildRequestMap()方法所需要的数据类型,
* 因此,顺气自然就想到了我们自定义一个类继承FilterInvocationSecurityMetadataSource接口,
* 将数据查出的数据放到requestMap中去。
* 制定类MyFilterInvocationSecurityMetadataSource继承FilterInvocationSecurityMetadataSource和InitializingBean接口。
* @auther zhangsq on 2017-9-6.
*/
public class MyFilterInvocationSecurityMetadataSource
implements FilterInvocationSecurityMetadataSource,InitializingBean {
private final static List NULL_CONFIG_ATTRIBUTE = null;
//资源权限集合
private Map> requestMap;
//查找数据库权限和资源的关系
private JdbcRequestMapBulider bulider;
/**
* 更具访问资源的地址查找所需要的权限
* @param object
* @return
* @throws IllegalArgumentException
*/
public Collection getAttributes(Object object) throws IllegalArgumentException {
final HttpServletRequest request = ((FilterInvocation) object) .getRequest();
Collection atts = NULL_CONFIG_ATTRIBUTE;
for(Map.Entry> entry : requestMap.entrySet()){
if(entry.getKey().matches(request)){
atts = entry.getValue();
break;
}
}
return atts;
}
/**
* 获取所有的权限
* @return
*/
public Collection getAllConfigAttributes() {
Set attributes = new HashSet();
for(Map.Entry> entry : requestMap.entrySet()){
attributes.addAll(entry.getValue());
}
System.out.println("总共有这些权限:"+attributes.toString());
return attributes;
}
public boolean supports(Class> aClass) {
return FilterInvocation.class.isAssignableFrom(aClass);
}
//绑定requestMap
protected Map> bindRequestMap() {
return bulider.buildRequestMap();
}
public void afterPropertiesSet() throws Exception {
this.requestMap = this.bindRequestMap();
}
public void refreshResuorceMap() {
this.requestMap = this.bindRequestMap();
}
public JdbcRequestMapBulider getBulider() {
return bulider;
}
public void setBulider(JdbcRequestMapBulider bulider) {
this.bulider = bulider;
}
}
说明:
1.http中的custom-filter是特别要注意的,就是通过这个标签来增加过滤器的,其中before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默认的过滤器之前执行。
package cn.quan.ssm.sec.dao;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
import java.util.Iterator;
/**
* @auther zhangsq on 2017-9-6.
*/
public class MyAccessDecisionManager implements AccessDecisionManager {
/**
* 该方法决定该权限是否有权限访问该资源,其实object就是一个资源的地址,authentication是当前用户的对应权限,
* 如果没登陆就为游客,登陆了就是该用户对应的权限
* @param authentication
* @param object
* @param configAttributes
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
*/
public void decide(Authentication authentication, Object object, Collection configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if(null == configAttributes){
return;
}
//所请求的资源拥有的权限(一个资源对多个权限)
Iterator iterable = configAttributes.iterator();
while (iterable.hasNext()){
ConfigAttribute configAttribute = iterable.next();
//访问所请求的资源所需要的权限
String needPermission = configAttribute.getAttribute();
System.out.println("访问"+object.toString()+"需要的权限是:"+needPermission);
//用户所拥有的权限authentication
Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority ga : authorities) {
if (needPermission.equals(ga.getAuthority())) {
return;
}
}
}
//没有权限
throw new AccessDeniedException("没有权限访问!");
}
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
public boolean supports(Class> aClass) {
return true;
}
}