spring security3.2配置---权限管理


之前已经在我的博客中发过security的执行流程图了,大家能够先去看看那个图再看这篇。今天我主要在这里贴出了security配置中的几个重要的类和两个xml配置文件,基本上控制权限的就是这几个文件了。由于近期都比較忙,一直没有时间发出来,导致有点忘记配置时的过程了,所以忘记了一些细节的内容,原本我打算写的具体一点的,但如今都有点忘记了,我在这里就不再一一写出来了,由于在每一个文件的方法或配置里,我用凝视说明了一些配置时所遇到的问题,大家能够看看,可能比較难看,由于表达可能不是非常好,有些写得比較具体,导致非常乱。假设大家有在网上搜索这类文章,基本上大多数配置都是差点儿相同的,这在此之前也在网上參考了几篇文章,都写的不错,我也是參考那里配置的。我给出我看过的几个网址出来,大家能够也去看看:

1、http://wenku.baidu.com/view/ba1f791aff00bed5b9f31dc1.html 我数据库是在这篇里取的

2、http://blog.csdn.net/k10509806/article/details/6436987

3、http://johnny-lee.iteye.com/blog/1701126 

4、http://blog.csdn.net/k10509806/article/details/6369131

依照第4篇的配置基本上都是能配置出来的


另一个须要说明的是,在applicationContext-security.xml里我凝视掉了

auto-config="true"

这个配置比較重要的,假设你不配置,security可能不会启动,我为什么又把它凝视掉了,是由于当你配置了自己主动义的登录页面,就不用这个了,这个的作用可能是在项目启动时,假设你没有自己定义的登录页面,它就会跳转到security默认的登录页面中。


我把我配置的项目放上来,大家能够去下载,我也就赚点积分,常在CSDN混,不能没有积分啊,望大家理解,下载地址:

http://download.csdn.net/detail/u011511684/7597101


事实上之前我也上传了一个上去了,可是那个配置好像不是非常完整。

我的项目是用maven搭建的,假设你配置了maven,那么就能够非常轻松的执行起项目来了,

步骤:

1、在我的项目下找到database目录,把里面的union_ssh.sql文件导入到mysql数据库中

2、导入项目SSHMS到myEclipse中

3、在myEclipse中使用maven install,执行后,可能会稍等一下,由于它在连网下载jar包,这样就不用自己去下载jar包了

4、执行后,在登录页面输入账号、passwordadmin就能够登录到主界面去了,这个账号的权限是能够訪问树中的全部页面;也能够使用账号、passwordtest登录,但这个账号权限仅仅能用户管理这个页面


web.xml

<?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_3_0.xsd" version="3.0">
  <display-name></display-name>
  
  <!-- spring配置文件位置 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring.xml,classpath:spring-hibernate.xml,classpath:applicationContext-security.xml</param-value>
	</context-param> 
	
   <!-- spring security 过滤器, 这个的位置顺序和spring的监听器启动位置没有什么关系,能够放在spring监听器的前面,也能够放置在后面。
    但一定要放在struts的过滤器前面,由于假设有自己定义的登录页面,当登录时,就会跳转到了struts相应的action中,
    导致无法使用spring security的验证登录了,正常情况下,应该登录时,会经过自己定义的MyUsernamePasswordAuthenticationFilter类的attemptAuthentication方法进行验证。
  假设验证成功,则登录成功,不再运行相应的action验证登录 ;spring security验证失败,则跳回指定登录失败的页面。  
   -->
  <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>	  

   <!-- spring监听器 -->
   <listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>	

  <!-- hibernate配置 -->
	<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
    <init-param>
      <param-name>singleSession</param-name>
      <param-value>true</param-value>
      </init-param>
    </filter>

  <!-- Struts2配置 -->
	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>
	<!-- hibernate的session启动过滤器,在url请求action时启动 ,不配置这个,url请求时无法启动hibernate的session-->
	 <filter-mapping>
        <filter-name>openSessionInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
	  <!-- struts拦截的url后缀 -->
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>*.action</url-pattern>
		<url-pattern>*.jsp</url-pattern>
		<url-pattern>*.html</url-pattern>
	</filter-mapping>

  <welcome-file-list>
    <welcome-file>/login.jsp</welcome-file>
  </welcome-file-list>
