SpringSecurity學習之自定義過濾器


  我們系統中的認證場景通常比較復雜,比如說用戶被鎖定無法登錄,限制登錄IP等。而SpringSecuriy最基本的是基於用戶與密碼的形式進行認證,由此可知它的一套驗證規范根本無法滿足業務需要,因此擴展勢在必行。那么我們可以考慮自己定義filter添加至SpringSecurity的過濾器棧當中,來實現我們自己的驗證需要。

  本例中,基於前篇的數據庫的Student表來模擬一個簡單的例子:當Student的jointime在當天之后,那么才允許登錄

一、創建自己定義的Filter

我們先在web包下創建好幾個包並定義如下幾個類

CustomerAuthFilter:

package com.bdqn.lyrk.security.study.web.filter;

import com.bdqn.lyrk.security.study.web.authentication.UserJoinTimeAuthentication;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class CustomerAuthFilter extends AbstractAuthenticationProcessingFilter {

    private AuthenticationManager authenticationManager;


    public CustomerAuthFilter(AuthenticationManager authenticationManager) {

        super(new AntPathRequestMatcher("/login", "POST"));
        this.authenticationManager = authenticationManager;

    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String username = request.getParameter("username");
        UserJoinTimeAuthentication usernamePasswordAuthenticationToken =new UserJoinTimeAuthentication(username);
        Authentication authentication = this.authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        if (authentication != null) {
            super.setContinueChainBeforeSuccessfulAuthentication(true);
        }
        return authentication;
    }
}
View Code

  該類繼承AbstractAuthenticationProcessingFilter,這個filter的作用是對最基本的用戶驗證的處理,我們必須重寫attemptAuthentication方法。Authentication接口表示授權接口,通常情況下業務認證通過時會返回一個這個對象。super.setContinueChainBeforeSuccessfulAuthentication(true) 設置成true的話,會交給其他過濾器處理。

 

二、定義UserJoinTimeAuthentication

package com.bdqn.lyrk.security.study.web.authentication;

import org.springframework.security.authentication.AbstractAuthenticationToken;

public class UserJoinTimeAuthentication extends AbstractAuthenticationToken {
    private String username;

    public UserJoinTimeAuthentication(String username) {
        super(null);
        this.username = username;
    }


    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return username;
    }
}
View Code

自定義授權方式,在這里接收username的值處理,其中getPrincipal我們可以用來存放登錄名,getCredentials可以存放密碼,這些方法來自於Authentication接口

 

三、定義AuthenticationProvider

package com.bdqn.lyrk.security.study.web.authentication;

import com.bdqn.lyrk.security.study.app.pojo.Student;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

import java.util.Date;

/**
 * 基本的驗證方式
 *
 * @author chen.nie
 * @date 2018/6/12
 **/
public class UserJoinTimeAuthenticationProvider implements AuthenticationProvider {
    private UserDetailsService userDetailsService;

    public UserJoinTimeAuthenticationProvider(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    /**
     * 認證授權,如果jointime在當前時間之后則認證通過
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = (String) authentication.getPrincipal();
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
        if (!(userDetails instanceof Student)) {
            return null;
        }
        Student student = (Student) userDetails;
        if (student.getJoinTime().after(new Date()))
            return new UserJoinTimeAuthentication(username);
        return null;
    }

    /**
     * 只處理UserJoinTimeAuthentication的認證
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.getName().equals(UserJoinTimeAuthentication.class.getName());
    }
}
View Code

   AuthenticationManager會委托AuthenticationProvider進行授權處理,在這里我們需要重寫support方法,該方法定義Provider支持的授權對象,那么在這里我們是對UserJoinTimeAuthentication處理。

 

四、WebSecurityConfig

package com.bdqn.lyrk.security.study.app.config;

import com.bdqn.lyrk.security.study.app.service.UserService;
import com.bdqn.lyrk.security.study.web.authentication.UserJoinTimeAuthenticationProvider;
import com.bdqn.lyrk.security.study.web.filter.CustomerAuthFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * spring-security的相關配置
 *
 * @author chen.nie
 * @date 2018/6/7
 **/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*
            1.配置靜態資源不進行授權驗證
            2.登錄地址及跳轉過后的成功頁不需要驗證
            3.其余均進行授權驗證
         */
        http.
                authorizeRequests().antMatchers("/static/**").permitAll().
                and().authorizeRequests().antMatchers("/user/**").hasRole("7022").
                and().authorizeRequests().anyRequest().authenticated().
                and().formLogin().loginPage("/login").successForwardUrl("/toIndex").permitAll()
                .and().logout().logoutUrl("/logout").invalidateHttpSession(true).deleteCookies().permitAll()
        ;

        http.addFilterBefore(new CustomerAuthFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);


    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //設置自定義userService
        auth.userDetailsService(userService);
        auth.authenticationProvider(new UserJoinTimeAuthenticationProvider(userService));
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }
}
View Code

  在這里面我們通過HttpSecurity的方法來添加我們自定義的filter,一定要注意先后順序。在AuthenticationManagerBuilder當中還需要添加我們剛才定義的 AuthenticationProvider

 

啟動成功后,我們將Student表里的jointime值改為早於今天的時間,進行登錄可以發現:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM