1. 程式人生 > >spring security四種實現方式

spring security四種實現方式

spring security實現方式大致可以分為這幾種:

    1.配置檔案實現,只需要在配置檔案中指定攔截的url所需要許可權、配置userDetailsService指定使用者名稱、密碼、對應許可權,就可以實現。

    2.實現UserDetailsService,loadUserByUsername(String userName)方法,根據userName來實現自己的業務邏輯返回UserDetails的實現類,需要自定義User類實現UserDetails,比較重要的方法是getAuthorities(),用來返回該使用者所擁有的許可權。

    3.通過自定義filter重寫spring security攔截器,實現動態過濾使用者許可權。

    4.通過自定義filter重寫spring security攔截器,實現自定義引數來檢驗使用者,並且過濾許可權。

1.最簡單配置spring-security.xml,實現1

<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:security="http://www.springframework.org/schema/security"  
    xmlns:p="http://www.springframework.org/schema/p" 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-4.0.xsd  
          http://www.springframework.org/schema/security  
          http://www.springframework.org/schema/security/spring-security-4.0.xsd">  
  
    <!-- use-expressions:Spring 表示式語言配置訪問控制 -->  
    <security:http auto-config="true" use-expressions="false">  
    		<!-- 配置許可權攔截,訪問所有url,都需要使用者登入,且擁有ROLE_USER許可權 -->
        <security:intercept-url pattern="/**" access="ROLE_USER" />  
           
    </security:http>  
  
    <security:authentication-manager alias="authenticationManager">  
        <security:authentication-provider>  
        		<!-- 配置預設使用者,使用者名稱:admin 密碼:123456 擁有許可權:ROLE_USER -->
            <security:user-service>  
                <security:user name="admin" password="123456"  
                    authorities="ROLE_USER" />  
            </security:user-service>  
        </security:authentication-provider>  
         
    </security:authentication-manager>  
        
</beans>  


2.實現UserDetailsService

先整理下spring secruity驗證流程:

springSecurity的登入驗證是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter這個過濾器來完成的,在該類的父類AbstractAuthenticationProcessingFilter中有一個AuthenticationManager介面屬性,驗證工作主要是通過這個AuthenticationManager介面的例項來完成的。在預設情況下,springSecurity框架會把

org.springframework.security.authentication.ProviderManager類的例項注入到該屬性

UsernamePasswordAuthenticationFilter的驗證過程如下:

1. 首先過濾器會呼叫自身的attemptAuthentication方法,從request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter過濾器中通過捕獲使用者提交的登入表單中的內容生成的一個org.springframework.security.core.Authentication介面例項.

2. 拿到authentication物件後,過濾器會呼叫ProviderManager類的authenticate方法,並傳入該物件

3.ProviderManager類的authenticate方法中會呼叫類中的List<AuthenticationProvider> providers集合中的各個AuthenticationProvider介面實現類中的authenticate(Authentication authentication)方法進行驗證,由此可見,真正的驗證邏輯是由各個AuthenticationProvider介面實現類來完成的。DaoAuthenticationProvider類是預設情況下注入的一個AuthenticationProvider介面實現類

4.provider的實現類在驗證使用者時,會呼叫userDetailsService的實現類的loadUserByUsername方法來獲取使用者資訊


首先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-4.3.xsd
                        http://www.springframework.org/schema/security 
                        http://www.springframework.org/schema/security/spring-security.xsd">
  <!-- 	use-expressions=”true” 需要使用表示式方式來寫許可權-->
	<http auto-config="true"  use-expressions="false">      
	   <!--這是spring 提供的http/https通道安全的這個是重要的!你的請求通道是安全的!-->
	   <!--
	   釋放使用者登陸page 允許任何人訪問該頁面 ,IS_AUTHENTICATED_ANONYMOUSLY表示不攔截
	   另一種不攔截資源的配置:<http pattern="/login.jsp" security="none">
	   -->
	
	   <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
	   
	    <!-- 配置使用者正常訪問page-->
	    <intercept-url pattern="/**" access="ROLE_USER"/>
	    
	    <!-- 自定義使用者登陸page default-target-url登陸成功跳轉的page ,authentication-failure-url="/login.jsp?error=true"這裡是登陸失敗跳轉的page-->
	    <form-login login-page="/login.jsp" default-target-url="/jsp/index/main.jsp" authentication-failure-url="/login.jsp?error=true"/>
	    <!-- 記住密碼 -->	