</web-app>


applicationContext-security.xml,security的主要配置文件

<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.xsd">
     
    <!--  No bean named 'springSecurityFilterChain' is defined   
    1、 这时公布一下你的项目,查看tomcat的webapps目录下,找到你的项目目录的classes目录有没有相关的spring.xml文件存在,不存在就会报错
    2、查看web.xml文件<param-value>标签有没有引入applicationContext-security.xml这个文件
        -->
     
      <!-- 不用经过spring security过滤,一般js、css都不须要过滤   -->
     
      <http pattern="/*/js/**" security="none"/> 
      <http pattern="/common/js/**" security="none"/>
      <http pattern="/login.jsp" security="none"/> 
       <http pattern="/login_logo.jpg" security="none"/>
      
      <!-- auto-config="true" -->  
      <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint" >
	     <!-- 不再在这里对url进行权限拦截,在数据库中取出url中相应的权限  
	       <intercept-url pattern="/**" access="ROLE_USER" />
	     -->
           <!-- 单用户登陆 -->
           <session-management>
              <concurrency-control max-sessions="1"
                error-if-maximum-exceeded="true" />
           </session-management>
      
 <!-- 这样的自己定义的登录页面,不能经过security的用户信息验证,也就等于不能取出用户的权限     
           <form-login login-page='/login.jsp' default-target-url="/index.jsp"/>
  -->          
            <!-- 尝试訪问没有权限的页面时跳转的页面 -->      
           <access-denied-handler error-page="/403.jsp"/>
          
           <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"  />   
             
           <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
   
           <!-- 检測失效的sessionId,session超时时,定位到另外一个URL -->  
            <session-management invalid-session-url="/sessionTimeOut.jsp" /> 
        <!-- 
            <custom-filter ref="logoutFilter" before="LOGOUT_FILTER"/>
             -->
             
             <logout invalidate-session="true"  logout-success-url="/"  logout-url="/logout"/>
	  </http>

         <!-- 登录验证器 -->  
    <beans:bean id="loginFilter"  
        class="framework.security.login.MyUsernamePasswordAuthenticationFilter">  
       
        <!-- value="/loginUser.action"处理登录表单的action ,value值要以“/”开关,否则会报错 :
        org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#3' while setting bean property 'sourceList' with key [3]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#3': Cannot resolve reference to bean 'loginFilter' while setting constructor argument with key [4]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginFilter' defined in class path resource [applicationContext-security.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'filterProcessesUrl' threw exception; nested exception is java.lang.IllegalArgumentException: userAction!login.action isn't a valid redirect URL        
        -->  
        <beans:property name="filterProcessesUrl" value="/user/loginUser.action"></beans:property>  
                <!-- 验证成功后的处理  -->   
        <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property>  
            
                <!-- 验证失败后的处理  --> 
        <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property>  
      
       
        <beans:property name="authenticationManager" ref="authenticationManager"></beans:property> 
                  
        <!-- 注入DAO为了查询相应的用户 -->  
        <beans:property name="userDao" ref="userDao"></beans:property>  
    </beans:bean>  
   
   
    <beans:bean id="loginLogAuthenticationSuccessHandler"  
        class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">  
        <beans:property name="defaultTargetUrl" value="/index.jsp"></beans:property>  
    </beans:bean>  
   
    <beans:bean id="simpleUrlAuthenticationFailureHandler"  
        class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">  
        <!-- 能够配置相应的跳转方式。属性forwardToDestination为true採用forward false为sendRedirect -->  
        <beans:property name="defaultFailureUrl" value="/login.jsp"></beans:property>  
    </beans:bean>  
   
      
    <!-- 认证过滤器 -->  
        <beans:bean id="myFilter"  class="framework.security.base.MyFilterSecurityInterceptor"> 
            
            <beans:property name="authenticationManager"  ref="authenticationManager" /> 
                             
            <beans:property name="accessDecisionManager"  ref="myAccessDecisionManagerBean" />    
                     
            <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" /> 
        </beans:bean>

        <!-- spring security提供的用户登录验证 ,alias的值相应上面的ref="authenticationManager" -->
        <authentication-manager alias="authenticationManager">     
            <!--userDetailServiceImpl 获取登录的用户、用户权限 -->          
            <authentication-provider user-service-ref="userDetailServiceImpl" />
            
        </authentication-manager>
    	  
       
        <!-- 获取登录的用户、用户权限 -->	   
	    <beans:bean id="userDetailServiceImpl" class="framework.security.base.MyUserDetailsService">
		    <beans:property name="userDao" ref="userDao"></beans:property>
	    </beans:bean>
	    
	    <!-- 推断是否有权限訪问请求的url页面 -->
 	    
	    <beans:bean id="myAccessDecisionManagerBean" 
	          class="framework.security.base.MyAccessDecisionManager">
	    </beans:bean>     
	    
	    <!-- 获取数据库中全部的url资源,读出url资源与权限的相应关系  -->    
	      
	    <beans:bean id="mySecurityMetadataSource"  
	          class="framework.security.base.MySecurityMetadataSource">
	    </beans:bean>  

        <!-- 未登录的切入点 -->  
    <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
        <beans:property name="loginFormUrl" value="/sessionTimeOut.jsp"></beans:property>  
    </beans:bean>
    
  
