SpringBoot之自定義驗證碼


代碼地址如下:
http://www.demodashi.com/demo/14280.html

項目介紹

Spring Security是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,為應用系統提供聲明式的安全訪問控制功能,減少了為企業系統安全控制編寫大量重復代碼的工作。
Spring Security的核心功能就是對用戶進行身份認證和授權。而他的認證機制就是通過Spring Security一系列的過濾器鏈,當一個請求來的時候,首先要通過過濾器鏈的校驗,校驗通過之后才會訪問用戶各種信息

但是在進行一些登錄,注冊等操作的時候,往往還需要使用驗證碼來進行校驗。本案例采用的面向接口編程,將自定義的驗證碼過濾器添加到Spring Security的過濾器鏈上來實現靈活的,可擴展,可重用,可配置的驗證碼驗證。

項目結構

項目實現過程

1. 首先,定義圖片驗證碼實體類。用來封裝驗證碼相關信息
public class ImageCode{
	
    private BufferedImage image;
	//失效時間
    private LocalDateTime expireTime;
	//驗證碼
    private String code;

    public ImageCode(BufferedImage image, String code, int expireIn){
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
        this.image = image;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }


    public LocalDateTime getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(LocalDateTime expireTime) {
        this.expireTime = expireTime;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }

    public boolean isExpried() {
        return LocalDateTime.now().isAfter(expireTime);
    }
}
2.驗證碼屬性配置。
  • 系統配置
 @ConfigurationProperties(prefix = "jcohy.security")
public class SecurityProperties {

    /**
     * 驗證碼配置
     */
    private ValidateCodeProperties validateCode = new ValidateCodeProperties();

    public ValidateCodeProperties getValidateCode() {
        return validateCode;
    }

    public void setValidateCode(ValidateCodeProperties validateCode) {
        this.validateCode = validateCode;
    }
}
  • 驗證碼配置。方便大家進行擴展。如要添加其他驗證配置,添加其他配置屬性類即可
public class ValidateCodeProperties {
    /**
     * 圖片驗證碼選項
     */
    private ImageCodeProperties imageCode = new ImageCodeProperties();

    public ImageCodeProperties getImageCode() {
        return imageCode;
    }

    public void setImageCode(ImageCodeProperties imageCode) {
        this.imageCode = imageCode;
    }
}
  • 圖片驗證碼配置
public class ImageCodeProperties{

    private int length = 4;

    private int expireIn = 60;
    /**
     * 驗證碼的寬
     */
    private int width = 67;

    /**
     * 驗證碼的高
     */
    private int height = 23;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getExpireIn() {
        return expireIn;
    }

    public void setExpireIn(int expireIn) {
        this.expireIn = expireIn;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}
  • 使配置生效
 @Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class PropertiesConfig {

}
3.關於驗證碼配置的說明
  • 驗證碼的配置分為三個級別,
  • 默認配置:當什么都不配置時,采用默認配置
  • 應用級配置:配置在application.properties文件中。例如:jcohy.security.validateCode.imageCode.width=100
  • 請求級配置:在請求中攜帶驗證碼相關參數。例如:"/code/image?width=100"。說明指定驗證碼圖片寬度為100px。

其優先級如下:請求級配置>應用級配置>默認配置。當前一個存在時,會覆蓋后面的配置。

4.好了,前面已經對驗證碼的基本屬性和配置已經編寫完成,接下來根據配置文件屬性生成驗證碼圖片。此處采用的是面向接口編程的方法,方便大家定制不同的驗證碼生成策略,多種方式驗證。使用更高級的驗證碼策略等。我這里只提供了一種簡單的實現。
public interface ValidateCodeGenerator {
    /**
     * 圖形驗證碼實現方法接口
     * @param request
     * @return
     */
    ImageCode generate(ServletWebRequest request);
}
public class ImageCodeGenerator implements ValidateCodeGenerator{

    /**
     * 系統配置
     */
    @Autowired
    private SecurityProperties securityProperties;

    @Override
    public ImageCode generate(ServletWebRequest request) {
     ....
	 ....
	 //繪制並生成圖片驗證碼過程
	 ....
	 ....
    }

