1. 程式人生 > >spring security4自定義登陸加驗證碼配置

spring security4自定義登陸加驗證碼配置

最近做的專案中使用了spring security來管理許可權,由於之前沒有接觸過所以在網上找了半天資料研究,打算記錄下來。
security的版本使用的是4.2,相信網上很多文章都已經說明了3.x和4.x的區別,這裡就不再說明了,下面說下準備工作。

相關jar包,pom下載:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-ldap</artifactId>
    <version
>
4.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>4.2.2.RELEASE</version> </dependency> <dependency> <groupId>
org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.2.2.RELEASE</version> </dependency>

說明一下本專案中使用的使用者是從公司的ldap獲取的所以還加入了spring-security-ldap包

配置:

接下來的就是security的配置了。關於配置可以分為”名稱空間“方式和”java config“的方式。

本文所使用的是名稱空間的配置方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:s="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/security
                        http://www.springframework.org/schema/security/spring-security-4.2.xsd
                        http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">


    <!-- 過濾的資源-->
    <!-- tip:如果將security="none"則這些路徑的資源將不在security的管控範圍之內 -->
    <s:http pattern="/images/**" security="none" />
    <s:http pattern="/js/**" security="none" />
    <s:http pattern="/css/**" security="none" />
    <!-- use-expressions表示是否使用表示式,auto-config設為true表示使用表單 -->
    <s:http use-expressions="true" auto-config='true'>
        <!-- access用來設定許可權如果設定為"ROLE_ANONYMOUS"表示可以匿名訪問也就是不用登陸可以訪問,但是屬於security的監控範圍內 -->
        <s:intercept-url pattern="/loginPage" access="hasRole('ROLE_ANONYMOUS')" />
        <s:intercept-url pattern="/**"
            access="hasAnyRole('ROLE_OPER','ROLE_ADMIN')" />
            <!-- form-login用來配置登陸的一些屬性(security預設會提供一個get的登入頁"/login"如果不想使用預設的登入頁也可以自定義配置如下使用了自定義的登入頁)-->
        <s:form-login login-page="/loginPage" 
                        username-parameter="username"
                        password-parameter="password" 
                        default-target-url='/homepage/index'
                        always-use-default-target='true' />
        <s:csrf disabled="false" />
        <s:logout logout-url="/logout" logout-success-url="/login" />
        <!-- 在FORM_LOGIN_FILTER之前插入一個自定義過濾器 -->
        <s:custom-filter before="FORM_LOGIN_FILTER" ref="myFilter" /> 
    </s:http>

    <!-- 自定義filter處理登陸的驗證及額外處理 -->
    <beans:bean id="myFilter" class="com.onstar.bdif.common.filters.MySpecialAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager" />
        <!--呼叫登入認證成功後的處理 -->
        <property name="authenticationSuccessHandler" ref="successHandler" />
        <!--呼叫登入失敗後的處理配置 -->
        <property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" />
    </beans:bean>
    <!--登入認證成功後的處理-->
    <bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/homepage/index" />
    </bean>
    <!--登入失敗後的處理配置-->
    <bean id="simpleUrlAuthenticationFailureHandler"
     class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <property name="defaultFailureUrl" value="/loginPage" />
    </bean>

    <!-- ldap使用者認證 -->
    <!-- security在登陸認證完成之後預設會清除密碼資訊,如果設定erase-credentials=false則會保留密碼資訊,方便後續使用 -->
    <s:authentication-manager id="authenticationManager" erase-credentials="false">
        <!-- ldap的配置使用者資訊配置 -->
        <s:ldap-authentication-provider
            user-details-class="inetOrgPerson" group-search-filter="uniqueMember={0}"
            role-prefix="xx" group-search-base="ou=xx"
            user-dn-pattern="uid={0},ou=user,ou=xx" server-ref="ldapContextSource" />
    </s:authentication-manager>
    <!-- ldap地址 -->
    <s:ldap-server id="ldapContextSource"
        url="ldap://xx.xx.xx.xx:xx/ou=xx,ou=xx,o=xx.com" />
</beans>

由於在專案中使用了驗證碼的功能,並且在前端的登陸頁面還加入了密碼加密的一個方法,所以如上插入了一個自定義的filter來驗證驗證碼和還原未加密的密碼以供ldap驗證

//繼承了UsernamePasswordAuthenticationFilter
public class MySpecialAuthenticationFilter extends UsernamePasswordAuthenticationFilter {


    private Logger logger = LogManager.getLogger(MySpecialAuthenticationFilter.class);

    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "bdifacun";
    public static final String SPRING_SECURITY_FORM_BDIFACPWD_KEY = "bdifacpwd";
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String bdifacpwdParameter = SPRING_SECURITY_FORM_BDIFACPWD_KEY;
    private boolean postOnly = true;

    //重寫原有的驗證方法
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        HttpSession session = request.getSession();
        String username = obtainUsername(request);
        String bdifacpwd = obtainPassword(request);
        // 驗證碼
        String inputValidateCode = request.getParameter("inputValidateCode");
        // 判斷使用者名稱登入密碼是否為空
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(bdifacpwd)) {
            session.setAttribute("errorMsg", "請輸入使用者名稱或者密碼!");
            throw new AuthenticationServiceException("username or bdifacpwd is not null!"); 
        }else {
            bdifacpwd = AesUtil.aesDecrypt(bdifacpwd, OnstarConstans.AES_LOGIN_KEY);
        }
        // 從session中獲得驗證碼,與使用者輸入的驗證碼做比較
        String sessionCaptcha = (String) session.getAttribute("validateCode");
        if (StringUtils.isEmpty(inputValidateCode) || StringUtils.isEmpty(sessionCaptcha)) {
            session.setAttribute("errorMsg", "驗證碼為空!");
            throw new AuthenticationServiceException("the session validate is not null!");
        }
        // 清空Sesion裡的驗證碼
        session.removeAttribute("validateCode");
        // 驗證碼輸入校驗
        if (!sessionCaptcha.equalsIgnoreCase(inputValidateCode)) {
            session.setAttribute("errorMsg", "驗證碼輸入錯誤!");
            throw new AuthenticationServiceException("the validate is error!");
        }
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, bdifacpwd);
        setDetails(request, authRequest);
        Authentication authen = null;
        // 執行ldap的登入驗證
        try {
            authen = this.getAuthenticationManager().authenticate(authRequest);
        } catch (AuthenticationException failed) {
            session.setAttribute("errorMsg", "使用者名稱或密碼錯誤!");
            throw new AuthenticationServiceException("the validate is error!");
        }
        session.setAttribute("managerSession", username);
        return authen;
    }

    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(bdifacpwdParameter);
    }

    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(usernameParameter);
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }
}

以上則可以完成security登陸加驗證碼及前端自定義密碼加密驗證的過程,文章寫的比較簡潔哈哈,記錄下來以便日後翻閱。