<!-- 	    <remember-me key="elim" user-service-ref="securityManager"/> -->
	 </http>
	 
	<authentication-manager alias="authenticationManager">
		<!-- 
			 authentication-provider 引用UserDetailsService實現類時使用user-service-ref屬性,引用authentication實現類時,使用ref屬性
			 這兩個屬性的區別在於  
       ref:直接將ref依賴的bean注入到AuthenticationProvider的providers集合中  
       user-service-ref:定義DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中,  
       並且DaoAuthenticationProvider的變數userDetailsService由user-service-ref依賴的bean注入。
		-->
		<authentication-provider user-service-ref="msecurityManager">
			<!-- 密碼加密 -->
			<password-encoder ref="myPasswordEncoder"/>
		</authentication-provider>
	</authentication-manager>
	
	<!-- 實現UserDetailsService -->
	<beans:bean id="msecurityManager" class="com.ultrapower.me.util.security.support.SecurityManagerSupport"></beans:bean>
	<!-- 密碼加密 -->
	<beans:bean id="myPasswordEncoder" class="com.ultrapower.me.util.security.MyPasswordEncoder"/>
	
</beans:beans>


userDetailsService實現:

/**
 * 
 */
package com.ultrapower.me.util.security.support;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.ultrapower.me.util.Constants;
import com.ultrapower.me.util.dbDao.SpringBeanUtil;
import com.ultrapower.me.util.security.SecurityManager;
import com.ultrapower.me.util.security.entity.Resource;
import com.ultrapower.me.util.security.entity.Role;
import com.ultrapower.me.util.security.entity.User;
import com.ultrapower.me.util.task.PasswordUtils;


public class SecurityManagerSupport  implements UserDetailsService{
	private   Log   log   = LogFactory.getLog(this.getClass().getName()); 
     

	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
//        List<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);
    	log.info("SecurityManagerSupport.loadUserByUsername.userName:"+userName);
    	
    	User user =null;
    	if("admin".equals(userName)){   	
    	    Set<Role> roles = new HashSet<Role>() ;
	    	Role role = new Role();
	    	role.setRoleid("ROLE_USER");
	    	role.setRoleName("ROLE_USER");
	    	
	    	Set<Resource> resources=new HashSet<Resource>() ;
	    	
	    	Resource res = new Resource();
	    	res.setResid("ME001");
	    	res.setResName("首頁");
	    	res.setResUrl("/jsp/index/main.jsp");
	    	res.setType("ROLE_USER");
	    	res.setRoles(roles);
	    	resources.add(res);
	    	
	    	role.setResources(resources);
	    	
	    	roles.add(role);
	        user = new User();
	    	user.setAccount("admin");
	    	user.setDisabled(false);
	    	user.setPassword(PasswordUtils.entryptPassword(Constants.securityKey));
	    	log.info(user.getPassword());
	    	user.setRoles(roles);	    	
    	}
      return user;//返回UserDetails的實現user不為空,則驗證通過
    }
    
}

UserDetails實現:
/**
 * 
 */
package com.ultrapower.me.util.security.entity;

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

