Spring Security構建Rest服務-0800-Spring Security圖片驗證碼


驗證碼邏輯

以前在項目中也做過驗證碼,生成驗證碼的代碼網上有很多,也有一些第三方的jar包也可以生成漂亮的驗證碼。驗證碼邏輯很簡單,就是在登錄頁放一個image標簽,src指向一個controller,這個Controller返回把生成的圖片以輸出流返回給頁面,生成圖片的同時把圖片上的文本放在session,登錄的時候帶過來輸入的驗證碼,從session中取出,兩者對比。這位老師講的用Spring Security集成驗證碼,大體思路和我說的一樣,但更加規范和通用些。

spring security是一系列的過濾器鏈,所以在這里驗證碼也聲明為過濾器,加在過濾器鏈的 登錄過濾器之前,然后自定義一個異常類,來響應驗證碼的錯誤信息。

 

代碼結構:

驗證碼代碼放在core項目,在browser項目做一下配置。

主要代碼:

1,ImageCode:

 首先是ImageCode類,封裝驗證碼圖片、文本、過期時間

package com.imooc.security.core.validate.code;

import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
import java.time.LocalTime;

/**
 * 驗證碼
 * ClassName: ImageCode 
 * @Description: 驗證碼
 * @author lihaoyang
 * @date 2018年3月1日
 */
public class ImageCode {

    private BufferedImage image;
    
    private String code;
    
    private LocalDateTime expireTime;//過期時間點
    

    /**
     * 
     * <p>Description: </p>
     * @param image
     * @param code
     * @param expireTn 多少秒過期
     */
    public ImageCode(BufferedImage image, String code, int expireTn) {
        super();
        this.image = image;
        this.code = code;
        //過期時間=當前時間+過期秒數 
        this.expireTime = LocalDateTime.now().plusSeconds(expireTn);
    }

    
    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
        super();
        this.image = image;
        this.code = code;
        this.expireTime = expireTime;
    }

    /**
     * 驗證碼是否過期
     * @Description: 驗證碼是否過期
     * @param @return  true 過期,false 沒過期
     * @return boolean   true 過期,false 沒過期
     * @throws
     * @author lihaoyang
     * @date 2018年3月2日
     */
    public boolean isExpired(){
        return LocalDateTime.now().isAfter(expireTime);
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        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;
    }
    
    
}

VerifyCode:生成驗證碼的工具類,在這里http://www.cnblogs.com/lihaoyang/p/7131512.html 當然也可以使用第三方jar包,無所謂。

ValidateCodeException:封裝驗證碼異常

/**  
 * @Title: ValidateCodeException.java
 * @Package com.imooc.security.core.validate.code
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年3月2日
 */
package com.imooc.security.core.validate.code;

import org.springframework.security.core.AuthenticationException;

/**
 * ClassName: ValidateCodeException 
 * @Description: 驗證碼錯誤異常,繼承spring security的認證異常
 * @author lihaoyang
 * @date 2018年3月2日
 */
public class ValidateCodeException extends AuthenticationException {

    /**
     * @Fields serialVersionUID : TODO
     */
    private static final long serialVersionUID = 1L;

    public ValidateCodeException(String msg) {
        super(msg);
    }

}

ValidateCodeFilter:驗證碼過濾器

邏輯:繼承OncePerRequestFilter 保證過濾器每次只會被調用一次(不太清楚為什么),注入認證失敗處理器,在驗證失敗時調用。

package com.imooc.security.core.validate.code;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 處理登錄驗證碼過濾器
 * ClassName: ValidateCodeFilter 
 * @Description:
 *  OncePerRequestFilter:spring提供的工具,保證過濾器每次只會被調用一次
 * @author lihaoyang
 * @date 2018年3月2日
 */
public class ValidateCodeFilter extends OncePerRequestFilter{

    //認證失敗處理器
    private AuthenticationFailureHandler authenticationFailureHandler;

