1. 程式人生 > >spring-security 多型別使用者登入+登入多引數驗證

spring-security 多型別使用者登入+登入多引數驗證

如果一個系統分為前臺使用者和後臺使用者那麼就不能使用spring-security的預設配置了。 需要自己來分開配置兩種使用者的登入方式。

首先建立spring-disuser-security.xml 與 spring-etuser-security.xml 兩個配置檔案,分別來配置兩種使用者登入的許可權與驗證方式

spring-disuser-security.xml的內容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:security="http://www.springframework.org/schema/security" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/security   
        http://www.springframework.org/schema/security/spring-security-3.1.xsd">
        
        <!-- 使用自定義Filter時需要將http節點的auto-config="true"屬性刪除,並且要通過entry-point-ref指定一個入口  -->
        <security:http use-expressions="true" access-denied-page="/powermiss.jsp" 
               authentication-manager-ref="disuserAuthManager" name="disuserSecurity" 
               entry-point-ref="authenticationEntryPoint">
            <security:intercept-url pattern="/disuserlogin.jsp"  access="permitAll"/>
            <!-- 配置自定義的Filter,並且將其放在FORM_LOGIN_FILTER節點,就會替換掉原來的FORM_LOGIN_FILTER節點  -->
            <security:custom-filter ref="loginProcessFilter" position="FORM_LOGIN_FILTER"/>  
        </security:http>  
           
        <security:authentication-manager id="disuserAuthManager" >  
            <security:authentication-provider user-service-ref="DisuserserDetailService" />
        </security:authentication-manager>   
        
        <!-- 登入處理Filter  -->
        <bean id="loginProcessFilter" class="com.tuanfang.service.DisUsernamePasswordAuthenticationFilter">
            <property name="loginid" value="loginid" />
            <property name="yzm" value="yzm" />
            <property name="usernameParameter" value="username" />  
            <property name="passwordParameter" value="password" />
            <property name="filterProcessesUrl" value="/disuserlogin.htm" />
            <property name="authenticationManager" ref="disuserAuthManager" />
            <property name="authenticationSuccessHandler" ref="successHandler" />
            <property name="authenticationFailureHandler" ref="failureHandler" />
        </bean>        
        
        <!-- 登入成功處理 -->
        <bean id="successHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
            <property name="defaultTargetUrl" value="/index.jsp" />
            <property name="alwaysUseDefaultTargetUrl" value="true" />
        </bean>
        
        <!-- 登入失敗處理 -->
        <bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
              <property name="defaultFailureUrl" value="/disuserlogin.jsp?error=true" />
        </bean>
        
      <!-- 登入入口 --> 
       <bean id="authenticationEntryPoint"  
            class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
            <property name="loginFormUrl" value="/disuserlogin.jsp" />  
       </bean>  
</beans>

接下來編寫DisUsernamePasswordAuthenticationFilter.java檔案,處理使用者登入。

spring-security預設的處理登入的類是org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter,可以將其中的程式碼複製到自己寫的DisUsernamePasswordAuthenticationFilter.java檔案中。

然後在進行自己的修改,達到驗證驗證碼,與公司登入id的驗證。

/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package com.tuanfang.service;
 
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
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.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.Assert;
 
 
/**
 * Processes an authentication form submission. Called {@code AuthenticationProcessingFilter} prior to Spring Security
 * 3.0.
 * <p>
 * Login forms must present two parameters to this filter: a username and
 * password. The default parameter names to use are contained in the
 * static fields {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}.
 * The parameter names can also be changed by setting the {@code usernameParameter} and {@code passwordParameter}
 * properties.
 * <p>
 * This filter by default responds to the URL {@code /j_spring_security_check}.
 *
 * @author Ben Alex
 * @author Colin Sampaleanu
 * @author Luke Taylor
 * @since 3.0
 */