import org.apache.commons.lang.StringUtils;
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 {
	
	private static final long serialVersionUID = 8026813053768023527L;

   
	private String account;
	
	private String name;
	
	private String password;
	
	private boolean disabled;
 
	private Set<Role> roles;
	
 
	private Map<String, List<Resource>> roleResources;
	
	/**
	 * The default constructor
	 */
	public User() {
		
	}
	
	/**
	 * Returns the authorites string
	 * 
	 * eg. 
	 *    downpour --- ROLE_ADMIN,ROLE_USER
	 *    robbin --- ROLE_ADMIN
	 * 
	 * @return
	 */
	public String getAuthoritiesString() {
	    List<String> authorities = new ArrayList<String>();
	    for(GrantedAuthority authority : this.getAuthorities()) {
	        authorities.add(authority.getAuthority());
	    }
	    return StringUtils.join(authorities, ",");
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// 根據自定義邏輯來返回使用者許可權,如果使用者許可權返回空或者和攔截路徑對應許可權不同,驗證不通過
		if(!roles.isEmpty()){
			List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
			GrantedAuthority au = new SimpleGrantedAuthority("ROLE_USER");
			list.add(au);
			return list;
		}
		return null;
	}

	/* 
	 * 密碼
	 */
	public String getPassword() {
		return password;
	}

	/* 
	 * 使用者名稱
	 */
	public String getUsername() {
		return name;
	}

	/* 
	 *帳號是否不過期,false則驗證不通過
	 */
	public boolean isAccountNonExpired() {
		return true;
	}

	/* 
	 * 帳號是否不鎖定,false則驗證不通過
	 */
	public boolean isAccountNonLocked() {
		return true;
	}

	/* 
	 * 憑證是否不過期,false則驗證不通過
	 */
	public boolean isCredentialsNonExpired() {
		return true;
	}

	/* 
	 * 該帳號是否啟用,false則驗證不通過
	 */
	public boolean isEnabled() {
		return !disabled;
	}

 

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return the disabled
	 */
	public boolean isDisabled() {
		return disabled;
	}

	/**
	 * @return the roles
	 */
	public Set<Role> getRoles() {
		return roles;
	}

	/**
	 * @return the roleResources
	 */
	public Map<String, List<Resource>> getRoleResources() {
		// init roleResources for the first time
		System.out.println("---------------------------------------------------");
		if(this.roleResources == null) {
			
			this.roleResources = new HashMap<String, List<Resource>>();
			
			for(Role role : this.roles) {
				String roleName = role.getRoleName();
				Set<Resource> resources = role.getResources();
				for(Resource resource : resources) {
					String key = roleName + "_" + resource.getType();
					if(!this.roleResources.containsKey(key)) {
						this.roleResources.put(key, new ArrayList<Resource>());
					}
					this.roleResources.get(key).add(resource);					
				}
			}
			
		}
		return this.roleResources;
	}

 
	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @param disabled the disabled to set
	 */
	public void setDisabled(boolean disabled) {
		this.disabled = disabled;
	}

	/**
	 * @param roles the roles to set
	 */
	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}

	public String getAccount() {
		return account;
	}

	public void setAccount(String account) {
		this.account = account;
	}

	public void setRoleResources(Map<String, List<Resource>> roleResources) {
		this.roleResources = roleResources;
	}
	
}

3.實現動態過濾使用者許可權

在spring-security配置檔案的http標籤中新增如下配置
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="securityInterceptor"/>
在spring-security配置檔案中新增如下配置
<!-- 	自定義攔截器 -->
	<beans:bean id="securityInterceptor" class="com.ultrapower.me.util.security.interceptor.SecurityInterceptor">
		<beans:property name="authenticationManager" ref="authenticationManager"/>
	    <beans:property name="accessDecisionManager" ref="mesecurityAccessDecisionManager"/>
	    <beans:property name="securityMetadataSource" ref="secureResourceFilterInvocationDefinitionSource" />
	</beans:bean>
<!-- 	獲取訪問url對應的所有許可權 -->
	<beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.ultrapower.me.util.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />
<!-- 	校驗使用者的許可權是否足夠 -->
	<beans:bean id="mesecurityAccessDecisionManager" class="com.ultrapower.me.util.security.interceptor.SecurityAccessDecisionManager" />
	


securityInterceptor繼承AbstractSecurityInterceptor過濾器,實現Filter過濾器
package com.ultrapower.me.util.security.interceptor;

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;

public class SecurityInterceptor extends AbstractSecurityInterceptor implements Filter{

	//配置檔案注入
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
	
	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
		return securityMetadataSource;
	}

	public void setSecurityMetadataSource(
			FilterInvocationSecurityMetadataSource securityMetadataSource) {
		this.securityMetadataSource = securityMetadataSource;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub\
		
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		//fi裡面有一個被攔截的url
        //裡面呼叫MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的所有許可權
        //再呼叫MyAccessDecisionManager的decide方法來校驗使用者的許可權是否足夠
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
	        //執行下一個攔截器
	        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());   
        } finally { 
            super.afterInvocation(token, null);  
        }   
		
	}

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

	@Override
	public Class<?> getSecureObjectClass() {
		// TODO Auto-generated method stub
		return FilterInvocation.class; 
	}

	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		// TODO Auto-generated method stub
		return this.securityMetadataSource;   
	}

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