    //獲取session工具類
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        //如果是 登錄請求 則執行
        if(StringUtils.equals("/authentication/form", request.getRequestURI())
                &&StringUtils.equalsIgnoreCase(request.getMethod(), "post")){
            try {
                validate(new ServletWebRequest(request));
            } catch (ValidateCodeException e) {
                //調用錯誤處理器,最終調用自己的
                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
                return ;//結束方法,不再調用過濾器鏈
            }
        }
        //不是登錄請求,調用其它過濾器鏈
        filterChain.doFilter(request, response);
    }

    /**
     * 校驗驗證碼
     * @Description: 校驗驗證碼
     * @param @param request
     * @param @throws ServletRequestBindingException   
     * @return void  
     * @throws ValidateCodeException
     * @author lihaoyang
     * @date 2018年3月2日
     */
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        //拿出session中的ImageCode對象
        ImageCode imageCodeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);
        //拿出請求中的驗證碼
        String imageCodeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
        //校驗
        if(StringUtils.isBlank(imageCodeInRequest)){
            throw new ValidateCodeException("驗證碼不能為空");
        }
        if(imageCodeInSession == null){
            throw new ValidateCodeException("驗證碼不存在,請刷新驗證碼");
        }
        if(imageCodeInSession.isExpired()){
            //從session移除過期的驗證碼
            sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
            throw new ValidateCodeException("驗證碼已過期,請刷新驗證碼");
        }
        if(!StringUtils.equalsIgnoreCase(imageCodeInSession.getCode(), imageCodeInRequest)){
            throw new ValidateCodeException("驗證碼錯誤");
        }
        //驗證通過,移除session中驗證碼
        sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
    }

    public AuthenticationFailureHandler getAuthenticationFailureHandler() {
        return authenticationFailureHandler;
    }

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

ValidateCodeController:生成驗證碼Control

package com.imooc.security.core.validate.code;

import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

/**
 * 驗證碼Control
 * ClassName: ValidateCodeController 
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年3月1日
 */
@RestController
public class ValidateCodeController {
    
    public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";    
    
    //獲取session
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    @GetMapping("/verifycode/image")
    public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException{
        
        ImageCode imageCode = createImageCode(request, response);
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }

    
    private ImageCode createImageCode(HttpServletRequest request, HttpServletResponse response) {
        VerifyCode verifyCode = new VerifyCode();
        return new ImageCode(verifyCode.getImage(),verifyCode.getText(),60);
    }

}

BrowserSecurityConfig里進行過濾器配置:

package com.imooc.security.browser;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.ValidateCodeFilter;

@Configuration //這是一個配置
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
    
    //讀取用戶配置的登錄頁配置
    @Autowired
    private SecurityProperties securityProperties;
    
    //自定義的登錄成功后的處理器
    @Autowired
    private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
    
    //自定義的認證失敗后的處理器
    @Autowired
    private AuthenticationFailureHandler imoocAuthenticationFailureHandler;

    //注意是org.springframework.security.crypto.password.PasswordEncoder
    @Bean
    public PasswordEncoder passwordencoder(){
        //BCryptPasswordEncoder implements PasswordEncoder
        return new BCryptPasswordEncoder();
    }
    //版本二:可配置的登錄頁
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //驗證碼過濾器
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //驗證碼過濾器中使用自己的錯誤處理
        validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
        
        //實現需要認證的接口跳轉表單登錄,安全=認證+授權
        //http.httpBasic() //這個就是默認的彈框認證
        //
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)//把驗證碼過濾器加載登錄過濾器前邊
            .formLogin() //表單認證
            .loginPage("/authentication/require") //處理用戶認證BrowserSecurityController
            //登錄過濾器UsernamePasswordAuthenticationFilter默認登錄的url是"/login",在這能改
            .loginProcessingUrl("/authentication/form") 
            .successHandler(imoocAuthenticationSuccessHandler)//自定義的認證后處理器
            .failureHandler(imoocAuthenticationFailureHandler) //登錄失敗后的處理
            .and()
            .authorizeRequests() //下邊的都是授權的配置
            // /authentication/require:處理登錄,securityProperties.getBrowser().getLoginPage():用戶配置的登錄頁
            .antMatchers("/authentication/require",
                    securityProperties.getBrowser().getLoginPage(),//放過登錄頁不過濾,否則報錯
                    "/verifycode/image").permitAll() //驗證碼
            .anyRequest()        //任何請求
            .authenticated()    //都需要身份認證
            .and()
            .csrf().disable() //關閉csrf防護
            ;    
    }
}

登陸頁:登陸頁做的比較粗糙,其實驗證碼可以在驗證碼input失去焦點的時候做校驗,還可以做個點擊圖片刷新驗證碼功能,這里就不做了。

