Spring Security 3.0数据库动态实现权限控制

第一次接触Spring Security,在网上找了很多资料,有简单示例的,就是把用户名及权限信息配置在XML里面,但这种不太适合一般的项目!有些资料是翻译源代码讲解各个过滤器的作用以及其工作原理!由于项目时间紧所以求成心切的我看这些资料的时候总是很匆忙的跳步来看,到最后反而稀里糊涂,脑子里更乱,真心的希望有个狄仁杰中的元芳能在身边,这样我还能听取下他的意见:"元芳,你怎么看!"这两天静下来把之前看的那些详细看了看,最后感觉这玩意儿也不是那么那么让人抓狂!

开发环境 SpringIDE 3.0、Spring Security 3.0.2、myibatis、Maven、MySql
最前面的spring配置以及Maven管理架包、myibatis的配置就不说了
首先:
1、需要将所需要的JAR包在pom中配置
[img]http://dl.iteye.com/upload/attachment/0075/3078/beabd0dd-9529-3fb0-a7a5-676c04cf8690.jpg[/img]
2、在web.xml中添加spring security的过滤链

<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>

3、添加applicationContext-security.xml到根目录下
内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<!-- <global-method-security pre-post-annotations="enabled" /> -->

<!-- access-denied-page配置访问失败页面 -->
<http auto-config="true" access-denied-page="/accessDenied.htm">
<!-- 不要过滤图片等静态资源,其中**代表可以跨越目录,*不可以跨越目录。 -->
<intercept-url pattern="/**/*.jpg" filters="none" />
<intercept-url pattern="/**/*.png" filters="none" />
<intercept-url pattern="/**/*.gif" filters="none" />
<intercept-url pattern="/**/*.css" filters="none" />
<intercept-url pattern="/**/*.js" filters="none" />
<intercept-url pattern="/login.htm*" filters="none"/>
<!-- <intercept-url pattern="/index.htm" access="ROLE_USER,ROLE_ADMIN"/>
<intercept-url pattern="/header.htm" access="ROLE_USER,ROLE_ADMIN"/>
<intercept-url pattern="/left.htm" access="ROLE_USER,ROLE_ADMIN"/> -->
<intercept-url pattern="/**" access="ROLE_USER"/>
<!-- 配置登录页面 -->
<form-login login-page="/login.htm"
default-target-url="/" authentication-failure-url="/login.htm?error=true"
login-processing-url="/springSecurityLogin"/>
<!--"记住我"功能,采用持久化策略(将用户的登录信息存放在数据库表中) -->
<!-- <remember-me/> -->
<!-- 用户退出的跳转页面 -->
<logout logout-success-url="/login.htm" invalidate-session="true"
logout-url="/logout" />
<!-- 会话管理,设置最多登录异常,error-if-maximum-exceeded = false为第二次登录就会使前一个登录失效 -->
<session-management invalid-session-url="/error/errorPage.jsp">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="false" />
</session-management>

<!--添加自定义的过滤器 放在FILTER_SECURITY_INTERCEPTOR之前有效 -->
<custom-filter ref="customFilterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</http>

<!-- 配置认证管理器 -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder hash="md5"/>
<!-- <jdbc-user-service data-source-ref="dataSource"/> -->
</authentication-provider>
</authentication-manager>
</b:beans>

上面是XML文件,需要说明的是:intercept-url节点需至少有一个拦截的,如果全部过滤图片或者是样式这些,spring框架启动的时候,就不会拦截任何URL,因为你没有配置相关的拦截的intercept-url,所以它就以为任何人都可以访问整个资源!


其中的节点用途都有说明,就不再啰嗦
4、创建CustomUserDetailsService、CustomAccessDecisionManager、CustomFilterSecurityInterceptor、CustomInvocationSecurityMetadataSource
这4个类我定义的接口,有相应的实现类,在实现动态权限控制中这4个类都会用到


[size=x-large][b]4个接口代码如下:[/b][/size]


I、CustomUserDetailsService接口类代码如下:

package com.doone.wisdom.security.service;

import org.springframework.security.core.userdetails.UserDetailsService;

/**
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomUserDetailsService
* @author Johnny_L_Q
*/
public interface CustomUserDetailsService extends UserDetailsService{


}



II、CustomAccessDecisionManager接口类的代码如下:

package com.doone.wisdom.security.service;

