经过Spring Security官方文档及相关资料的学习,自己做了一个简单的用户登录模块,现在将自己写的代码整理处理,以供大家参考或交流,如有不对,请指正错误。
1、库表建立
1)用户表
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(10) NOT NULL auto_increment,
`login_name` varchar(20) default NULL,
`password` varchar(20) default NULL,
`name` varchar(20) default NULL,
`email` varchar(30) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2)角色表
DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (
`id` int(10) NOT NULL auto_increment,
`name` varchar(20) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3)权限表
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`id` int(10) NOT NULL auto_increment,
`name` varchar(20) default NULL,
`display_name` varchar(20) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4)用户-角色表
DROP TABLE IF EXISTS `users_roles`;
CREATE TABLE `users_roles` (
`user_id` int(10) NOT NULL,
`role_id` int(10) NOT NULL,
PRIMARY KEY (`user_id`,`role_id`),
KEY `FK_R_2` (`role_id`),
CONSTRAINT `FK_R_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
CONSTRAINT `FK_R_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`)
5)角色-权限表
DROP TABLE IF EXISTS `roles_authorities`;
CREATE TABLE `roles_authorities` (
`role_id` int(10) NOT NULL,
`authority_id` int(10) NOT NULL,
PRIMARY KEY (`role_id`,`authority_id`),
KEY `FK_R_4` (`authority_id`),
CONSTRAINT `FK_R_3` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`),
CONSTRAINT `FK_R_4` FOREIGN KEY (`authority_id`) REFERENCES `authorities` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、建立Pojo类
1)用户
package cn.com.sunjiesh.springmvcdemo.entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
/**
* 统一定义id的entity基类.
*
* @author calvin
*/
@MappedSuperclass
public class IdEntity {
private Long id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
package cn.com.sunjiesh.springmvcdemo.entity.user;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.springside.modules.utils.CollectionUtils;
import cn.com.sunjiesh.springmvcdemo.entity.IdEntity;
/**
* 用户.
*
* 注意@Cache(Entity与集合的缓存),@ManyToMany/@JoinTable(多对多关系),@OrderBy/LinkedHashSet(集合排序),@Transient(非持久化属性)的应用.
*
* @author calvin
*/
@Entity
@Table(name = "USERS")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User extends IdEntity {
private String loginName;
private String password;
private String name;
private String email;
private Set<Role> roles = new LinkedHashSet<Role>();
@Column(name="login_name")
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
@Column(name="password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name="email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE },fetch=FetchType.EAGER)
@JoinTable(name = "USERS_ROLES", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
@OrderBy("id")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@Transient
public String getRoleNames() throws Exception {
return CollectionUtils.fetchPropertyToString(roles, "name", ", ");
}
@SuppressWarnings("unchecked")
@Transient
public List<Long> getRoleIds() throws Exception {
return CollectionUtils.fetchPropertyToList(roles, "id");
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
2)角色
package cn.com.sunjiesh.springmvcdemo.entity.user;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.springside.modules.utils.CollectionUtils;
import cn.com.sunjiesh.springmvcdemo.entity.IdEntity;
/**
* 角色.
*
* 注意@Cache(Entity与集合的缓存),@ManyToMany/@JoinTable(多对多关系),@OrderBy/LinkedHashSet(集合排序),@Transient(非持久化属性)的应用.
*
* @author calvin
*/
@Entity
@Table(name = "ROLES")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Role extends IdEntity {
private String name;
private Set<Authority> auths = new LinkedHashSet<Authority>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE },fetch=FetchType.EAGER)
@JoinTable(name = "ROLES_AUTHORITIES", joinColumns = { @JoinColumn(name = "ROLE_ID") }, inverseJoinColumns = { @JoinColumn(name = "AUTHORITY_ID") })
@OrderBy("id")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public Set<Authority> getAuths() {
return auths;
}
public void setAuths(Set<Authority> auths) {
this.auths = auths;
}
@Transient
public String getAuthNames() throws Exception {
return CollectionUtils.fetchPropertyToString(auths, "displayName", ", ");
}
@SuppressWarnings("unchecked")
@Transient
public List<Long> getAuthIds() throws Exception {
return CollectionUtils.fetchPropertyToList(auths, "id");
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
3)权限
package cn.com.sunjiesh.springmvcdemo.entity.user;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import cn.com.sunjiesh.springmvcdemo.entity.IdEntity;
/**
* 权限.
*
* @Cache使用READ_ONLY策略.
*
* @author calvin
*/
@Entity
@Table(name = "AUTHORITIES")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Authority extends IdEntity {
private String name;
private String displayName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name="display_name")
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
注意:@ManyToMany中的fetch=FetchType.EAGER。如果fetch=FetchType.LAZY会报异常。
3、建立DAO类
1)接口
package cn.com.sunjiesh.springmvcdemo.dao.iface;
import cn.com.sunjiesh.springmvcdemo.entity.user.User;
/**
*
* @author sunjie
* @since 2009-03-29
*
*/
public interface IUserDAO extends IBaseHibernateDAO<User> {
public User getUserByUserName(String username);
}
2)实现类
package cn.com.sunjiesh.springmvcdemo.dao.impl;
import org.apache.log4j.Logger;
import org.hibernate.Query;
import cn.com.sunjiesh.springmvcdemo.dao.iface.IUserDAO;
import cn.com.sunjiesh.springmvcdemo.entity.user.User;
public class UserDAOImpl extends BaseHibernateDAOImpl<User> implements IUserDAO {
private static Logger LOG = Logger.getLogger(UserDAOImpl.class);
public User getUserByUserName(String username) {
String hql = "from User where loginName = ?";
Query query = getSession().createQuery(hql);
query.setParameter(0, username);
User user = (User) query.list().get(0);
return user;
}
}
4、Spring Security相关代码
package cn.com.sunjiesh.springmvcdemo.service.security;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import cn.com.sunjiesh.springmvcdemo.dao.iface.IUserDAO;
import cn.com.sunjiesh.springmvcdemo.entity.user.Authority;
import cn.com.sunjiesh.springmvcdemo.entity.user.Role;
import cn.com.sunjiesh.springmvcdemo.entity.user.User;
/**
* 实现SpringSecurity的UserDetailsService接口,获取用户Detail信息.
* @author calvin
*/
public class UserDetailServiceImpl implements UserDetailsService{
private static Logger LOG=Logger.getLogger(UserDetailServiceImpl.class);
private IUserDAO userDao;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
if(username==null){
LOG.error("username is null");
}
if(userDao==null){
LOG.error("userDao is null");
}
User user=userDao.getUserByUserName(username);
//TOTO 判断用户存在,如果不存在,则抛出异常。
if(user==null){
LOG.error(username+" is not exist", new UsernameNotFoundException(username+" is not exist"));
}
List<GrantedAuthority> authsList = new ArrayList<GrantedAuthority>();
System.out.println("user.getRoles().size()="+user.getRoles().size());
for (Role role : user.getRoles()) {
for (Authority authority : role.getAuths()) {
authsList.add(new GrantedAuthorityImpl(authority.getName()));
}
}
//TODO
org.springframework.security.userdetails.User userdetail = new org.springframework.security.userdetails.User(
user.getLoginName(), user.getPassword(), true, true, true, true, authsList
.toArray(new GrantedAuthority[authsList.size()]));
return userdetail;
}
public void setUserDao(IUserDAO userDao) {
this.userDao = userDao;
}
}
5、配置文件
1)web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name> SpringMVCDemo</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/cn/com/sunjiesh/springmvcdemo/spring/spring-*.xml,/WEB-INF/springmvcdemo-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring Security可以限制一个主体并行认证到同一系统的次数。 很多ISV利用这点来加强授权公里,网管也喜欢这个功能,因为它可以防止人们共享登录名。 你可以,比如,禁止用户"Batman"从两个不同的会话登录到web应用里。 --> <listener> <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class> </listener> <!--Character Encoding Convert--> <filter> <filter-name>encodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>gb2312</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <!-- Spring Security Filter--> <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> <!-- Use DWR --> <servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> <!-- Spring MVC Dispatcher --> <servlet> <servlet-name>springmvcdemo</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/springmvcdemo-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvcdemo</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- JSTL Configuration --> <jsp-config> <taglib> <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri> <taglib-location>/WEB-INF/tlds/fmt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/fmt-rt</taglib-uri> <taglib-location>/WEB-INF/tlds/fmt-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/core</taglib-uri> <taglib-location>/WEB-INF/tlds/c.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/core-rt</taglib-uri> <taglib-location>/WEB-INF/tlds/c-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/sql</taglib-uri> <taglib-location>/WEB-INF/tlds/sql.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/sql-rt</taglib-uri> <taglib-location>/WEB-INF/tlds/sql-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/x</taglib-uri> <taglib-location>/WEB-INF/tlds/x.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/x-rt</taglib-uri> <taglib-location>/WEB-INF/tlds/x-rt.tld</taglib-location> </taglib> </jsp-config> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
2)spring-base.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:/cn/com/sunjiesh/springmvcdemo/spring/jdbc.properties</value> </list> </property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- Hibernate Annotation Entity --> <property name="annotatedClasses"> <list> <value>cn.com.sunjiesh.springmvcdemo.entity.user.Authority</value> <value>cn.com.sunjiesh.springmvcdemo.entity.user.Role</value> <value>cn.com.sunjiesh.springmvcdemo.entity.user.User</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.cache.use_query_cache">false</prop> </props> </property> </bean> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="baseTxService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> <property name="transactionManager" ref="txManager" /> <property name="proxyTargetClass" value="true" /> <property name="transactionAttributes"> <props> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="remove*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
3)spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <!-- Author By Sun Jie Shared document --> <beans default-autowire="byName" default-lazy-init="true"> <!-- Add By Sun Jie --> <bean id="userDaoTarget" class="cn.com.sunjiesh.springmvcdemo.dao.impl.UserDAOImpl"> </bean> <!-- End Edit --> <!-- Add By Sun Jie --> <bean id="userDao" parent="baseTxService"> <property name="target"> <ref bean="userDaoTarget"/> </property> </bean> <!-- End Edit --> </beans>
4)spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd"> <!-- FilterChainProxy会按顺序来调用这些filter,使这些filter能享用Spring Ioc的功能 --> <bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy"> <security:filter-chain-map path-type="ant"> <security:filter-chain pattern="/user/**" filters="httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter" /> </security:filter-chain-map> </bean> <!-- 集成过滤器(HttpSessionContextIntegrationFilter是集成过滤器的一个实现) 每次request前HttpSessionContextIntegrationFilter从Session中获取Authentication对象,在request完后 又把Authentication对象保存到Session中供下次request使用,此filter必须在其他Acegi filter前使用 --> <bean id="httpSessionContextIntegrationFilter" class="org.springframework.security.context.HttpSessionContextIntegrationFilter" /> <!-- 退出(Logout)过滤器 退出登录操作 --> <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter"> <!-- 退出系统后系统跳转到此URL --> <constructor-arg value="/login.action" /> <!-- 退出系统后的操作(调用logout方法) --> <constructor-arg> <list> <!-- 实现了LogoutHandler接口(logout方法) --> <ref bean="rememberMeServices" /> <bean class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" /> </list> </constructor-arg> </bean> <!-- 处理表单认证filter: 1.authenticationManager 认证管理器 2.authenticationFailureUrl 定义登录失败时转向的页面 3.defaultTargetUrl 定义登录成功时转向的页面 4.filterProcessesUrl 定义登录请求的地址 5.rememberMeServices 在验证成功后添加cookie信息 --> <bean id="authenticationProcessingFilter" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"></property> <property name="authenticationFailureUrl" value="/user/login.jsp"></property> <property name="defaultTargetUrl" value="/user/index.jsp"></property> <property name="filterProcessesUrl" value="/user/j_spring_security_check"></property> <property name="rememberMeServices" ref="rememberMeServices"></property> </bean> <!-- 基本认证机制处理 --> <bean id="basicProcessingFilter" class="org.springframework.security.ui.basicauth.BasicProcessingFilter"> <property name="authenticationManager"> <ref bean="authenticationManager" /> </property> <property name="authenticationEntryPoint"> <bean id="authenticationEntryPoint" class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint"> <property name="realmName"> <value>Name Of Your Realm</value> </property> </bean> </property> </bean> <bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter" /> <!-- 如果不存在任何授权信息时,自动添加匿名用户身份至SecurityContextHolder中 --> <bean id="anonymousProcessingFilter" class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter"> <property name="key" value="springsecurity"></property> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"></property> </bean> <!-- 异常处理filter(异常转换过滤器),主要是处理AccessDeniedException和AuthenticationException, 将给每个异常找到合适的"去向" --> <bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint" /> <property name="accessDeniedHandler"> <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl"> <property name="errorPage" value="/accessDenied.jsp" /> </bean> </property> </bean> <!-- 使用过滤器安全拦截器保护资源 filterSecurityInterceptor在执行转向目标url前检查objectDefinitionSource中设定的用户权限信息, 安全强制过滤器负责拦截请求,判断请求是否安全,并且给予认证和访问决策管理器一个机会来验证用户的身份和权限 过程: 首先,过滤器安全拦截器使用authenticationManager调用自己的provider来对用户的认证信息进行验证并获取用户已有的权限。 然后,使用访问决策管理器来判断用户是否拥用合适的授权来访问受保护的资源。 (objectDefinitionSource属性定义了访问URL需要的权限信息) 最后,有投票者根据用户持有认证和访问url需要的属性,调用自己的voter来投票,决定是否允许访问。 --> <bean id="filterSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager"></property> <property name="accessDecisionManager" ref="accessDecisionManager"></property> <!-- <property name="objectDefinitionSource" ref="objectDefinitionSource"></property> --> <property name="objectDefinitionSource"> <security:filter-invocation-definition-source> <security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE" /> <security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER" /> <security:intercept-url pattern="/login.action*" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <security:intercept-url pattern="/user/user!save.action*" access="ROLE_MODIFY_USER" /> <security:intercept-url pattern="/user/user!delete.action*" access="ROLE_MODIFY_USER" /> <security:intercept-url pattern="/user/user*.action*" access="ROLE_VIEW_USER" /> <security:intercept-url pattern="/user/role!save.action*" access="ROLE_MODIFY_ROLE" /> <security:intercept-url pattern="/user/role!delete.action*" access="ROLE_MODIFY_ROLE" /> <security:intercept-url pattern="/user/role*.action*" access="ROLE_VIEW_ROLE" /> </security:filter-invocation-definition-source> </property> </bean> <!-- 访问决策管理器 验证用户是否有权限访问相应的资源(filterSecurityInterceptor中objectDefinitionSource属性定义的访问URL需要的属性信息) --> <bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased" p:allowIfAllAbstainDecisions="false"> <property name="decisionVoters"> <list> <bean class="org.springframework.security.vote.RoleVoter" /> <bean class="org.springframework.security.vote.AuthenticatedVoter" /> </list> </property> </bean> <bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl" value="/login.jsp" /> <property name="forceHttps" value="false" /> </bean> <!-- 认证管理器(org.springframework.security.AuthenticationManager接口) org.springframework.security.providers.ProviderManager是认证管理器的一个实现, ProviderManager通过遍历一个提供者的集合来实现身份验证, 直到某一个认证提供者能够成功地验证该用户的身份 --> <!-- 通过Providers提供认证者列表,如果一个认证提供者失败可以尝试另外一个认证提供者,以保证获取不同来源的身份认证,如 DaoAuthenticationProvider 从数据库中读取用户信息验证身份 AnonymousAuthenticationProvider 匿名用户身份认证 RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证 其它的还有 AuthByAdapterProvider 使用容器的适配器验证身份 CasAuthenticationProvider 根据Yale中心认证服务验证身份, 用于实现单点登陆 JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份 RemoteAuthenticationProvider 根据远程服务验证用户身份 RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证 X509AuthenticationProvider 从X509认证中获取用户信息验证身份 TestingAuthenticationProvider 单元测试时使用 每个认证者会对自己指定的证明信息进行认证,如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。 --> <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager"> <property name="providers"> <list> <ref local="daoAuthenticationProvider" /> <ref local="anonymousAuthenticationProvider" /> </list> </property> <!-- <property name="sessionController" ref="concurrentSessionController" /> --> </bean> <bean id="daoAuthenticationProvider" class="org.springframework.security.providers.dao.DaoAuthenticationProvider"> <!-- <property name="passwordEncoder" ref="passwordEncoder"></property>--> <property name="userDetailsService" ref="userDetailsService"></property> </bean> <bean id="anonymousAuthenticationProvider" class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider"> <property name="key" value="springsecurity"></property> </bean> <!-- RemeberMeServices --> <bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices"> <property name="key" value="springsecurity"></property> <property name="userDetailsService" ref="userDetailsService"></property> </bean> <bean id="userDetailsService" class="cn.com.sunjiesh.springmvcdemo.service.security.UserDetailServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="passwordEncoder" class="org.springframework.security.providers.encoding.Md5PasswordEncoder" /> <bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" /> </beans>
5)springmvcdemo-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Author Sun Jie --> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Multi-Action--> <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <property name="paramName" value="method" /> <property name="defaultMethodName" value="index" /> </bean> <!-- View Resolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/" /> </bean> <bean id="adminHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <!-- Add By Sun Jie --> <prop key="/user/login.do">userLoginController</prop> <!-- Add By Sun Jie --> </props> </property> </bean> <bean id="userLoginController" class="cn.com.sunjiesh.springmvcdemo.web.user.LoginController" autowire="byName"> <property name="successView" value="/user/index.jsp" /> <property name="errorView" value="" /> </bean> </beans>
6、页面
<%@ page contentType="text/html;charset=gb2312"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%@ page import="org.springframework.security.ui.AbstractProcessingFilter"%>
<%@ page import="org.springframework.security.ui.webapp.AuthenticationProcessingFilter"%>
<%@ page import="org.springframework.security.AuthenticationException"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SpringMVCDemo登录页</title>
<%@ include file="/common/meta.jsp"%>
<script src="${ctx}/js/validate/jquery.validate.js" type="text/javascript"></script>
<script src="${ctx}/js/validate/messages_cn.js" type="text/javascript"></script>
<script>
$(document).ready(function(){
$("#loginForm").validate();
});
</script>
</head>
<body>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
<div id="content">
<%
if (session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY) != null) {
%>
<span style="color:red"> 登录失败,请重试. </span>
<%
}
%>
<h2>SpringMVCDemo示例</h2>
<h3>--CRUD管理界面演示</h3>
<form id="loginForm" action="${ctx}/user/j_spring_security_check" method="post">
<table class="inputView">
<tr>
<td>
用户名:
</td>
<td>
<input type='text' name='j_username'
<c:if test="${not empty param.error}"> value='<%=session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY)%>'</c:if> class="required"/>
</td>
</tr>
<tr>
<td>
密码:
</td>
<td>
<input type='password' name='j_password' class="required" />
</td>
</tr>
<tr>
<td colspan='2'>
<input value="登录" type="submit" />
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
备注:登录页面上的表单action与spring-security中authenticationProcessingFilter过滤器中的filterProcessesUrl相对应。用户名与输入框的name属性值分别是j_username与j_password。