</beans:beans>


MySecurityMetadataSource类,负责读取数据库中的url相应的权限

package framework.security.base;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;


import com.user.dao.UserDaoI;

import framework.security.util.UrlPathMatcher;

//1、2、3、4是server启动时调用的顺序
public class MySecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource {
	
	/*
	*resourceMap用static声明了,为了避免用户每请求一次都要去数据库读取资源、权限,这里仅仅读取一次,将它保存起来
	*/
	private static Map<String, Collection<ConfigAttribute>> resourceMap = null; 
	
	private UrlPathMatcher urlMatcher = new UrlPathMatcher();
	
	private UserDaoI userDao;

	// 1
	//构造函数,由于server启动时会调用这个类,利用构造函数读取全部的url、角色
	public MySecurityMetadataSource() {   
	
		   //初始化,读取数据库全部的url、角色
	       loadResourceDefine();  
	}
	
	//2
	//这种方法应该是要从数据库读取数据的,这里仅仅用来測试
	/*private void loadResourceDefine() {  
		
		System.out.println("metadata : loadResourceDefine");
	      resourceMap = new HashMap<String, Collection<ConfigAttribute>>();           
		  Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();           
		
		*//**
		 * 将这里的new SecurityConfig("ROLE_ADMIN")值改为ROLE_USER,登录成功也不同意訪问index.jsp了,
		 * 由于在applicationContext-security.xml设置了仅仅同意角色为ROLE_ADMIN的訪问。
		 *  <intercept-url pattern="/**" access="ROLE_ADMIN" />
		 *//*
		  ConfigAttribute ca = new SecurityConfig("ROLE_ADMIN");           
		  atts.add(ca);           
		 
		  //ca为訪问的权限,以下为url地址赋予ca中的权限
		  resourceMap.put("/i.jsp", atts);   
		  resourceMap.put("/index.jsp", atts);          
	
    }*/
	
	//这种方法在url请求时才会调用,server启动时不会运行这种方法,前提是须要在<http>标签内设置  <custom-filter>标签
	//getAttributes这种方法会依据你的请求路径去获取这个路径应该是有哪些权限才干够去訪问。
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
	
