Spring Security應用實例(一):用戶登錄

經過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。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章