public class DisUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    //~ Static fields/initializers =====================================================================================
 
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
    public static final String SPRING_SECURITY_FORM_YZM_KEY = "yzm";
    public static final String SPRING_SECURITY_FORM_LOGINID_KEY = "loginid";
    public static final String USERNAME_LOGINID_SPLIT = "/";
    /**
     * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler}
     */
    @Deprecated
    public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
 
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private String yzm = SPRING_SECURITY_FORM_YZM_KEY;
    private String loginid = SPRING_SECURITY_FORM_LOGINID_KEY;
    private boolean postOnly = true;
 
    //~ Constructors ===================================================================================================
 
    public DisUsernamePasswordAuthenticationFilter() {
        super("/j_spring_security_check");
    }
 
    //~ Methods ========================================================================================================
 
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
 
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        String loginid = obtainLoginid(request);
        String yzm = obtainYzm(request);
        
        if (username == null) {
            username = "";
        }
 
        if (password == null) {
            password = "";
        }
        
        if (loginid == null) {
            loginid = "";
        }
        
        if (yzm == null) {
            yzm = "";
        }
 
        username = username.trim();
        password = password.trim();
        loginid = loginid.trim();
        yzm = yzm.trim();
        
        username = loginid + USERNAME_LOGINID_SPLIT + username ;   //將公司登入id與登入使用者名稱使用 / 連線起來
 
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
 
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
 
        return this.getAuthenticationManager().authenticate(authRequest);
    }
 
    /**
     * Enables subclasses to override the composition of the password, such as by including additional values
     * and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
     * password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
     * <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the password that will be presented in the <code>Authentication</code> request token to the
     *         <code>AuthenticationManager</code>
     */
    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(passwordParameter);
    }
    
    //獲取驗證碼
    protected String obtainYzm(HttpServletRequest request){
           return request.getParameter(yzm);
    }
    
    //獲取公司id
    protected String obtainLoginid(HttpServletRequest request){
           return request.getParameter(loginid);
    }
    
    
    
    /**
     * Enables subclasses to override the composition of the username, such as by including additional values
     * and a separator.
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the username that will be presented in the <code>Authentication</code> request token to the
     *         <code>AuthenticationManager</code>
     */
    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(usernameParameter);
    }
 
    /**
     * Provided so that subclasses may configure what is put into the authentication request's details
     * property.
     *
     * @param request that an authentication request is being created for
     * @param authRequest the authentication request object that should have its details set
     */
    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }
 
    /**
     * Sets the parameter name which will be used to obtain the username from the login request.
     *
     * @param usernameParameter the parameter name. Defaults to "j_username".
     */
    public void setUsernameParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.usernameParameter = usernameParameter;
    }
 
    /**
     * Sets the parameter name which will be used to obtain the password from the login request..
     *
     * @param passwordParameter the parameter name. Defaults to "j_password".
     */
    public void setPasswordParameter(String passwordParameter) {
        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
        this.passwordParameter = passwordParameter;
    }
 
    /**
     * Defines whether only HTTP POST requests will be allowed by this filter.
     * If set to true, and an authentication request is received which is not a POST request, an exception will
     * be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
     * will be called as if handling a failed authentication.
     * <p>
     * Defaults to <tt>true</tt> but may be overridden by subclasses.
     */
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
 
    public final String getUsernameParameter() {
        return usernameParameter;
    }
 
    public final String getPasswordParameter() {
        return passwordParameter;
    }
 
    public String getYzm() {
        return yzm;
    }
 
    public void setYzm(String yzm) {
        this.yzm = yzm;
    }
 
    public String getLoginid() {
        return loginid;
    }
 
    public void setLoginid(String loginid) {
        this.loginid = loginid;
    }
}
接下來編寫提供Disuser的UserDetailsService類

package com.tuanfang.service;
 
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
 
import javax.annotation.Resource;
 
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Repository;
 
import com.tuanfang.dao.impl.DisuserDaoImpl;
import com.tuanfang.dao.impl.SysPowrDaoImpl;
import com.tuanfang.pojo.Disuser;
import com.tuanfang.pojo.SysPower;
 
@Repository("DisuserserDetailService")  
public class DisuserserDetailService implements UserDetailsService{
    
    @Resource(name="DisuserDaoImpl")
    private DisuserDaoImpl disuserDao ;
    
    @Resource(name="SysPowrDaoImpl")
    private SysPowrDaoImpl sysPowrDao ;
 
    @Override
    public UserDetails loadUserByUsername(String usernameAndloginId)
            throws UsernameNotFoundException {
        //將公司登入id與登入使用者名稱使用/分開
        String args[]  = usernameAndloginId.split(DisUsernamePasswordAuthenticationFilter.USERNAME_LOGINID_SPLIT);
        String loginid = args[0];
        String username = args[1];
        Disuser disuser =  disuserDao.findByLoginIdAndUserName(loginid , username);
        UserDetails userDetail = null ;
        if(disuser != null){
            userDetail = new User(username, disuser.getPassword(),disuser.getStatus() ==Disuser.ENABLE ,
                    true, true, true, obtainUserPowers(disuser.getId()));
        }
        return userDetail;
    }
    