登陸後,每次訪問資源都會被這個攔截器攔截,會執行doFilter這個方法,這個方法呼叫了invoke方法,其中fi斷點顯示是一個url(可能重寫了toString方法吧,但是裡面還有一些方法的),最重要的是beforeInvocation這個方法,它首先會呼叫MyInvocationSecurityMetadataSource類的getAttributes方法獲取被攔截url所需的許可權,在呼叫MyAccessDecisionManager類decide方法判斷使用者是否夠許可權。弄完這一切就會執行下一個攔截器。
secureResourceFilterInvocationDefinitionSource實現
/**
 * 
 */
package com.ultrapower.me.util.security.interceptor;

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

import javax.servlet.ServletContext;

import org.springframework.beans.factory.InitializingBean;
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 org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;


public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
    
    private PathMatcher matcher;
    
    private static Map<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();

    /* 
     * 初始化使用者許可權,為了簡便操作沒有從資料庫獲取
     * 實際操作可以從資料庫中獲取所有資源路徑url所對應的許可權
     */
    public void afterPropertiesSet() throws Exception {
        this.matcher = new AntPathMatcher();//用來匹配訪問資源路徑
        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); 
        ConfigAttribute ca = new SecurityConfig("ROLE_USER");
        atts.add(ca); 
        map.put("/jsp/index/main.jsp", atts);  
        Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();
        ConfigAttribute cano = new SecurityConfig("ROLE_NO");
        attsno.add(cano);
        map.put("/http://blog.csdn.net/u012367513/article/details/other.jsp", attsno);   
    }
    
     

    @Override
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
		// TODO Auto-generated method stub
    	FilterInvocation filterInvocation = (FilterInvocation) object;
    	
    	String requestURI = filterInvocation.getRequestUrl();
    	//迴圈資源路徑,當訪問的Url和資源路徑url匹配時,返回該Url所需要的許可權
        for(Iterator<Map.Entry<String, Collection<ConfigAttribute>>> iter = map.entrySet().iterator(); iter.hasNext();) {
	          Map.Entry<String, Collection<ConfigAttribute>> entry = iter.next();
	          String url = entry.getKey();
	          
	          if(matcher.match(url, requestURI)) {
	              return map.get(requestURI);
	          }
        }
      
		return null;
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
     * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
     */
	@SuppressWarnings("rawtypes")
	public Collection getConfigAttributeDefinitions() {
        return null;
    }

    /* (non-Javadoc)
     * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
     */
	public boolean supports(@SuppressWarnings("rawtypes") Class clazz) {
        return true;
    }
    
    /**
     * 
     * @param filterInvocation
     * @return
     */
    @SuppressWarnings("unchecked")
	private Map<String, String> getUrlAuthorities(org.springframework.security.web.FilterInvocation filterInvocation) {
        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
        return (Map<String, String>)servletContext.getAttribute("urlAuthorities");
    }

}

mesecurityAccessDecisionManager實現
package com.ultrapower.me.util.security.interceptor;

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 SecurityAccessDecisionManager implements AccessDecisionManager {
    
	/**
	 * 檢查使用者是否夠許可權訪問資源
	 * authentication 是從spring的全域性快取SecurityContextHolder中拿到的,裡面是使用者的許可權資訊
	 * object 是url
	 * configAttributes 所需的許可權
	 * @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
	 */
	@Override
	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
		// 對應url沒有許可權時,直接跳出方法
	   if(configAttributes == null){ 
           return;       
       }  
        
       Iterator<ConfigAttribute> ite=configAttributes.iterator();
       //判斷使用者所擁有的許可權,是否符合對應的Url許可權,如果實現了UserDetailsService,則使用者許可權是loadUserByUsername返回使用者所對應的許可權
       while(ite.hasNext()){
           ConfigAttribute ca=ite.next();  
           String needRole=((SecurityConfig)ca).getAttribute();
           for(GrantedAuthority ga : authentication.getAuthorities()){ 
        	   System.out.println(":::::::::::::"+ga.getAuthority());
               if(needRole.equals(ga.getAuthority())){  
                   return;              
               }            
           }      
       } 
       //注意:執行這裡,後臺是會拋異常的,但是介面會跳轉到所配的access-denied-page頁面
       throw new AccessDeniedException("no right");  
	}
	@Override
	public boolean supports(ConfigAttribute attribute) {
		return true;
	}
	@Override
	public boolean supports(Class<?> clazz) {
		return true;
	}

}

