驗證碼邏輯
以前在項目中也做過驗證碼,生成驗證碼的代碼網上有很多,也有一些第三方的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接口,他們配置了實現類的name為imageCodeGenerator,就用他們自己的實現,這樣就做到了程序的可擴展性。
主要代碼:
代碼生成器接口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