天天看點

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

[html] view plain copy

<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實作:

[java] view plain copy

/** 

 *  

 */  

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.Collection;  

import org.apache.commons.lang.StringUtils;  

import org.springframework.security.core.GrantedAuthority;  

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

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() {  

     * 憑證是否不過期,false則驗證不通過 

    public boolean isCredentialsNonExpired() {  

     * 該帳号是否啟用,false則驗證不通過 

    public boolean isEnabled() {  

        return !disabled;  

     * @return the name 

    public String getName() {  

     * @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;  

    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);    

        }     

    public void init(FilterConfig arg0) throws ServletException {  

        // TODO Auto-generated method stub  

    public Class<?> getSecureObjectClass() {  

        return FilterInvocation.class;   

    public SecurityMetadataSource obtainSecurityMetadataSource() {  

        return this.securityMetadataSource;     

    public void destroy() {  

登陸後,每次通路資源都會被這個攔截器攔截,會執行doFilter這個方法,這個方法調用了invoke方法,其中fi斷點顯示是一個url(可能重寫了toString方法吧,但是裡面還有一些方法的),最重要的是beforeInvocation這個方法,它首先會調用MyInvocationSecurityMetadataSource類的getAttributes方法擷取被攔截url所需的權限,在調用MyAccessDecisionManager類decide方法判斷使用者是否夠權限。弄完這一切就會執行下一個攔截器。

secureResourceFilterInvocationDefinitionSource實作

import java.util.Iterator;  

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.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);     

    public Collection<ConfigAttribute> getAttributes(Object object)  

            throws IllegalArgumentException {  

        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);  

              }  

    public Collection<ConfigAttribute> getAllConfigAttributes() {  

    /* (non-Javadoc) 

     * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions() 

    @SuppressWarnings("rawtypes")  

    public Collection getConfigAttributeDefinitions() {  

     * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class) 

    public boolean supports(@SuppressWarnings("rawtypes") Class clazz) {  

     * @param filterInvocation 

    @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實作

import org.springframework.security.access.AccessDecisionManager;  

import org.springframework.security.access.AccessDeniedException;  

import org.springframework.security.authentication.InsufficientAuthenticationException;  

import org.springframework.security.core.Authentication;  

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) 

    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");    

    public boolean supports(ConfigAttribute attribute) {  

    public boolean supports(Class<?> clazz) {  

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類,用于定義參數,需要實作的方法

     * 憑證,使用者密碼 

    public Object getCredentials() {  

     * 當事人,登入名 使用者Id 

    public Object getPrincipal() {  

        return userID;  

2.User類要實作Authentication,需要實作的方法

     * 傳回使用者所屬權限 

    public Collection<GrantedAuthority> getAuthorities() {  

        return this.accesses;  

    public Object getDetails() {  

     * 登入名稱 

        return loginName;  

     * 是否認證 

    public boolean isAuthenticated() {  

        return this.authenticated;  

     * 設定是否認證字段 

    public void setAuthenticated(boolean isAuthenticated)  

        this.authenticated=isAuthenticated;  

3.需要userService實作AuthenticationProvider的 authenticate(Authentication authentication)方法

@SuppressWarnings("unchecked")  

    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 ){  

                if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){  

                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))){  

            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;  

   重寫supports(Class<? extends Object> authentication)方法,authentication要

     * 如果此處驗證不通過,是不會執行authentication方法的 

    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.util.Date;  

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.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()) {  

            String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime);  

            if (md1.equals(md2))  

                return true;  

        } catch (Exception e) {           

            //e.printStackTrace();  

        return false;  

     * 可以通過request擷取頁面傳遞過來的參數,并且set到相應的token中 

    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;  

            //此處調用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;  

    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"/>

繼續閱讀