import org.springframework.security.access.AccessDecisionManager;

/**
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomAccessDecisionManager
* @author Johnny_L_Q
*/
public interface CustomAccessDecisionManager extends AccessDecisionManager{


}


III、CustomFilterSecurityInterceptor接口类的代码如下:

package com.doone.wisdom.security.service;

import javax.servlet.Filter;

/**
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomFilterSecurityInterceptor
* @author Johnny_L_Q
*/
public interface CustomFilterSecurityInterceptor extends Filter{

}


IV、CustomInvocationSecurityMetadataSource接口类的代码如下:

package com.doone.wisdom.security.service;

import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

/**
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomInvocationSecurityMetadataSource
* @author Johnny_L_Q
*/
public interface CustomInvocationSecurityMetadataSource extends FilterInvocationSecurityMetadataSource{

}



[size=x-large][b]以下相应接口类的实现类代码:[/b][/size]


a、CustomUserDetailsServiceImpl 实现 CustomUserDetailsService接口,CustomUserDetailsService接口类继承自 org.springframework.security.core.userdetails.UserDetailsService,这个类主要是处理用户登录信息,在用户输入用户名和密码后,spring security会带着用户名调用类里面的loadUserByUsername(usrename)方法,在这里个人理解是,它没有用密码作为验证条件是通过用户名查出用户信息,然后把数据库中查出的用户密码和刚刚用户输入的存储在session中的密码做比较,然后判断该用户是否合法!

package com.doone.wisdom.security.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.doone.wisdom.dao.security.UserDao;
import com.doone.wisdom.entity.security.RoleEntity;
import com.doone.wisdom.entity.security.UserEntity;
import com.doone.wisdom.security.service.CustomUserDetailsService;