4.實現AuthenticationProvider,自定義引數驗證

這種驗證以前專案用過,現在沒有寫示例程式碼,先寫下大概流程和需要用到的類 這種驗證的好處:可以在自定義登入介面新增登入時需要的引數,如多個驗證碼等、可以修改預設登入名稱和密碼的引數名 整體流程: 1.使用者登入時,先經過自定義的passcard_filter過濾器,該過濾器繼承了AbstractAuthenticationProcessingFilter,並且綁定了登入失敗和成功時需要的處理器(跳轉頁面使用) 2.執行attemptAuthentication方法,可以通過request獲取登入頁面傳遞的引數,實現自己的邏輯,並且把對應引數set到AbstractAuthenticationToken的實現類中 3.驗證邏輯走完後,呼叫 this.getAuthenticationManager().authenticate(token);方法,執行AuthenticationProvider的實現類的supports方法 4.如果返回true則繼續執行authenticate方法 5.在authenticate方法中,首先可以根據使用者名稱獲取到使用者資訊,再者可以拿自定義引數和使用者資訊做邏輯驗證,如密碼的驗證 6.自定義驗證通過以後,獲取使用者許可權set到User中,用於springSecurity做許可權驗證 7.this.getAuthenticationManager().authenticate(token)方法執行完後,會返回Authentication,如果不為空,則說明驗證通過 8.驗證通過後,可實現自定義邏輯操作,如記錄cookie資訊 9.attemptAuthentication方法執行完成後,由springSecuriy來進行對應許可權驗證,成功於否會跳轉到相對應處理器設定的介面。 1.自定義PassCardAuthenticationToken類,繼承AbstractAuthenticationToken類,用於定義引數,需要實現的方法
/**
	 * 憑證,使用者密碼
	 */
	@Override
	public Object getCredentials() {
		return password;
	}

	/**
	 * 當事人,登入名 使用者Id
	 */
	@Override
	public Object getPrincipal() {
		return userID;
	}


2.User類要實現Authentication,需要實現的方法
/**
	 * 返回使用者所屬許可權
	 */
	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		return this.accesses;
	}
	
	@Override
	public Object getCredentials() {
		return null;
	}
	@Override
	public Object getDetails() {
		return null;
	}
	/**
	 * 登入名稱
	 */
	@Override
	public Object getPrincipal() {
		return loginName;
	}
	/**
	 * 是否認證
	 */
	@Override
	public boolean isAuthenticated() {
		return this.authenticated;
	}
	/**
	 * 設定是否認證欄位
	 */
	@Override
	public void setAuthenticated(boolean isAuthenticated)
			throws IllegalArgumentException {
		this.authenticated=isAuthenticated;
	}


