在spring security3进级篇II中,虽然将用户和权限存入到数据库中,但在配置文件中仍然要对URL地址配置角色进行控制访问,如何将用户,角色,资源存放到数据库中,进行统一管理,逐步实现RBAC的模型呢,这需要更复杂的实现,这一篇将实现将所有的信息存储到数据库中,但不涉及组,许可等表。
1、首先建立数据表
CREATE DATABASE IF NOT EXISTS spring_securityiii;
USE spring_securityiii;
--
-- Definition of table `pub_resources`
--
DROP TABLE IF EXISTS `pub_resources`;
CREATE TABLE `pub_resources` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`resource_name` varchar(50) NOT NULL,
`resource_type` varchar(50) NOT NULL,
`resource_string` varchar(200) NOT NULL,
`resource_enabled` tinyint(1) NOT NULL,
`resource_desc` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_pub_resource` (`resource_name`)
) ENGINE=InnoDB AUTO_INCREMENT=402 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `pub_resources`
--
/*!40000 ALTER TABLE `pub_resources` DISABLE KEYS */;
INSERT INTO `pub_resources` (`id`,`resource_name`,`resource_type`,`resource_string`,`resource_enabled`,`resource_desc`) VALUES
(400,'index页面','url','/index.*',1,'index页面'),
(401,'admin页面','url','/admin.*',1,'admin页面');
/*!40000 ALTER TABLE `pub_resources` ENABLE KEYS */;
--
-- Definition of table `pub_roles`
--
DROP TABLE IF EXISTS `pub_roles`;
CREATE TABLE `pub_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_name` varchar(100) NOT NULL,
`role_enabled` tinyint(1) NOT NULL,
`role_desc` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_pub_role` (`role_name`)
) ENGINE=InnoDB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `pub_roles`
--
/*!40000 ALTER TABLE `pub_roles` DISABLE KEYS */;
INSERT INTO `pub_roles` (`id`,`role_name`,`role_enabled`,`role_desc`) VALUES
(200,'ROLE_ADMIN',1,'管理员角色'),
(201,'ROLE_USER',1,'普通用户角色');
/*!40000 ALTER TABLE `pub_roles` ENABLE KEYS */;
--
-- Definition of table `pub_roles_resources`
--
DROP TABLE IF EXISTS `pub_roles_resources`;
CREATE TABLE `pub_roles_resources` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) NOT NULL,
`resource_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `roles_resources_ibfk_1` (`role_id`),
KEY `roles_resources_ibfk_2` (`resource_id`),
CONSTRAINT `roles_resources_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `pub_roles` (`id`),
CONSTRAINT `roles_resources_ibfk_2` FOREIGN KEY (`resource_id`) REFERENCES `pub_resources` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=503 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `pub_roles_resources`
--
/*!40000 ALTER TABLE `pub_roles_resources` DISABLE KEYS */;
INSERT INTO `pub_roles_resources` (`id`,`role_id`,`resource_id`) VALUES
(500,200,400),
(501,200,401),
(502,201,400);
/*!40000 ALTER TABLE `pub_roles_resources` ENABLE KEYS */;
--
-- Definition of table `pub_users`
--
DROP TABLE IF EXISTS `pub_users`;
CREATE TABLE `pub_users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_account` varchar(50) NOT NULL,
`user_password` varchar(20) NOT NULL,
`user_enabled` tinyint(1) NOT NULL,
`user_desc` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_pub_user` (`user_account`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `pub_users`
--
/*!40000 ALTER TABLE `pub_users` DISABLE KEYS */;
INSERT INTO `pub_users` (`id`,`user_account`,`user_password`,`user_enabled`,`user_desc`) VALUES
(100,'admin','admin',1,'管理员'),
(101,'user','user',1,'普通用户');
/*!40000 ALTER TABLE `pub_users` ENABLE KEYS */;
--
-- Definition of table `pub_users_roles`
--
DROP TABLE IF EXISTS `pub_users_roles`;
CREATE TABLE `pub_users_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
`ur_enabled` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `users_roles_ibfk_1` (`user_id`),
KEY `users_roles_ibfk_2` (`role_id`),
CONSTRAINT `users_roles_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `pub_users` (`id`),
CONSTRAINT `users_roles_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `pub_roles` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=303 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `pub_users_roles`
--
/*!40000 ALTER TABLE `pub_users_roles` DISABLE KEYS */;
INSERT INTO `pub_users_roles` (`id`,`user_id`,`role_id`,`ur_enabled`) VALUES
(300,100,200,1),
(301,100,201,1),
(302,101,201,1);
/*!40000 ALTER TABLE `pub_users_roles` ENABLE KEYS */;
2、自定义实现spring security的四个类
package com.spring.security.service.impl;
import javax.annotation.Resource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.spring.security.dao.UserDao;
import com.spring.security.domain.User;
import com.spring.security.service.CustomUserDetailsService;
@Service("customUserDetailsService")
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
@Resource
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String userName)throws UsernameNotFoundException {
User user = userDao.findUserByName(userName);
if (user == null) {
throw new UsernameNotFoundException("用户名" + userName + "不存在");
}
// 因为User已经实现了UserDetails接口,所以直接返回user即可
return user;
}
}
package com.spring.security.service.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.stereotype.Service;
import com.spring.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支持安全对象的类型。
*/
@Service("customAccessDecisionManager")
public class CustomAccessDecisionManagerImpl implements CustomAccessDecisionManager {
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
*/
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();
// ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (needRole.trim().equals(ga.getAuthority().trim())) {
return;
}
}
}
throw new AccessDeniedException("Acess Denied");
}
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#supports(org.springframework.security.access.ConfigAttribute)
*/
public boolean supports(ConfigAttribute attribute) {
return true;
}
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#supports(java.lang.Class)
*/
public boolean supports(Class<?> clazz) {
return true;
}
}
package com.spring.security.service.impl;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import com.spring.security.service.CustomFilterSecurityInterceptor;
/** */
/**
* 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
* securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。
* 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中,
* 供Spring Security使用,用于权限校验。
*
*/
@Service("customFilterSecurityInterceptor")
public class CustomFilterSecurityInterceptorImpl extends AbstractSecurityInterceptor implements CustomFilterSecurityInterceptor {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return this.securityMetadataSource;
}
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
}
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);
}
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void destroy() {
}
public void init(FilterConfig filterconfig) throws ServletException {
}
}
package com.spring.security.service.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import com.spring.security.dao.ResourceDao;
import com.spring.security.domain.Resource;
import com.spring.security.domain.Role;
import com.spring.security.service.CustomInvocationSecurityMetadataSource;
@Service("customInvocationSecurityMetadataSource")
public class CustomInvocationSecurityMetadataSourceImpl implements CustomInvocationSecurityMetadataSource {
@javax.annotation.Resource
private ResourceDao resourceDao;
private AntPathRequestMatcher pathMatcher;
private HashMap<String, Collection<ConfigAttribute>> resourceMap = null;
/**
* 自定义方法,这个类放入到Spring容器后,
* 指定init为初始化方法,从数据库中读取资源
*
*/
@PostConstruct
public void init(){
this.resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
for (Resource item : resourceDao.getAllResource()) {
resourceMap.put(item.getResource_string(), listToCollection(item.getRoles()));
}
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
for (Map.Entry<String, Collection<ConfigAttribute>> entry : resourceMap.entrySet()) {
allAttributes.addAll(entry.getValue());
}
return allAttributes;
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object)throws IllegalArgumentException {
HttpServletRequest request = ((FilterInvocation) object).getRequest();
System.out.println("requestUrl is " + request.getRequestURI());
if (resourceMap == null) {
this.loadAllResourcesAndAuthorities();
}
Iterator<String> it = resourceMap.keySet().iterator();
while (it.hasNext()) {
String resURL = it.next();
pathMatcher = new AntPathRequestMatcher(resURL);
if (pathMatcher.matches(request)) {
Collection<ConfigAttribute> returnCollection = resourceMap.get(resURL);
return returnCollection;
}
}
return null;
}
@Override
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
/**
* 自定义方法,将List<Role>集合转换为框架需要的Collection<ConfigAttribute>集合
*
* @param roles
* @return
*/
private Collection<ConfigAttribute> listToCollection(List<Role> roles) {
List<ConfigAttribute> list = new ArrayList<ConfigAttribute>();
for (Role role : roles) {
list.add(new SecurityConfig(role.getRole_name()));
}
return list;
}
/**
* 加载所有资源与权限的关系
*/
private void loadAllResourcesAndAuthorities() {
if (resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
}
List<Resource> resources = this.resourceDao.getAllResource();
for (Resource resource : resources) {
resourceMap.put(resource.getResource_string(),listToCollection(resource.getRoles()));
}
}
}
3、定义用户,角色,资源的类
package com.spring.security.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Resources
*/
public class Resource implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String resource_name;
private String resource_type;
private String resource_string;
private String resource_enabled;
private String resource_desc;
private List<Role> roles = new ArrayList<Role>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getResource_name() {
return resource_name;
}
public void setResource_name(String resource_name) {
this.resource_name = resource_name;
}
public String getResource_type() {
return resource_type;
}
public void setResource_type(String resource_type) {
this.resource_type = resource_type;
}
public String getResource_string() {
return resource_string;
}
public void setResource_string(String resource_string) {
this.resource_string = resource_string;
}
public String getResource_enabled() {
return resource_enabled;
}
public void setResource_enabled(String resource_enabled) {
this.resource_enabled = resource_enabled;
}
public String getResource_desc() {
return resource_desc;
}
public void setResource_desc(String resource_desc) {
this.resource_desc = resource_desc;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
package com.spring.security.domain;
import java.io.Serializable;
/**
* Roles.
*/
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String role_name;
private Integer role_enabled;
private String role_desc;
public Role() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public Integer getRole_enabled() {
return role_enabled;
}
public void setRole_enabled(Integer role_enabled) {
this.role_enabled = role_enabled;
}
public String getRole_desc() {
return role_desc;
}
public void setRole_desc(String role_desc) {
this.role_desc = role_desc;
}
}
package com.spring.security.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String user_account;
private Integer user_enabled;
private String user_password;
private String user_desc;
private List<Role> roles = new ArrayList<Role>();
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUser_account() {
return user_account;
}
public void setUser_account(String user_account) {
this.user_account = user_account;
}
public Integer getUser_enabled() {
return user_enabled;
}
public void setUser_enabled(Integer user_enabled) {
this.user_enabled = user_enabled;
}
public String getUser_password() {
return user_password;
}
public void setUser_password(String user_password) {
this.user_password = user_password;
}
public String getUser_desc() {
return user_desc;
}
public void setUser_desc(String user_desc) {
this.user_desc = user_desc;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
/*
* 获取用户权限集合,权限使用GrantedAuthority接口表示,框架中有它的实现类
* GrantedAuthorityImpl,只需要把角色的名称放入即可 (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getAuthorities
* ()
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
for (Role role : roles) {
list.add(new SimpleGrantedAuthority(role.getRole_name()));
}
return list;
}
/*
* 获取用户名 (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getUsername()
*/
@Override
public String getUsername() {
// TODO Auto-generated method stub
return this.user_account;
}
/*
* 用户密码 (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#getPassword()
*/
@Override
public String getPassword() {
// TODO Auto-generated method stub
return this.user_password;
}
/*
* 直接返回true,表示没有过期 (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#isAccountNonExpired
* ()
*/
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
/*
* 直接返回true,表示没有锁定 (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#isAccountNonLocked
* ()
*/
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
/*
* 是否禁用 (non-Javadoc)
*
* @see
* org.springframework.security.core.userdetails.UserDetails#isEnabled()
*/
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
}
4 用户,资源,角色DAo类的实现
package com.spring.security.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.spring.security.dao.UserDao;
import com.spring.security.dao.support.SimpleDaoSupport;
import com.spring.security.domain.Role;
import com.spring.security.domain.User;
@Repository("userDao")
public class UserDaoImpl extends SimpleDaoSupport implements UserDao {
@Override
public User findUserByName(String userName) {
String sql = "SELECT id,user_account,user_enabled,user_password,user_desc FROM pub_users where user_account=?";
return this.getSimpleJdbcTemplate().queryForObject(sql,
new UserMapper(), userName);
}
/**
* 获取User对象的role列表
*
* @param userID
* @return RoleList
*/
public List<Role> getRolesByUserID(String userName) {
String sql = "SELECT r.id,r.role_name,r.role_enabled,r.role_desc FROM pub_users u,pub_roles r,pub_users_roles ur "
+ "WHERE u.id=ur.user_id AND r.id=ur.role_id AND u.user_account=?";
return this.getSimpleJdbcTemplate().query(sql,
BeanPropertyRowMapper.newInstance(Role.class), userName);
}
/**
* 定义UserMapper
*/
protected class UserMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setUser_account(rs.getString("user_account"));
user.setUser_password(rs.getString("user_password"));
user.setUser_enabled(rs.getInt("user_enabled"));
user.setUser_desc(rs.getString("user_desc"));
// 调用上面的方法获取用户所有的权限
user.setRoles(getRolesByUserID(rs.getString("user_account")));
return user;
}
}
}
package com.spring.security.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.spring.security.dao.ResourceDao;
import com.spring.security.dao.support.SimpleDaoSupport;
import com.spring.security.domain.Resource;
import com.spring.security.domain.Role;
@Repository("resourceDao")
public class ResourceDaoImpl extends SimpleDaoSupport implements ResourceDao {
@Override
public List<Resource> getAllResource() {
List<Resource> list = null;
String sql = "SELECT * from pub_resources";
list = this.getSimpleJdbcTemplate().query(sql, new RowMapper<Resource>() {
public Resource mapRow(ResultSet rs, int arg1) throws SQLException {
Resource resource = new Resource();
resource.setId(rs.getInt("id"));
resource.setResource_name(rs.getString("resource_name"));
resource.setResource_type(rs.getString("resource_type"));
resource.setResource_string(rs.getString("resource_string"));
resource.setResource_enabled(rs.getString("resource_enabled"));
resource.setResource_desc(rs.getString("resource_desc"));
resource.setRoles(getRoleByResourceId(resource.getId()));
return resource;
}
});
return list;
}
private List<Role> getRoleByResourceId(int id) {
String sql = "SELECT r.id,r.role_name,r.role_enabled,r.role_desc FROM pub_roles r,pub_roles_resources rr WHERE rr.role_id=r.id AND rr.resource_id=?";
return this.getSimpleJdbcTemplate().query(sql, new RowMapper<Role>() {
public Role mapRow(ResultSet rs, int arg1) throws SQLException {
Role role = new Role();
role.setId(rs.getInt("id"));
role.setRole_name(rs.getString("role_name"));
role.setRole_enabled(rs.getInt("role_enabled"));
role.setRole_desc(rs.getString("role_desc"));
return role;
}
}, new Object[] { id });
}
}
5 spring security的配置如下:
<?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-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <global-method-security pre-post-annotations="enabled" /> <!--对登录页面不进行拦截,在页面后面加*表示,该页面后面可能会带一些参数 --> <http pattern="/login.jsp*" security="none" /> <http pattern="/common/**" security="none" /> <http pattern="/js/**" security="none" /> <!-- 保护应用程序配置一些列的权限问题,当没有权限403返回页面为403.jsp --> <http auto-config="true" access-denied-page="/common/403.jsp" use-expressions="true"> <!-- login-page: 指定登录页面,并指定默认的target访问地址index.jsp --> <form-login login-page="/login.jsp" default-target-url='/index.jsp' always-use-default-target='true' /> <!-- 配置用户退出的默认返回页面 --> <logout logout-success-url="/login.jsp" /> <!-- 会话管理配置 ,设置最多登录一次,二次登录会让第一次登录失效, 则设置error-if-maximum-exceeded为false,要求第一次有效设置为true --> <session-management invalid-session-url="/common/timeout.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> <!-- 配置自己的拦截器 --> <beans:bean id="customFilterSecurityInterceptor" class="com.spring.security.service.impl.CustomFilterSecurityInterceptorImpl"> <beans:property name="authenticationManager" ref="autheticationManager"/> <beans:property name="accessDecisionManager" ref="customAccessDecisionManager" /> <!-- resourceService在applicationContext.xml中定义 --> <beans:property name="securityMetadataSource" ref="customInvocationSecurityMetadataSource" /> </beans:bean> <!--配置认证管理器 --> <authentication-manager alias="autheticationManager"> <!-- 使用自定义UserDetailsService --> <authentication-provider user-service-ref="customUserDetailsService"> <password-encoder hash="md5"/> </authentication-provider> </authentication-manager> <beans:bean id="webPrivilegeEvaluator" class="org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator"> <beans:constructor-arg ref="customFilterSecurityInterceptor"/> </beans:bean> <!-- Jcaptcha相关的配置 --> <beans:bean id="captchaService" class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService"> <beans:property name="captchaEngine"> <beans:bean class="com.spring.security.jcaptcha.GMailEngine" /> </beans:property> <!-- 默认生成的图片180秒过期 , 可另行设置 --> <beans:property name="minGuarantedStorageDelayInSeconds" value="180" /> </beans:bean> </beans:beans>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>springSecurityIII</display-name> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.xml</param-value> </context-param> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> <!-- JCaptcha`s filter --> <filter> <filter-name>jcaptchaFilter</filter-name> <filter-class>com.spring.security.jcaptcha.JCaptchaFilter</filter-class> <init-param> <param-name>failureUrl</param-name> <param-value>/login.jsp</param-value> </init-param> </filter> <!-- jcaptcha图片生成URL. --> <filter-mapping> <filter-name>jcaptchaFilter</filter-name> <url-pattern>/jcaptcha.jpg</url-pattern> </filter-mapping> <!-- jcaptcha登录表单处理URL. 必须放在springSecurityFilter的filter-mapping定义之前 --> <filter-mapping> <filter-name>jcaptchaFilter</filter-name> <url-pattern>/j_spring_security_check</url-pattern> </filter-mapping> <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> <listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
6、其他页面保持不变,进行访问页面如下:
admin用户点击admin页面
切换user用户登入页面进入
点击admin页面,用户访问被拒绝
总结: 这种方法虽然将用户、角色、资源存放到数据库中,但spring security 中<sec:authorize url> tag 的隐藏功能消失,这需要自己去定义类似标签去自行控制。
要想在真正在企业中做到很好的控制,其设计会更复杂,下面是基于RBAC设计的数据库