/**
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomUserDetailsServiceImpl
* @author Johnny_L_Q
*/
@Service("customUserDetailsService")
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {

/**
* @Fields serialVersionUID : TODO(用一句话描述这个变量表示什么).
*/
private static final long serialVersionUID = 1L;

@Resource
private UserDao userDao;


/*
* 根据用户名判断是否存在
* <p>Title: loadUserByUsername</p>
* <p>Description: </p>
* @param arg0
* @return
* @throws UsernameNotFoundException
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserEntity user = userDao.getUser(username);

if (null == user) {
throw new UsernameNotFoundException("用户" + username + "不存在");
}


Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
List<String> list = userDao.getAuthoritiesByUsername(username);
for (int i = 0; i < list.size(); i++) {
auths.add(new GrantedAuthorityImpl(list.get(i)));
System.out.println("loadUserByUsername : " + list.get(i));
}
//因为UserEntity实现了UserDetails,所以也可以直接返回user
return new User(username, user.getPassword(), true, true, true, true, auths);
}

}



b、CustomAccessDecisionManagerImpl 实现 CustomAccessDecisionManager接口, CustomAccessDecisionManager接口类继承自 org.springframework.security.access.AccessDecisionManager
这个类主要是处理用户在访问某个URL的时候,就会通过访问该类的权限与登录用户所拥有的权限做比较,如果拥有权限,那你就可以到访问外星文明了,如果没有权限,那你就要被遣送回非洲,还会抛一个异常告诉你,你已经被遣送回非洲!

package com.doone.wisdom.security.impl;

import java.util.Collection;
import java.util.Iterator;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.stereotype.Service;

import com.doone.wisdom.security.service.CustomAccessDecisionManager;

/**
*
* AccessdecisionManager在Spring security中是很重要的。
*
* 在验证部分简略提过了,所有的Authentication实现需要保存在一个GrantedAuthority对象数组中。 这就是赋予给主体的权限。
* GrantedAuthority对象通过AuthenticationManager 保存到
* Authentication对象里,然后从AccessDecisionManager读出来,进行授权判断。
*
* Spring Security提供了一些拦截器,来控制对安全对象的访问权限,例如方法调用或web请求。
* 一个是否允许执行调用的预调用决定,是由AccessDecisionManager实现的。 这个 AccessDecisionManager
* 被AbstractSecurityInterceptor调用, 它用来作最终访问控制的决定。
* 这个AccessDecisionManager接口包含三个方法:
*
* void decide(Authentication authentication, Object secureObject,
* List<ConfigAttributeDefinition> config) throws AccessDeniedException; boolean
* supports(ConfigAttribute attribute); boolean supports(Class clazz);
*
* 从第一个方法可以看出来,AccessDecisionManager使用方法参数传递所有信息,这好像在认证评估时进行决定。
* 特别是,在真实的安全方法期望调用的时候,传递安全Object启用那些参数。 比如,让我们假设安全对象是一个MethodInvocation。
* 很容易为任何Customer参数查询MethodInvocation,
* 然后在AccessDecisionManager里实现一些有序的安全逻辑,来确认主体是否允许在那个客户上操作。
* 如果访问被拒绝,实现将抛出一个AccessDeniedException异常。
*
* 这个 supports(ConfigAttribute) 方法在启动的时候被
* AbstractSecurityInterceptor调用,来决定AccessDecisionManager
* 是否可以执行传递ConfigAttribute。 supports(Class)方法被安全拦截器实现调用,
* 包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomAccessDecisionManagerImpl
* @author Johnny_L_Q
*/
@Service("customAccessDecisionManager")
public class CustomAccessDecisionManagerImpl implements
CustomAccessDecisionManager {

/*
* <p>Title: decide</p>
* <p>Description: </p>
* @param arg0
* @param arg1
* @param arg2
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
* @see org.springframework.security.access.AccessDecisionManager
* #decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
*/
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
InsufficientAuthenticationException {
if (null == configAttributes) {
return;
}

Iterator<ConfigAttribute> cons = configAttributes.iterator();

while(cons.hasNext()){
ConfigAttribute ca = cons.next();
String needRole = ((SecurityConfig) ca).getAttribute();
//gra 为用户所被赋予的权限,needRole为访问相应的资源应具有的权限
for (GrantedAuthority gra : authentication.getAuthorities()) {
if (needRole.trim().equals(gra.getAuthority().trim())) {
return;
}
}
}
throw new AccessDeniedException("Access Denied");
}

/*
* <p>Title: supports</p>
* <p>Description: </p>
* @param arg0
* @return
* @see org.springframework.security.access.AccessDecisionManager#supports(org.springframework.security.access.ConfigAttribute)
*/
@Override
public boolean supports(ConfigAttribute arg0) {
// TODO Auto-generated method stub
return true;
}

/*
* <p>Title: supports</p>
* <p>Description: </p>
* @param arg0
* @return
* @see org.springframework.security.access.AccessDecisionManager#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
}


c、CustomFilterSecurityInterceptorImpl 实现 CustomFilterSecurityInterceptor接口, CustomFilterSecurityInterceptor接口类继承 javax.servlet.Filter
该类的目的是拦截的入口,首先启动的时候会注入注入里面的属性,然后当用户访问你配置过的URL的时候,就会被拦截进入到doFilter方法中,在调用infoke()方法进入到CustomAccessDecisionManagerImpl.decide()方法中将访问的url与配置的url的权限做比较

package com.doone.wisdom.security.impl;

import java.io.IOException;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;

import com.doone.wisdom.security.service.CustomFilterSecurityInterceptor;

/**
* 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
* securityMetadataSource相当于本包中自定义的CostomInvocationSecurityMetadataSourceService。
* 该CostomInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中,
* 供Spring Security使用,用于权限校验。
*
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomFilterSecurityInterceptorImpl
* @author Johnny_L_Q
*/
@Service("customFilterSecurityInterceptor")
public class CustomFilterSecurityInterceptorImpl extends
AbstractSecurityInterceptor implements CustomFilterSecurityInterceptor {

@Resource
@Qualifier("customInvocationSecurityMetadataSource")
private FilterInvocationSecurityMetadataSource securityMetadataSource;


@Resource
@Qualifier("customAccessDecisionManager")
@Override
public void setAccessDecisionManager(
AccessDecisionManager accessDecisionManager) {
// TODO Auto-generated method stub
super.setAccessDecisionManager(accessDecisionManager);
}
/* @Resource
@Qualifier("customAccessDecisionManager")
public AccessDecisionManager accessDecisionManager;*/

/* @Resource
@Qualifier("authenticationManager")
public AuthenticationManager authenticationManager;*/


@Resource
@Qualifier("authenticationManager")
@Override
public void setAuthenticationManager(AuthenticationManager newManager) {
super.setAuthenticationManager(newManager);
}

/*
* <p>Title: doFilter</p>
* <p>Description: </p>
* @param arg0
* @param arg1
* @param arg2
* @throws IOException
* @throws ServletException
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
infoke(fi);

}

/**
* TODO(这里用一句话描述这个方法的作用).
* @param fi
* @throws ServletException
* @throws IOException
*/
private void infoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);

try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}

}

