在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、其他页面保持不变,进行访问页面如下:
总结: 这种方法虽然将用户、角色、资源存放到数据库中,但spring security 中<sec:authorize url> tag 的隐藏功能消失,这需要自己去定义类似标签去自行控制。
要想在真正在企业中做到很好的控制,其设计会更复杂,下面是基于RBAC设计的数据库