		//object getRequestUrl 是获取用户请求的url地址		
		String url = ((FilterInvocation) object).getRequestUrl();   
		
		//resourceMap保存了loadResourceDefine方法载入进来的数据
		Iterator<String> ite = resourceMap.keySet().iterator();   
		
		while (ite.hasNext()) {     
		     
			 //取出resourceMap中读取数据库的url地址
			 String resURL = ite.next();   
		
			 //假设两个 url地址同样,那么将返回resourceMap中相应的权限集合,然后跳转到MyAccessDecisionManager类里的decide方法,再推断权限
			if (urlMatcher.pathMatchesUrl(url, resURL)) {     
			     return resourceMap.get(resURL);   //返回相应的url地址的权限 ,resourceMap是一个主键为地址,值为权限的集合对象
		    }  
		}
		
		//假设上面的两个url地址没有匹配,返回return null,不再调用MyAccessDecisionManager类里的decide方法进行权限验证,代表同意訪问页面
		return null;				
	}

	// 4
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
	
		return null;
	}
	
	// 3
	@Override 
	public boolean supports(Class<?> clazz) {
	
		return true;
		
	}
	
	private void loadResourceDefine() {

		//请注意这里读取了spring的xml配置文件,假设改变了spring的xml文件名,这里也要改变的
		ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"classpath:spring.xml","classpath:spring-hibernate.xml"});
		
		SessionFactory sessionFactory = (SessionFactory) context
                .getBean("sessionFactory");
		
		Session session=(Session) sessionFactory.openSession();
	
	 // 提取系统中的全部权限。
		List<String> auNames =session.createQuery("select roleName from PubRoles").list();
	
		session.close();//取数后将session关闭
		
		resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
		
          for (String auth : auNames) {
            ConfigAttribute ca = new SecurityConfig(auth);
             
            //查出相应的角色的资源
            Query query1=sessionFactory.openSession().createSQLQuery("SELECT resource_string FROM pub_resources WHERE resource_id IN (SELECT resource_id FROM pub_authorities_resources WHERE authority_id IN (SELECT authority_id FROM  pub_roles_authorities WHERE role_id =( SELECT role_id FROM pub_roles  WHERE role_name='"+auth+"')))");
            
    		List<String> list = query1.list();
    		
    		
                       
            for (String res : list) {
                String url = res;
                
                // * 推断资源文件和权限的相应关系,假设已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限添加�到权限集合中。                           
                if (resourceMap.containsKey(url)) {

                    Collection<ConfigAttribute> value = resourceMap.get(url); //取出这个url的权限集合
                    value.add(ca);
                    resourceMap.put(url, value);
                } else {
                    Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
                    atts.add(ca);
                    resourceMap.put(url, atts);
                }
            }
        }
	}

	public UserDaoI getUserDao() {
		return userDao;
	}

	public void setUserDao(UserDaoI userDao) {
		this.userDao = userDao;
	}
	
}

MyFilterSecurityInterceptor类,负责过滤url请求

package framework.security.base;

import java.io.IOException;

import javax.servlet.Filter;
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;

//implements Filter是servlet的filter类
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

	private FilterInvocationSecurityMetadataSource securityMetadataSource;
	
	@Override
	public void destroy() {
		// TODO Auto-generated method stub		
	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1,
			FilterChain arg2) throws IOException, ServletException {
		
		FilterInvocation fi = new FilterInvocation(arg0, arg1, arg2);    
		invoke(fi);
		
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Class<?> getSecureObjectClass() {
		return FilterInvocation.class;
	}

	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		return this.securityMetadataSource;		
	}
	
	//自己定义的方法
	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);  
		}  
	} 
   
	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
		return securityMetadataSource;
	}

	
	public void setSecurityMetadataSource(
			FilterInvocationSecurityMetadataSource securityMetadataSource) {

		this.securityMetadataSource = securityMetadataSource;
	}

}