/*
* <p>Title: init</p>
* <p>Description: </p>
* @param arg0
* @throws ServletException
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub

}

/*
* <p>Title: getSecureObjectClass</p>
* <p>Description: </p>
* @return
* @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass()
*/
@Override
public Class<?> getSecureObjectClass() {
// TODO Auto-generated method stub
return FilterInvocation.class;
}

/*
* <p>Title: obtainSecurityMetadataSource</p>
* <p>Description: </p>
* @return
* @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#obtainSecurityMetadataSource()
*/
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
// TODO Auto-generated method stub
return this.securityMetadataSource;
}

/*
* <p>Title: destroy</p>
* <p>Description: </p>
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy() {
// TODO Auto-generated method stub

}


public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}


public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
}

d、CustomFilterInvocationSecurityMetadataSourceImpl 实现 CustomFilterInvocationSecurityMetadataSource接口,CustomFilterInvocationSecurityMetadataSource接口类继承自
org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource
该类的主要作用是在Spring Security的整个过滤链启动后,在容器启动的时候,程序就会进入到该类中的init()方法,init调用了loadResourceDefine()方法,该方法的主要目的是将数据库中的所有资源与权限读取到本地缓存中保存起来!类中的resourceMap就是保存的所有资源和权限的集合,URL为Key,权限作为Value!

package com.doone.wisdom.security.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.stereotype.Service;

import com.doone.wisdom.entity.security.AuthorityEntity;
import com.doone.wisdom.entity.security.RoleEntity;
import com.doone.wisdom.security.service.CustomInvocationSecurityMetadataSource;
import com.doone.wisdom.service.iface.AuthorityService;
import com.doone.wisdom.service.iface.ResourceService;

/**
* TODO(这里用一句话描述这个类的职责).
* @ClassName: CustomInvocationSecurityMetadataSourceImpl
* @author Johnny_L_Q
*/
@Service("customInvocationSecurityMetadataSource")
public class CustomInvocationSecurityMetadataSourceImpl implements
CustomInvocationSecurityMetadataSource {

@Resource
private ResourceService resourceService;

@Resource
private AuthorityService authorityService;

private UrlMatcher urlMatcher = new AntUrlPathMatcher();

//private AntPathRequestMatcher pathMatcher;

private HashMap<String, Collection<ConfigAttribute>> resourceMap = null;

/**
*
* 自定义方法,这个类放入到Spring容器后,
* 指定init为初始化方法,从数据库中读取资源
* TODO(这里用一句话描述这个方法的作用).
*/
@PostConstruct
public void init() {
loadResourceDefine();
}

/**
*
* TODO(程序启动的时候就加载所有资源信息).
*/
private void loadResourceDefine() {

// 在Web服务器启动时,提取系统中的所有权限。
//sql = "select authority_name from pub_authorities";

List<AuthorityEntity> query = authorityService.getAllAuthoritys();

/**//*
* 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。
* sparta
*/
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();

for (AuthorityEntity auth : query) {
String authName = auth.getAuthorityName();

ConfigAttribute ca = new SecurityConfig(auth.getAuthorityName());

List<String> resources = resourceService.getResourcesByAuthName(authName);


for (String str : resources) {
//String authName = auth2.getAuthorityName();
String url = str;

/**//*
* 判断资源文件和权限的对应关系,如果已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限增加到权限集合中。
* sparta
*/
if (resourceMap.containsKey(url)) {

Collection<ConfigAttribute> value = resourceMap.get(url);
value.add(ca);
resourceMap.put(url, value);
} else {
Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
atts.add(ca);
resourceMap.put(url, atts);
}

}

}

}

/**
* TODO(自定义方法,将List<Role>集合转换为框架需要的Collection<ConfigAttribute>集合).
* @param roles 角色集合
* @return list 封装好的Collection集合
*/
private Collection<ConfigAttribute> listToCollection(List<RoleEntity> roles) {
List<ConfigAttribute> list = new ArrayList<ConfigAttribute>();

for (RoleEntity role : roles) {
list.add(new SecurityConfig(role.getRoleName()));

}
return list;
}

/*
* <p>Title: getAllConfigAttributes</p>
* <p>Description: </p>
* @return
* @see org.springframework.security.access.SecurityMetadataSource#getAllConfigAttributes()
*/
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}