    public SecurityProperties getSecurityProperties() {
        return securityProperties;
    }

    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }
}
5.圖片驗證碼生成的過程已經完成,接下來就是對驗證碼進行驗證了。我們使用過濾器來進行驗證
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

    /**
     * 驗證碼校驗失敗處理器
     */
    private AuthenticationFailureHandler authenticationFailureHandler;

    private SecurityProperties securityProperties;

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
   //需要進行驗證的的請求地址。
    private Set<String> urls = new HashSet<>();

    /**
     * 驗證請求url與配置的url是否匹配的工具類
     */
    private AntPathMatcher pathMatcher = new AntPathMatcher();
    
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
		//“/authentication/form”為登錄請求。
        urls.add("/authentication/form");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        boolean action = false;
        for(String url:urls){
            if(pathMatcher.match(url,request.getRequestURI())){
                action = true;
            }
        }

        if(action){
            try{
                validate(new ServletWebRequest(request));
            }catch (ValidateCodeException e){
                authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                return;
            }
        }
        chain.doFilter(request, response);

    }

    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, UserController.SESSION_KEY);
        String codeInRequest;
        try {
            codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
                    "imageCode");
        } catch (ServletRequestBindingException e) {
            throw new ValidateCodeException("獲取驗證碼的值失敗");
        }

        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("imageCode:驗證碼的值不能為空");
        }

        if (codeInSession == null) {
            throw new ValidateCodeException("imageCode:驗證碼不存在");
        }

        if (codeInSession.isExpried()) {
            sessionStrategy.removeAttribute(request,UserController.SESSION_KEY);
            throw new ValidateCodeException("imageCode: 驗證碼已過期");
        }

        if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
            throw new ValidateCodeException("imageCode:驗證碼不匹配");
        }
        sessionStrategy.removeAttribute(request,UserController.SESSION_KEY);
    }

    public AuthenticationFailureHandler getAuthenticationFailureHandler() {
        return authenticationFailureHandler;
    }

    public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }

    public SecurityProperties getSecurityProperties() {
        return securityProperties;
    }

    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }
}
  • 將生成驗證碼的Bean加入到容器中
@Configuration
public class ValidateCodeBeanConfig {

    @Autowired
    private SecurityProperties securityProperties;
	//條件匹配。此處可擴展其他的驗證條件和相應生成器
    @Bean
    @ConditionalOnMissingBean(name = "imageCodeGenerator")
    public ValidateCodeGenerator imageCodeGenerator(){
        ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator();
        imageCodeGenerator.setSecurityProperties(securityProperties);
        return imageCodeGenerator;
    }
}
6。將過濾器添加到過濾器鏈上
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 驗證碼校驗失敗處理器
     */
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private AuthenticationSuccessHandler JcohyAuthenticationSuccessHandler;
    /**
     * 系統配置信息
     */
    @Autowired
    private SecurityProperties securityProperties;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        validateCodeFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
        validateCodeFilter.setSecurityProperties(securityProperties);
        validateCodeFilter.afterPropertiesSet();
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
            .formLogin()
            .loginPage("/signIn.html")
            .loginProcessingUrl("/authentication/form")
            .successHandler(JcohyAuthenticationSuccessHandler)
            .and()
            .authorizeRequests()
            .antMatchers("/signIn.html"
            ,"/code/image").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .csrf().disable();
    }
}

項目運行

  • 本項目使用IntelliJ IDEA工具。使用maven構建。下載后導入到工具中可直接運行
  • 運行成功后訪問http://localhost:8080,出現如下界面
  • 本項目主要側重於驗證碼的生成和驗證,用戶部分比較簡單,用戶名隨便寫,密碼是123456,想要修改密碼可以在MyUserDetailService類中修改。
    SpringBoot之自定義驗證碼

代碼地址如下:
http://www.demodashi.com/demo/14280.html

注:本文著作權歸作者,由demo大師代發,拒絕轉載,轉載需要作者授權


免責聲明!

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



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