MyUserDetailsService类,用户登录验证的类

package framework.security.base;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.memory.UserMap;


import com.user.dao.UserDaoI;
import com.user.dao.impl.UserDaoImpl;
import com.user.model.PubUsers;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.authority.GrantedAuthorityImpl;

public class MyUserDetailsService implements UserDetailsService{

	private UserDaoImpl userDao;
	

	
	@Override
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		
		    Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();  

		    PubUsers users=userDao.userInfo(username);
		/*    if(users.getAccount()==null){
		    	throw new AccessDeniedException("账号或password错误!");         
		    }*/
	 		
		    /*不要使用GrantedAuthorityImpl,官网说这个已过期了,
		     * SimpleGrantedAuthority取代GrantedAuthorityImpl,赋予一个角色(即权限)
		     * 
		     * */
		    List<String> list =  userDao.findAuthByUsername(username); 
	        for (int i = 0; i < list.size(); i++) {  
        
	        	auths.add(new SimpleGrantedAuthority(list.get(i)));
	        	//auths.add(new GrantedAuthorityImpl(list.get(i)));  
	            
	        }  
		    		    		   		    
			boolean enables = true;
			boolean accountNonExpired = true;
			boolean credentialsNonExpired = true;
			boolean accountNonLocked = true;
				
			//System.out.println(users.getUserName());
			//System.out.println(users.getUserPassword());
			
			//封装成spring security的User
			User userdetail = new User(users.getUserAccount(), users.getUserPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, auths);
						
			return userdetail;
	}

	public UserDaoImpl getUserDao() {
		return userDao;
	}

	public void setUserDao(UserDaoImpl userDao) {
		this.userDao = userDao;
	}
		
}

MyAccessDecisionManager类,负责权限的控制,假设请求的url在权限集合中有这个url相应的值,则放行。注:假设数据库中没有对这个url定义訪问的权限,默认是会被放行的

package framework.security.base;

import java.util.Collection;
import java.util.Iterator;

import org.springframework.security.access.AccessDecisionManager;
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;

public class MyAccessDecisionManager implements AccessDecisionManager {

	 //In this method, need to compare authentication with configAttributes.      
	 // 1, A object is a URL, a filter was find permission configuration by this URL, and pass to here.      
	 // 2, Check authentication has attribute in permission configuration (configAttributes)      
	 // 3, If not match corresponding authentication, throw a AccessDeniedException.   
		
	//这种方法在url请求时才会调用,server启动时不会运行这种方法,前提是须要在<http>标签内设置  <custom-filter>标签
	/*
	 * 參数说明:
	 * 1、configAttributes 装载了请求的url同意的角色数组 。这里是从MySecurityMetadataSource里的loadResourceDefine方法里的atts对象取出的角色数据赋予给了configAttributes对象
	 * 2、authentication 装载了从数据库读出来的角色 数据。这里是从MyUserDetailsService里的loadUserByUsername方法里的auths对象的值传过来给 authentication 对象
	 *  
	 * */	
	@Override
	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
	
		/*
		 * authentication装载了用户的信息数据,当中有角色。是MyUserDetailsService里的loadUserByUsername方法的userdetail对象传过来的
		 * userdetail一共同拥有7个參数(以下打印出来的数据可相应一下security的User类,这个类能够看到有寻7个參数),
		 * 最后一个是用来保存角色数据的,假设角色为空,将无权訪问页面。
		 * 看到以下的打印数据,  Granted Authorities: ROLE_ADMIN,ROLE_ADMIN 就是角色了。
		 * 假设显示Not granted any authorities,则说明userdetail的最后一个參数为空,没有传送角色的值过来
		 * 
		 * 打印出的数据:
		 * auth:org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bf5fbace: 
	       Principal: org.springframework.security.core.userdetails.User@c20: 
	       Username: aa; 
	       Password: [PROTECTED]; 
	       Enabled: true; 
	       AccountNonExpired: true; 
	       credentialsNonExpired: true; 
	       AccountNonLocked: true; 
	       Granted Authorities: ROLE_ADMIN; 
	       Credentials: [PROTECTED]; 
	       Authenticated: true; 
	       Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffc7f0c: RemoteIpAddress: 127.0.0.1; 
	       SessionId: 0952B3F9F18222DCCD0ECE39D039F900; 
	       Granted Authorities: ROLE_ADMIN		 
		 */
			