    public Collection<GrantedAuthority> obtainUserPowers(BigInteger userId){
        Collection<GrantedAuthority> gas = new ArrayList<GrantedAuthority>();
        List<SysPower> powers = sysPowrDao.findDisuserPowers(userId);
        if(powers != null && powers.size() > 0){
            for (SysPower sysPower : powers) {  
                gas.add(new SimpleGrantedAuthority(sysPower.getCode()));   
            }
        }
        gas.add(new SimpleGrantedAuthority("HAVE_LOGIN"));    //登入    
        return gas ;
    }
 
}

登入頁面disuserlogin.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%  
String path = request.getContextPath();  
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
%>  
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>DisuserLogin</title>
</head>
<body>
     
    <form action="<%=basePath %>disuserlogin.htm" method="post">      
        <p> 
            <label for="username">Username</label> <input id="username"    
                name="username" type="text" />    
        </p>    
    
        <p>    
            <label for="password">password</label> <input id="password"    
                name="password" type="password" />            
        </p>    
          
    
        <p>    
            <label for="loginid">LoginId</label> <input id="loginid"    
                name="loginid" type="text" />            
        </p>    
          
    
        <p>    
            <label for="yzm">驗證碼</label> <input id="yzm"    
                name="yzm" type="text" />            
        </p>    
          
        <input type="submit" value="Login" /> 
    </form>    
    
      
</body>
</html>

這樣就配置好了Disuser的login

接下來配置EtdsUser的login。只需要username與password

spring-etuser-security.xml檔案內容如下(使用spring-security預設的方式配置):

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:security="http://www.springframework.org/schema/security" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/security   
        http://www.springframework.org/schema/security/spring-security-3.1.xsd">
        
        <security:http auto-config="true" pattern="/admin/**" use-expressions="true" access-denied-page="/powermiss.jsp" 
        authentication-manager-ref="etuserAuthManager" name="etuserSecurity">
            <security:intercept-url pattern="/admin/etuserlogin.jsp"  access="permitAll"/>
            <security:form-login login-processing-url="/admin/j_spring_security_check" authentication-failure-url="/admin/etuserlogin.jsp?error=true"  login-page="/admin/etuserlogin.jsp" 
                 default-target-url="/index.jsp" />
        </security:http>  
           
        
        <security:authentication-manager id="etuserAuthManager" >  
            <security:authentication-provider user-service-ref="EtuserDetailService"/>
        </security:authentication-manager>   
        
    
</beans>
Etdsuser的UserDetailsService類如下:

package com.tuanfang.service;
 
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
 
import javax.annotation.Resource;
 
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Repository;
 
import com.tuanfang.dao.impl.EtdsuserDaoImpl;
import com.tuanfang.dao.impl.SysPowrDaoImpl;
import com.tuanfang.pojo.EtdsUser;
import com.tuanfang.pojo.SysPower;
 
@Repository("EtuserDetailService")  
public class EtuserDetailService implements UserDetailsService{
    
    @Resource(name="EtdsuserDaoImpl")
    private EtdsuserDaoImpl etdsuserDao ;
    
    @Resource(name="SysPowrDaoImpl")
    private SysPowrDaoImpl sysPowrDao ;
 
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        EtdsUser etuser =  etdsuserDao.findUserByLoginName(username);
        UserDetails userDetail = null ;
        if(etuser != null){
            userDetail = new User(username, etuser.getPassword(),etuser.getStatus() ==EtdsUser.ENABLE ,
                    true, true, true, obtainUserPowers(etuser.getId()));  
        }
        return userDetail;
    }
    
    public Collection<GrantedAuthority> obtainUserPowers(BigInteger userId){
        Collection<GrantedAuthority> gas = new ArrayList<GrantedAuthority>();
        List<SysPower> powers = sysPowrDao.findEtuserPowers(userId);
        if(powers != null && powers.size() > 0){
            for (SysPower sysPower : powers) {  
                gas.add(new SimpleGrantedAuthority(sysPower.getCode()));   
            }
        }
        gas.add(new SimpleGrantedAuthority("HAVE_LOGIN"));    //登入   
        return gas ;
    }
 
}

然後在applicationContext.xml中匯入spring-disuser-security.xml與spring-etuser-security.xml