<body>
    demo 登錄頁. <br>
    <form action="/authentication/form" method="post">
        <table>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username"/></td>
                <td></td>
            </tr>
            <tr>
                <td>密碼:</td>
                <td><input type="password" name="password"/></td>
                <td></td>
            </tr>
            <tr>
                <td>驗證碼:</td>
                <td>
                    <input width="100" type="text" name="imageCode"/>
                </td>
                <td>
                    <img src="/verifycode/image"/>
                </td>
            </tr>
            <tr>
                <td colspan="2" align="right"><button type="submit">登錄</button></td>
            </tr>
        </table>
    </form>
  </body>

訪問 http://localhost:8080/demo-login.html:

響應自定義的異常信息

大體功能已經沒問題了。但是不夠通用,比如驗證碼圖片的寬高、過期時間、過濾的url、驗證碼成邏輯都是寫死的。這些可以做成活的,現在把驗證碼做成一個過濾器的好處體現出來了。我們可以配置需要過濾的url,有時候可能不只是登陸頁需要驗證碼,這樣更加通用。

1,通用性改造 之 驗證碼基本參數可配

做成可配置的,那個應用引用該模塊,他自己配置去,不配置就使用默認配置。而且,配置既可以在請求url中聲明,也可以在應用中聲明,老師的確是老師,代碼通用性真好!

想要實現的效果是,在application.properties里做這樣的配置:

#驗證碼 圖片寬、高、字符個數
imooc.security.code.image.width = 100
imooc.security.code.image.height = 30
imooc.security.code.image.length = 6

然后就能控制驗證碼的效果,因為驗證碼還分圖片驗證碼、短信驗證碼,所以多做了一級.code.image,這就用到了springboot的自定義配置文件,需要聲明對應的java類:

需要在SecurityProperties里聲明code屬性:

package com.imooc.security.core.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 自定義配置項
 * ClassName: SecurityProperties 
 * @Description: 自定義配置項
 * 這個類會讀取application.properties里所有以imooc.security開頭的配置項
 * 
 * imooc.security.browser.loginPage = /demo-login.html
 * 其中的browser的配置會讀取到BrowserProperties中去
 * 這是以點分割的,一級一級的和類的屬性對應
 * @author lihaoyang
 * @date 2018年2月28日
 */

@ConfigurationProperties(prefix="imooc.security")
public class SecurityProperties {

    private BrowserProperties browser = new BrowserProperties();
    
    private ValidateCodeProperties code = new ValidateCodeProperties();
    

    public BrowserProperties getBrowser() {
        return browser;
    }

    public void setBrowser(BrowserProperties browser) {
        this.browser = browser;
    }

    public ValidateCodeProperties getCode() {
        return code;
    }

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

ValidateCodeProperties:

package com.imooc.security.core.properties;

/**
 * 驗證碼配置
 * ClassName: ValidateCodeProperties 
 * @Description: 驗證碼配置,驗證碼有圖片驗證碼、短信驗證碼等,所以再包一層
 * @author lihaoyang
 * @date 2018年3月2日
 */
public class ValidateCodeProperties {
    
    //默認配置
    private ImageCodeProperties image = new ImageCodeProperties();

    public ImageCodeProperties getImage() {
        return image;
    }

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

    
    
}

ImageCodeProperties:

package com.imooc.security.core.properties;

/**
 * 圖片驗證碼配置類
 * ClassName: ImageCodeProperties 
 * @Description: 圖片驗證碼配置類
 * @author lihaoyang
 * @date 2018年3月2日
 */
public class ImageCodeProperties {