		if(configAttributes == null){            
			return;         
		}          			
		//System.out.println(object.toString());  //object is a URL.  
	
		Iterator<ConfigAttribute> ite=configAttributes.iterator();        
		while(ite.hasNext()){              
			
			ConfigAttribute ca=ite.next();              
			String needRole=((SecurityConfig)ca).getAttribute();              
			
			for(GrantedAuthority ga:authentication.getAuthorities()){                 
				//推断两个请求的url页面的权限和用户的权限是否同样,如同样,同意訪问
				if(needRole.equals(ga.getAuthority())){  
					
					/* <intercept-url pattern="/**" access="ROLE_ADMIN" />
					 * 假设applicationContext-security.xml的http标签里面有这样的配置,
					 * 在needRole为ROLE_USER时,即使needRole和ga.getAuthority权限匹配了,但权限是ROLE_USER,即使运行了return,
					 * 还是会无法訪问请求的url页面,由于终于都是以http标签里的权限来拦截,即仅仅能在权限为	ROLE_ADMIN才可訪问							 		        
			         */                
					return;                 
				}             
			}         
		} 
		//假设上面的needRole和ga.getAuthority两个权限没有匹配,将不同意訪问
		throw new AccessDeniedException("Access Denied");     
		
	}
	
	/*
	 * * 这个 supports(ConfigAttribute attribute) 方法在启动的时候被  
     * AbstractSecurityInterceptor调用,来决定AccessDecisionManager  
     * 能否够运行传递ConfigAttribute。 supports(Class)方法被安全拦截器实现调用,  
     * 包括安全拦截器将显示的AccessDecisionManager支持安全对象的类型。   
	 * 
	 * */
	@Override	
	public boolean supports(ConfigAttribute attribute) {
	
		 return true;
		//return false;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		
		 return true;
		//return false;
	}       
			
}

MyUsernamePasswordAuthenticationFilter类,自己定义的登录类

package framework.security.login;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.login.AccountExpiredException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.ServletActionContext;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


import com.user.dao.UserDaoI;
import com.user.model.PubUsers;

import framework.util.MD5Utils;