/*
* <p>Title: getAttributes</p>
* <p>Description: </p>
* @param arg0
* @return
* @throws IllegalArgumentException
* @see org.springframework.security.access.SecurityMetadataSource#getAttributes(java.lang.Object)
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
//object 是一个URL ,为用户请求URL
String url = ((FilterInvocation)object).getRequestUrl();
if("/".equals(url)){
return null;
}
int firstQuestionMarkIndex = url.indexOf(".");
//判断请求是否带有参数 如果有参数就去掉后面的后缀和参数(/index.do --> /index)
if(firstQuestionMarkIndex != -1){
url = url.substring(0,firstQuestionMarkIndex);
}

Iterator<String> ite = resourceMap.keySet().iterator();
//取到请求的URL后与上面取出来的资源做比较
while (ite.hasNext()) {
String resURL = ite.next();
if(urlMatcher.pathMatchesUrl(url, resURL)){
return resourceMap.get(resURL);
}
}
return null;
}


/*
* <p>Title: supports</p>
* <p>Description: </p>
* @param arg0
* @return
* @see org.springframework.security.access.SecurityMetadataSource#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
}



以上就是spring security所要用到的代码,接下来是数据库
5、数据库设计的话就是标准的7张表
1、pub_authorities

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:29:52
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_authorities
-- ----------------------------
CREATE TABLE `pub_authorities` (
`authority_id` varchar(32) COLLATE utf8_bin NOT NULL,
`authority_name` varchar(40) COLLATE utf8_bin NOT NULL,
`authority_desc` varchar(100) COLLATE utf8_bin DEFAULT NULL,
`enabled` int(10) NOT NULL,
`issys` int(10) NOT NULL,
PRIMARY KEY (`authority_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_authorities` VALUES ('1', 'ROLE_ADMIN', '首页广告条管理', '1', '0');
INSERT INTO `pub_authorities` VALUES ('2', 'ROLE_USER', '首页', '1', '0');

2、pub_resources

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:30:25
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_resources
-- ----------------------------
CREATE TABLE `pub_resources` (
`resource_id` varchar(32) COLLATE utf8_bin NOT NULL,
`resource_name` varchar(100) COLLATE utf8_bin NOT NULL,
`resource_type` varchar(40) COLLATE utf8_bin NOT NULL,
`priority` int(10) NOT NULL,
`resource_string` varchar(200) COLLATE utf8_bin NOT NULL,
`resource_desc` varchar(100) COLLATE utf8_bin DEFAULT NULL,
`enabled` int(10) NOT NULL,
`issys` int(10) NOT NULL,
PRIMARY KEY (`resource_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_resources` VALUES ('1', '首页广告条管理', 'action', '0', '/funcPages/adManager', '首页广告条管理', '1', '0');
INSERT INTO `pub_resources` VALUES ('2', '首页', 'action', '0', '/index', '首页', '1', '0');

3、pub_authorities_resources

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:30:17
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_authorities_resources
-- ----------------------------
CREATE TABLE `pub_authorities_resources` (
`id` varchar(32) COLLATE utf8_bin NOT NULL,
`authority_id` varchar(32) COLLATE utf8_bin NOT NULL,
`resource_id` varchar(32) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_res_aut` (`authority_id`),
KEY `fk_res` (`resource_id`),
CONSTRAINT `fk_res` FOREIGN KEY (`resource_id`) REFERENCES `pub_resources` (`resource_id`),
CONSTRAINT `fk_res_aut` FOREIGN KEY (`authority_id`) REFERENCES `pub_authorities` (`authority_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_authorities_resources` VALUES ('1', '1', '1');
INSERT INTO `pub_authorities_resources` VALUES ('2', '2', '2');

4、pub_roles

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:30:30
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_roles
-- ----------------------------
CREATE TABLE `pub_roles` (
`role_id` varchar(32) COLLATE utf8_bin NOT NULL,
`role_name` varchar(100) COLLATE utf8_bin NOT NULL,
`role_desc` varchar(100) COLLATE utf8_bin DEFAULT NULL,
`enabled` int(10) NOT NULL,
`issys` int(10) NOT NULL COMMENT '角色表',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_roles` VALUES ('1', 'ROLE_ADMIN', '系统登录', '1', '0');
INSERT INTO `pub_roles` VALUES ('2', 'ROLE_USER', '普通用户', '1', '0');

5、pub_roles_authorities

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:30:36
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_roles_authorities
-- ----------------------------
CREATE TABLE `pub_roles_authorities` (
`id` varchar(32) COLLATE utf8_bin NOT NULL,
`role_id` varchar(32) COLLATE utf8_bin NOT NULL,
`authority_id` varchar(32) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_aut_role` (`role_id`),
KEY `fk_aut` (`authority_id`),
CONSTRAINT `fk_aut` FOREIGN KEY (`authority_id`) REFERENCES `pub_authorities` (`authority_id`),
CONSTRAINT `fk_aut_role` FOREIGN KEY (`role_id`) REFERENCES `pub_roles` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_roles_authorities` VALUES ('1', '1', '1');
INSERT INTO `pub_roles_authorities` VALUES ('2', '1', '2');
INSERT INTO `pub_roles_authorities` VALUES ('3', '2', '2');

6、pub_users

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:30:42
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_users
-- ----------------------------
CREATE TABLE `pub_users` (
`user_id` varchar(32) COLLATE utf8_bin NOT NULL,
`user_account` varchar(30) COLLATE utf8_bin NOT NULL,
`user_name` varchar(40) COLLATE utf8_bin NOT NULL,
`user_password` varchar(100) COLLATE utf8_bin NOT NULL,
`enabled` int(10) NOT NULL,
`issys` int(10) NOT NULL,
`user_desc` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '用户表',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_users` VALUES ('1', 'admin', 'admin', '21232f297a57a5a743894a0e4a801fc3', '1', '1', '超级管理员');
INSERT INTO `pub_users` VALUES ('2', 'user', 'user', 'ee11cbb19052e40b07aac0ca060c23ee', '1', '0', '普通用户');

7、pub_users_roles

/*
MySQL Data Transfer
Source Host: localhost
Source Database: wisdom
Target Host: localhost
Target Database: wisdom
Date: 2012/10/19 9:30:48
*/

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pub_users_roles
-- ----------------------------
CREATE TABLE `pub_users_roles` (
`id` varchar(32) CHARACTER SET utf8 NOT NULL,
`user_id` varchar(32) COLLATE utf8_bin NOT NULL,
`role_id` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '角色和用户中间表',
PRIMARY KEY (`id`),
KEY `fk_user` (`user_id`),
KEY `fk_role` (`role_id`),
CONSTRAINT `fk_role` FOREIGN KEY (`role_id`) REFERENCES `pub_roles` (`role_id`),
CONSTRAINT `fk_user` FOREIGN KEY (`user_id`) REFERENCES `pub_users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `pub_users_roles` VALUES ('1', '1', '1');
INSERT INTO `pub_users_roles` VALUES ('2', '2', '2');



在最开始我运行的时候,我pub_authoritys表中存储的是AUTH_***的东西,但是好像spring security只认识ROLE_为前缀的权限,只有是ROLE_为前缀的它才能做比较,所以有一个地方需要注意,CustomInvocationSecurityMetadataSourceImpl.loadResourceDefine()方法中的resourceMap中的URL对于的权限必须是ROLE_为前缀的数据!如果你不确定的话,可以在方法在吧resourceMap的值打印出来看看!
在这里我提供下几个类中做用到的查询方法的SQL,这些都是基于myibatis的SQL:

1、getAuthoritiesByUsername(String username)

select b.authority_name
from
PUB_ROLES a,
PUB_AUTHORITIES b,
PUB_ROLES_AUTHORITIES c
where c.role_id = a.role_id and c.authority_id = b.authority_id and a.role_name in
(select role_name from
PUB_USERS_ROLES ur,
PUB_USERS u,
PUB_ROLES r
where ur.user_id = u.user_id and ur.role_id = r.role_id and u.user_name = #{username}
)


2、getUser(String username)

select * from
PUB_USERS u
where u.user_name = #{username}


3、getResourcesByAuthName(String authName)

select b.resource_string from PUB_AUTHORITIES_RESOURCES a ,PUB_RESOURCES b, PUB_AUTHORITIES c
where a.resource_id = b.resource_id and a.authority_id = c.authority_id and c.authority_name = #{authName}


4、getAllAuthoritys()

select * from pub_authorities
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值