spring security3进级篇III

      在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设计的数据库


猜你喜欢

转载自gaojiewyh.iteye.com/blog/1225251
今日推荐