public class MyUsernamePasswordAuthenticationFilter extends
		UsernamePasswordAuthenticationFilter {
	public static final String VALIDATE_CODE = "validateCode";  
    public static final String USERNAME = "userAccount";  
    public static final String PASSWORD = "userPassword";  
    private int showCheckCode = 0;
    
 	public int getShowCheckCode() {
 		return showCheckCode;
	}

	public void setShowCheckCode(int showCheckCode) {
		this.showCheckCode = showCheckCode;
	}

	private MD5Utils md5 =new MD5Utils();
      
    private UserDaoI userDao;  

	public UserDaoI getUserDao() {
		return userDao;
	}

	public void setUserDao(UserDaoI userDao) {
		this.userDao = userDao;
	}
	
	public MD5Utils getMd5() {
		return md5;
	}

	public void setMd5(MD5Utils md5) {
		this.md5 = md5;
	}

	@Override  
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {  
        if (!request.getMethod().equals("POST")) {  
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());  
        }  
        //检測验证码  
        checkValidateCode(request);  
          
        String username = obtainUsername(request);  
        String password = obtainPassword(request);  
          
        //验证用户账号与password是否相应  
        username = username.trim();  
          
        PubUsers users=userDao.userInfo(username); 
            
        HttpSession session = request.getSession();
        session = request.getSession(false);//false代表不创建新的session,直接获取当前的session
        
        //将用户名存进session,假设登录成功,显示在主页
        session.setAttribute("login_account",username);
        
        if(users == null) {       	 

         	session.setAttribute("showCheckCode" ,"1" );  
         	session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
					"用户名或password错误!" );  
         	
      	
            throw new AuthenticationServiceException("用户名或password错误!"); 
            
        }else if(users.getUserPassword()=="" || users.getUserPassword()==null){
        	
        	session.setAttribute("showCheckCode" ,"1" );  
        	session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
					"用户名或password错误!" ); 
        	
            throw new AuthenticationServiceException("用户名或password错误!");
            
        	
        }else if(!users.getUserPassword().equals(md5.MD5Encode(password))){// password加密后再进行验证
   	
         	session.setAttribute("showCheckCode" ,"1" ); 
         	session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
					"用户名或password错误!" ); 
 	
            throw new AuthenticationServiceException("用户名或password错误!");
        
        }else{
        	
        	if(session.getAttribute("showCheckCode")=="1"){
        	    session.setAttribute("showCheckCode" ,	"0" ); 
        	}
        }
        
        
      
        //UsernamePasswordAuthenticationToken实现 Authentication  
        //这里要注意了,我第二个參数是用自己的md5加密了password再去传參的,由于我的password都是加密后存进数据库的。
        //假设这里不加密,那么和在数据库取出来的不匹配,终于即使登录账号和password都正确,也将无法登录成功。
        //由于在AbstractUserDetailsAuthenticationProvider里还会对用户和password验证,各自是
        //user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);//这个通过才干顺利通过
        //还有一个是 additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);//假设retrieveUser方法验证不通过,将无法訪问
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, md5.MD5Encode(password));  
        // Place the last username attempted into HttpSession for views  
          
        // 同意子类设置具体属性  
        setDetails(request, authRequest);  
          
       
        
        // 执行UserDetailsService的loadUserByUsername 再次封装Authentication  
        return this.getAuthenticationManager().authenticate(authRequest);  
    }  
      
    protected void checkValidateCode(HttpServletRequest request) {   
        HttpSession session = request.getSession();  
          
        String sessionValidateCode = obtainSessionValidateCode(session);   
       
       if( session.getAttribute("showCheckCode")=="1"){
        
        //让上一次的验证码失效  
          session.setAttribute(VALIDATE_CODE, null);  
          String validateCodeParameter = obtainValidateCodeParameter(request); 
          
          //推断输入的验证码和保存在session中的验证码是否同样,这里不区分大写和小写进行验证
          if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {    
         
        	session = request.getSession(false);//false代表不创建新的session,直接获取当前的session
        	session.setAttribute("SECURITY_LOGIN_EXCEPTION" ,
					"验证码错误" );   
        	
        	throw new AuthenticationServiceException("验证码错误!");   
           }  
       }
    }  
      
    private String obtainValidateCodeParameter(HttpServletRequest request) {  
        Object obj = request.getParameter(VALIDATE_CODE);  
        return null == obj ? "" : obj.toString();  
    }  
  
    protected String obtainSessionValidateCode(HttpSession session) {  
        Object obj = session.getAttribute(VALIDATE_CODE);  
        return null == obj ? "" : obj.toString();  
    }  
  
    @Override  
    protected String obtainUsername(HttpServletRequest request) {  
        Object obj = request.getParameter(USERNAME);  
        return null == obj ? "" : obj.toString();  
    }  
  
    @Override  
    protected String obtainPassword(HttpServletRequest request) {  
        Object obj = request.getParameter(PASSWORD);  
        return null == obj ? "" : obj.toString();  
    }  
   
}  















你可能感兴趣的:(Spring Security)