3.需要userService實現AuthenticationProvider的 authenticate(Authentication authentication)方法   
@SuppressWarnings("unchecked")
	@Override
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication;
		/*
		 * 這裡進行邏輯認證操作,可以獲取token中的屬性來自定義驗證邏輯,程式碼驗證邏輯可以不用管
		 * 如果使用UserDetailsService的實現類來驗證,就只能獲取userName,不夠靈活
		 */
		if(token.getUserID()!=null&&token.getPassword()!=null){
			User user=(User)this.getDao().executeQueryUnique("User.loadByLoginName", QueryCmdType.QUERY_NAME, token.getUserID());
			
			String password=token.getPassword();
			if(this.passwordEncoder!=null){
				password=this.passwordEncoder.encodePassword(password, null);
			}
			
			if(!password.equalsIgnoreCase(user.getPassword())){
				
				token.setErrCode("2");
				return null;
			}
			
			if( token.isEnablePasscard() && usePassCard ){//token中啟用密碼卡且系統使用密碼卡
				
				int position1=((token.getRow1()-1)*7)+token.getColumn1();
				int position2=((token.getRow2()-1)*7)+token.getColumn2();
				//System.out.println( "---pos:"+position1+"---"+position2 );
				
				if(user.getPassCardId()==null){
					token.setErrCode("10");
					return null;
				}
				PassCard passcard=this.passCardDao.findById(user.getPassCardId(), false);
						
				if(passcard==null||passcard.getStatus()==PassCardHelper.STATUS_CANCEL ){
					token.setErrCode("10");
					return null;
				}
				if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){
					token.setErrCode("10");
					return null;
				}
				
				String content=passcard.getConfusedContent();
				int perLen=content.length()/49;
				String str1=content.substring((position1-1)*perLen, position1*perLen);
				String str2=content.substring((position2-1)*perLen, position2*perLen);
				String inputStr1=token.getCard1();
				String inputStr2=token.getCard2();
				if(this.passwordEncoder!=null){
					inputStr1 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr1));
					inputStr2 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr2));
				}
			
				if((!str1.equalsIgnoreCase(inputStr1))||(!str2.equalsIgnoreCase(inputStr2))){
					token.setErrCode("10");
					return null;
				}
			}
			user.setLastIp(token.getIp());
			user.setLastLogin(new Date());
			this.getDao().saveOrUpdate(user);			
			user.setAuthenticated(true);
			/*
			 * 匯入一次角色許可權,並且把許可權set到User中,用於spring驗證使用者許可權(getAuthorities方法)
			 */
			List<UserRole> userRoles=(List<UserRole>)this.getDao().executeQueryList("UserRole.listRoleByUserID", QueryCmdType.QUERY_NAME, -1, -1, user.getId());
			Set<GrantedAuthority> accesses=new HashSet<GrantedAuthority>();
			for(UserRole ur:userRoles){
				accesses.add(ur.getRole());				
			}
			user.getOrg().getOrgName();
			if(user.getOrg().getCertTypes()!=null) user.getOrg().getCertTypes().size();//延遲載入一下
			user.setAccesses(accesses);
			return user;
		}
		return null;
	}

   重寫supports(Class<? extends Object> authentication)方法,authentication要
/**
	 * 如果此處驗證不通過,是不會執行authentication方法的
	 */
	@Override
	public boolean supports(Class<? extends Object> authentication) {
		return authentication.equals(PassCardAuthenticationToken.class);
	}

4.定義filter,實現AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用於獲取在登入頁面傳遞過來的引數,spring預設只獲取userName(j_username),password(j_username),而且實現UserDetailsService時只傳遞username
import java.io.IOException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.StringUtils;

import cn.edu.jszg.cert.user.UserLog;
import cn.edu.jszg.cert.user.UserLogService;
import cn.edu.jszg.cert.web.WebApplicationConfiguration;
import cn.edu.jszg.cert.web.controller.portal.auth.RemoteDataValidator;

import com.google.code.kaptcha.servlet.KaptchaServlet;