    //圖片寬
    private int width = 67;
    //圖片高
    private int height = 23;
    //驗證碼字符個數
    private int length = 4;
    //過期時間
    private int expireIn = 60;

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

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

 

請求級的配置,如果請求里帶的有驗證碼的參數,就用請求里的:

在ValidateCodeController的createImageCode方法做控制,判斷請求參數是否有這些參數,有的話,傳給驗證碼生成類VerifyCode,在生成的時候就能動態控制了。

private ImageCode createImageCode(HttpServletRequest request, HttpServletResponse response) {
        //先從request里讀取有沒有長、寬、字符個數參數,有的話就用,沒有用默認的
        int width  = ServletRequestUtils.getIntParameter(request, "width",securityProperties.getCode().getImage().getWidth());
        
        int height = ServletRequestUtils.getIntParameter(request, "height",securityProperties.getCode().getImage().getHeight());
        
        int charLength = this.securityProperties.getCode().getImage().getLength();
        VerifyCode verifyCode = new VerifyCode(width,height,charLength);
        return new ImageCode(verifyCode.getImage(),verifyCode.getText(),this.securityProperties.getCode().getImage().getExpireIn());
    }

VerifyCode:

public VerifyCode(int w, int h, int charLength) {
        super();
        this.w = w;
        this.h = h;
        this.charLength = charLength;
    }

實驗:在demo項目做應用級配置

登錄表單做請求級配置

<img src="/verifycode/image?width=200"/>

訪問:

長度為請求級帶的參數200,高為30,字符為配置的6個。

2,通用性改造 之 驗證碼攔截的接口可配置

先要的效果就是再application.properties里能動態配置需要攔截的接口:

ImageCodeProperties新增一個屬性:private String url; //攔截的url,來匹配上圖的配置。

核心,驗證碼過濾器需要修改:

1,在攔截器里聲明一個set集合,用來存儲配置文件里配置的需要攔截的urls。

2,實現InitializingBean接口,目的: 在其他參數都組裝完畢的時候,初始化需要攔截的urls的值,重寫afterPropertiesSet方法來實現。

3,注入SecurityProperties,讀取配置文件

4,實例化AntPathMatcher工具類,這是一個匹配器

5,在browser項目的BrowserSecurityConfig里設置調用一下afterPropertiesSet方法。

6,在引用該模塊的demo項目的application.properties里配置要過濾的url

ValidateCodeFilter:

/**
 * 處理登錄驗證碼過濾器
 * ClassName: ValidateCodeFilter 
 * @Description:
 *  繼承OncePerRequestFilter:spring提供的工具,保證過濾器每次只會被調用一次
 *  實現 InitializingBean接口的目的:
 *      在其他參數都組裝完畢的時候,初始化需要攔截的urls的值
 * @author lihaoyang
 * @date 2018年3月2日
 */
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{

    //認證失敗處理器
    private AuthenticationFailureHandler authenticationFailureHandler;

    //獲取session工具類
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
    //需要攔截的url集合
    private Set<String> urls = new HashSet<>();
    //讀取配置
    private SecurityProperties securityProperties;
    //spring工具類
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        //讀取配置的攔截的urls
        String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(), ",");
        for (String configUrl : configUrls) {
            urls.add(configUrl);
        }
        //登錄的請求一定攔截
        urls.add("/authentication/form");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        
        /**
         * 可配置的驗證碼校驗
         * 判斷請求的url和配置的是否有匹配的,匹配上了就過濾
         */
        boolean action = false;
        for(String url:urls){
            if(antPathMatcher.match(url, request.getRequestURI())){
                action = true;
            }
        }
        if(action){
            try {
                validate(new ServletWebRequest(request));
            } catch (ValidateCodeException e) {
                //調用錯誤處理器,最終調用自己的
                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
                return ;//結束方法,不再調用過濾器鏈
            }
        }
        
        //不是登錄請求,調用其它過濾器鏈
        filterChain.doFilter(request, response);
    }

    //省略無關代碼,,,
    
}

BrowserSecurityConfig:

配置url:

#驗證碼攔截的接口配置
imooc.security.code.image.url = /user,/user/*

測試:/user  /user/1 被攔截了

訪問登錄頁,不寫驗證碼:

和預期一致。至此,動態配置攔截接口完成

3,驗證碼的生成邏輯可配置

 寫的比較好的程序,一般都開放接口,可以讓用戶去自定義實現,如果不實現就用默認的實現,下面來做這件事,使驗證碼的生成可以自己實現。如果要想把驗證碼的生成邏輯做成可配置的,就不能只寫一個圖片驗證碼生成器的類了,需要把驗證碼生成提取成一個接口ValidateCodeGenerator,一個生成驗證碼的方法generator()。因為驗證碼還有圖片驗證碼、短信驗證碼等,這樣,我們在自己的驗證模塊里做一個默認的實現,如圖片驗證碼的實現ImageCodeGenerator,在ImageCodeGenerator里我們不在該類上加@Component注解。然后使用寫一個驗證碼bean的配置類ValidateCodeBeanConfig,這個配置類配置各種需要的驗證碼實現類bean如圖片驗證碼實現imageCodeGenerator、短信驗證碼等,他們返回類型都是ValidateCodeGenerator,使用@ConditionalOnMissingBean(name="imageCodeGenerator")注解,可以判斷如果當前spring容器有名字為imageCodeGenerator的bean時,就使用,沒有的話再配置,這樣如果別人引用了你的該模塊,如果別人自己實現了驗證碼生成ValidateCodeGenerator接口,他們配置了實現類的nameimageCodeGenerator,就用他們自己的實現,這樣就做到了程序的可擴展性。

 

 主要代碼:

代碼生成器接口ValidateCodeGenerator:

package com.imooc.security.core.validate.code;

import org.springframework.web.context.request.ServletWebRequest;

/**
 * 驗證碼生成接口
 * ClassName: ValidateCodeGenerator 
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年3月2日
 */