public class PasscardAuthenticationProcessingFilter extends
		AbstractAuthenticationProcessingFilter {
	private String successPage = "/home/admin/index";
	private String failurePage = "/public/adminLoginEntry";
	private boolean forward = false;
	private boolean useVerifyCode=true;
	private String certLoginUrl;
	
	static Logger logger = Logger.getLogger(PasscardAuthenticationProcessingFilter.class);
	
	private WebApplicationConfiguration config;
	private UserLogService userLogService;	
	
	public void setConfig(WebApplicationConfiguration config) {
		this.config = config;
	}

	/**
	 * 實現AbstractAuthenticationProcessingFilter的有參構造
	 * 沒記錯的話,相當於該filter的訪問路徑 
	 */
	protected PasscardAuthenticationProcessingFilter() {
		super("/adminLoginCheck");
	}

	public void setUseVerifyCode(boolean useVerifyCode) {
		this.useVerifyCode = useVerifyCode;
	}

	public void setUserLogService(UserLogService userLogService) {
		this.userLogService = userLogService;
	}
	
	public boolean validate(HttpServletRequest request) {
		String userId = request.getParameter("username");
		String md2 = request.getParameter("m");
		String l = request.getParameter("l");
		if (userId == null || md2 == null || l == null) {
			return false;
		}
		long longTime = Long.parseLong(l);
		if (longTime < new Date().getTime()) {
			return false;
		}

		
		try {
			String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime);
			if (md1.equals(md2))
				return true;
			
		} catch (Exception e) {			
			//e.printStackTrace();
		}
		
		return false;
	}

	/**
	 * 可以通過request獲取頁面傳遞過來的引數,並且set到相應的token中
	 */
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException,
			IOException, ServletException {
		
//		logger.warn("-----------------start證書登入使用者----------");
		HttpSession s = request.getSession(true);
		PassCardAuthenticationToken token = new PassCardAuthenticationToken();
		
		String verifyCode = request.getParameter("verifyCode");
		String userID = request.getParameter("username");
		//....此處省略獲取引數,並且驗證、賦值的邏輯
		Authentication auth = null;
		
		try {
			//此處呼叫getAuthenticationManager的authenticate方法,當supports方法返回true時執行authenticate方法
			auth = this.getAuthenticationManager().authenticate(token);
			
			//此處為登入成功後,相應的處理邏輯
			if (auth == null || !auth.isAuthenticated()) {
				s.setAttribute("__login_error", token.getErrCode());
			} else  {
				s.removeAttribute("__login_error");
				s.removeAttribute("__login_username");
				s.removeAttribute("__cert_userid");
				if( token.isEnablePasscard()) {
					s.removeAttribute("__passcard_row1");
					s.removeAttribute("__passcard_row2");
					s.removeAttribute("__passcard_column1");
					s.removeAttribute("__passcard_column2");
				}
			}
		} catch (AuthenticationException e) {
			s.setAttribute("__login_error", token.getErrCode());
			throw e;
		}
		
	
		return auth;
	}

	public void setSuccessPage(String successPage) {
		this.successPage = successPage;
	}

	public void setFailurePage(String failurePage) {
		this.failurePage = failurePage;
	}

	public void setForward(boolean forward) {
		this.forward = forward;
	}

	public void setCertLoginUrl(String certLoginUrl) {
		this.certLoginUrl = certLoginUrl;
	}

	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		/*
		*該處理器實現了AuthenticationSuccessHandler, AuthenticationFailureHandler
		*用於處理登入成功或者失敗後,跳轉的介面
		*/
		AuthenticationResultHandler handler = new AuthenticationResultHandler();
		handler.setForward(forward);
		handler.setLoginFailurePage(failurePage);
		handler.setLoginSuccessPage(successPage);
		handler.setCertLoginUrl(certLoginUrl);
		//設定父類中的處理器
		this.setAuthenticationSuccessHandler(handler);
		this.setAuthenticationFailureHandler(handler);

	}

}

最後為spring-security配置檔案中的配置,需要新增authentication-provider的引用,和filter的配置
<security:authentication-manager alias="authenticationManager">
		<!-- 注意,這裡僅僅是系統預設的認證機制,請在正式系統中明確知道其功能再使用 -->
		<security:authentication-provider ref="acocunt_defaultAnthentiactionProvider"/>
		<security:authentication-provider ref="registrationService"/>
		<security:authentication-provider ref="enrollmentService"/>
		<security:authentication-provider ref="userService"/>
	</security:authentication-manager>	
	<bean id="passcard_filter" class="cn.edu.jszg.cert.security.PasscardAuthenticationProcessingFilter">
		<property name="authenticationManager" ref="authenticationManager"/>
		<property name="useVerifyCode" value="true"/>
		<property name="failurePage" value="/portal/home/auth/"></property>
		<property name="config" ref="webAppConfig"/>
		<property name="userLogService" ref="userLogService" />
		<property name="certLoginUrl" value="${cert.login.url}"/>
	</bean>
還要在http中新增<security:custom-filter ref="passcard_filter" after="SECURITY_CONTEXT_FILTER"/>