public interface ValidateCodeGenerator {

    /**
     * 圖片驗證碼生成接口
     * @Description: TODO
     * @param @param request
     * @param @return   
     * @return ImageCode  
     * @throws
     * @author lihaoyang
     * @date 2018年3月2日
     */
    ImageCode generator(ServletWebRequest request);
}

圖片驗證碼生成器實現ImageCodeGenerator:

package com.imooc.security.core.validate.code;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import com.imooc.security.core.properties.SecurityProperties;

/**
 * 圖片驗證碼生成類
 * ClassName: ImageCodeGenerator 
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年3月2日
 */
public class ImageCodeGenerator implements ValidateCodeGenerator {

    @Autowired
    private SecurityProperties securityProperties;
    
    @Override
    public ImageCode generator(ServletWebRequest request) {
        //先從request里讀取有沒有長、寬、字符個數參數,有的話就用,沒有用默認的
        int width  = ServletRequestUtils.getIntParameter(request.getRequest(), "width",securityProperties.getCode().getImage().getWidth());
        
        int height = ServletRequestUtils.getIntParameter(request.getRequest(), "height",securityProperties.getCode().getImage().getHeight());
        
        int charLength = this.securityProperties.getCode().getImage().getLength();
        VerifyCode verifyCode = new VerifyCode(width,height,charLength);
        return new ImageCode(verifyCode.getImage(),verifyCode.getText(),this.securityProperties.getCode().getImage().getExpireIn());
    }

    public SecurityProperties getSecurityProperties() {
        return securityProperties;
    }

    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }
    

}

ValidateCodeBeanConfig:

package com.imooc.security.core.validate.code;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.imooc.security.core.properties.SecurityProperties;

/**
 * 配置驗證碼生成接口ValidateCodeGenerator的實際實現類的Bean
 * ClassName: ValidateCodeBeanConfig 
 * @Description: 
 *     配置驗證碼生成接口ValidateCodeGenerator的實際實現類的Bean
 *     如圖片驗證碼的實現、短信驗證碼的實現
 * @author lihaoyang
 * @date 2018年3月5日
 */
@Configuration
public class ValidateCodeBeanConfig {

    @Autowired
    private SecurityProperties securityProperties;
    
    /**
     * @Description: 
     * @ConditionalOnMissingBean注解意思是當spring容器不存在imageCodeGenerator時才給配置一個該bean
     * 作用是使程序更具可擴展性,該配置類是配置在core模塊,這就意味着,如果引用該模塊的項目
     * 如果有一個自己的實現,實現了ValidateCodeGenerator接口,定義了自己的實現,名字也叫imageCodeGenerator時,
     * 就用應用級別的實現,沒有的話就用這個默認實現。
     * @param @return   
     * @return ValidateCodeGenerator  
     * @throws
     * @author lihaoyang
     * @date 2018年3月5日
     */
    @Bean
    @ConditionalOnMissingBean(name="imageCodeGenerator") 
    public ValidateCodeGenerator imageCodeGenerator(){ 
        ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
        codeGenerator.setSecurityProperties(securityProperties);
        return codeGenerator;
    }
}

這樣,如果哪個模塊引用了這個驗證碼模塊,他自定義了實現,如:

package com.imooc.code;

import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;

import com.imooc.security.core.validate.code.ImageCode;
import com.imooc.security.core.validate.code.ValidateCodeGenerator;

@Component("imageCodeGenerator")
public class DemoImageCodeGenerator implements ValidateCodeGenerator {

    @Override
    public ImageCode generator(ServletWebRequest request) {
        System.err.println("demo項目實現的生成驗證碼,,,");
        
        return null;
    }

}

這樣ValidateCodeBeanConfig在配置驗證碼bean時,就會使用使用者自定義的實現。

完整代碼放在了github:https://github.com/lhy1234/spring-security

 


免責聲明!

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



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