查看: 2262|回复: 0

[Java语言] Spring Security 图片验证码功能的实例代码

发表于 2018-4-16 08:00:05

验证码逻辑

以前在项目中也做过验证码,生成验证码的代码网上有很多,也有一些第三方的jar包也可以生成漂亮的验证码。验证码逻辑很简单,就是在登录页放一个image标签,src指向一个controller,这个Controller返回把生成的图片以输出流返回给页面,生成图片的同时把图片上的文本放在session,登录的时候带过来输入的验证码,从session中取出,两者对比。这位老师讲的用Spring Security集成验证码,大体思路和我说的一样,但更加规范和通用些。

spring security是一系列的过滤器链,所以在这里验证码也声明为过滤器,加在过滤器链的 登录过滤器之前,然后自定义一个异常类,来响应验证码的错误信息。

代码结构:

验证码代码放在core项目,在browser项目做一下配置。

主要代码:

1,ImageCode:

首先是ImageCode类,封装验证码图片、文本、过期时间

  1. package com.imooc.security.core.validate.code;
  2. import java.awt.image.BufferedImage;
  3. import java.time.LocalDateTime;
  4. import java.time.LocalTime;
  5. /**
  6. * 验证码
  7. * ClassName: ImageCode
  8. * @Description: 验证码
  9. * @author lihaoyang
  10. * @date 2018年3月1日
  11. */
  12. public class ImageCode {
  13. private BufferedImage image;
  14. private String code;
  15. private LocalDateTime expireTime;//过期时间点
  16. /**
  17. *
  18. * <p>Description: </p>
  19. * @param image
  20. * @param code
  21. * @param expireTn 多少秒过期
  22. */
  23. public ImageCode(BufferedImage image, String code, int expireTn) {
  24. super();
  25. this.image = image;
  26. this.code = code;
  27. //过期时间=当前时间+过期秒数
  28. this.expireTime = LocalDateTime.now().plusSeconds(expireTn);
  29. }
  30. public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
  31. super();
  32. this.image = image;
  33. this.code = code;
  34. this.expireTime = expireTime;
  35. }
  36. /**
  37. * 验证码是否过期
  38. * @Description: 验证码是否过期
  39. * @param @return true 过期,false 没过期
  40. * @return boolean true 过期,false 没过期
  41. * @throws
  42. * @author lihaoyang
  43. * @date 2018年3月2日
  44. */
  45. public boolean isExpired(){
  46. return LocalDateTime.now().isAfter(expireTime);
  47. }
  48. public BufferedImage getImage() {
  49. return image;
  50. }
  51. public void setImage(BufferedImage image) {
  52. this.image = image;
  53. }
  54. public String getCode() {
  55. return code;
  56. }
  57. public void setCode(String code) {
  58. this.code = code;
  59. }
  60. public LocalDateTime getExpireTime() {
  61. return expireTime;
  62. }
  63. public void setExpireTime(LocalDateTime expireTime) {
  64. this.expireTime = expireTime;
  65. }
  66. }
复制代码

VerifyCode:生成验证码的工具类,在这里http://www.cnblogs.com/lihaoyang/p/7131512.html 当然也可以使用第三方jar包,无所谓。

ValidateCodeException:封装验证码异常

  1. /**
  2. * @Title: ValidateCodeException.java
  3. * @Package com.imooc.security.core.validate.code
  4. * @Description: TODO
  5. * @author lihaoyang
  6. * @date 2018年3月2日
  7. */
  8. package com.imooc.security.core.validate.code;
  9. import org.springframework.security.core.AuthenticationException;
  10. /**
  11. * ClassName: ValidateCodeException
  12. * @Description: 验证码错误异常,继承spring security的认证异常
  13. * @author lihaoyang
  14. * @date 2018年3月2日
  15. */
  16. public class ValidateCodeException extends AuthenticationException {
  17. /**
  18. * @Fields serialVersionUID : TODO
  19. */
  20. private static final long serialVersionUID = 1L;
  21. public ValidateCodeException(String msg) {
  22. super(msg);
  23. }
  24. }
复制代码

ValidateCodeFilter:验证码过滤器

逻辑:继承OncePerRequestFilter 保证过滤器每次只会被调用一次(不太清楚为什么),注入认证失败处理器,在验证失败时调用。

  1. package com.imooc.security.core.validate.code;
  2. import java.io.IOException;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import org.apache.commons.lang.StringUtils;
  8. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  9. import org.springframework.social.connect.web.HttpSessionSessionStrategy;
  10. import org.springframework.social.connect.web.SessionStrategy;
  11. import org.springframework.web.bind.ServletRequestBindingException;
  12. import org.springframework.web.bind.ServletRequestUtils;
  13. import org.springframework.web.context.request.ServletWebRequest;
  14. import org.springframework.web.filter.OncePerRequestFilter;
  15. /**
  16. * 处理登录验证码过滤器
  17. * ClassName: ValidateCodeFilter
  18. * @Description:
  19. * OncePerRequestFilter:spring提供的工具,保证过滤器每次只会被调用一次
  20. * @author lihaoyang
  21. * @date 2018年3月2日
  22. */
  23. public class ValidateCodeFilter extends OncePerRequestFilter{
  24. //认证失败处理器
  25. private AuthenticationFailureHandler authenticationFailureHandler;
  26. //获取session工具类
  27. private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
  28. @Override
  29. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  30. throws ServletException, IOException {
  31. //如果是 登录请求 则执行
  32. if(StringUtils.equals("/authentication/form", request.getRequestURI())
  33. &&StringUtils.equalsIgnoreCase(request.getMethod(), "post")){
  34. try {
  35. validate(new ServletWebRequest(request));
  36. } catch (ValidateCodeException e) {
  37. //调用错误处理器,最终调用自己的
  38. authenticationFailureHandler.onAuthenticationFailure(request, response, e);
  39. return ;//结束方法,不再调用过滤器链
  40. }
  41. }
  42. //不是登录请求,调用其它过滤器链
  43. filterChain.doFilter(request, response);
  44. }
  45. /**
  46. * 校验验证码
  47. * @Description: 校验验证码
  48. * @param @param request
  49. * @param @throws ServletRequestBindingException
  50. * @return void
  51. * @throws ValidateCodeException
  52. * @author lihaoyang
  53. * @date 2018年3月2日
  54. */
  55. private void validate(ServletWebRequest request) throws ServletRequestBindingException {
  56. //拿出session中的ImageCode对象
  57. ImageCode imageCodeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);
  58. //拿出请求中的验证码
  59. String imageCodeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
  60. //校验
  61. if(StringUtils.isBlank(imageCodeInRequest)){
  62. throw new ValidateCodeException("验证码不能为空");
  63. }
  64. if(imageCodeInSession == null){
  65. throw new ValidateCodeException("验证码不存在,请刷新验证码");
  66. }
  67. if(imageCodeInSession.isExpired()){
  68. //从session移除过期的验证码
  69. sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
  70. throw new ValidateCodeException("验证码已过期,请刷新验证码");
  71. }
  72. if(!StringUtils.equalsIgnoreCase(imageCodeInSession.getCode(), imageCodeInRequest)){
  73. throw new ValidateCodeException("验证码错误");
  74. }
  75. //验证通过,移除session中验证码
  76. sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
  77. }
  78. public AuthenticationFailureHandler getAuthenticationFailureHandler() {
  79. return authenticationFailureHandler;
  80. }
  81. public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
  82. this.authenticationFailureHandler = authenticationFailureHandler;
  83. }
  84. }
复制代码

ValidateCodeController:生成验证码Control

  1. package com.imooc.security.core.validate.code;
  2. import java.io.IOException;
  3. import javax.imageio.ImageIO;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import org.springframework.social.connect.web.HttpSessionSessionStrategy;
  7. import org.springframework.social.connect.web.SessionStrategy;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import org.springframework.web.context.request.ServletWebRequest;
  11. /**
  12. * 验证码Control
  13. * ClassName: ValidateCodeController
  14. * @Description: TODO
  15. * @author lihaoyang
  16. * @date 2018年3月1日
  17. */
  18. @RestController
  19. public class ValidateCodeController {
  20. public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
  21. //获取session
  22. private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
  23. @GetMapping("/verifycode/image")
  24. public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException{
  25. ImageCode imageCode = createImageCode(request, response);
  26. sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
  27. ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
  28. }
  29. private ImageCode createImageCode(HttpServletRequest request, HttpServletResponse response) {
  30. VerifyCode verifyCode = new VerifyCode();
  31. return new ImageCode(verifyCode.getImage(),verifyCode.getText(),60);
  32. }
  33. }
复制代码

BrowserSecurityConfig里进行过滤器配置:

  1. package com.imooc.security.browser;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  6. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  7. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  8. import org.springframework.security.crypto.password.PasswordEncoder;
  9. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  10. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  11. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  12. import com.imooc.security.core.properties.SecurityProperties;
  13. import com.imooc.security.core.validate.code.ValidateCodeFilter;
  14. @Configuration //这是一个配置
  15. public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
  16. //读取用户配置的登录页配置
  17. @Autowired
  18. private SecurityProperties securityProperties;
  19. //自定义的登录成功后的处理器
  20. @Autowired
  21. private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
  22. //自定义的认证失败后的处理器
  23. @Autowired
  24. private AuthenticationFailureHandler imoocAuthenticationFailureHandler;
  25. //注意是org.springframework.security.crypto.password.PasswordEncoder
  26. @Bean
  27. public PasswordEncoder passwordencoder(){
  28. //BCryptPasswordEncoder implements PasswordEncoder
  29. return new BCryptPasswordEncoder();
  30. }
  31. //版本二:可配置的登录页
  32. @Override
  33. protected void configure(HttpSecurity http) throws Exception {
  34. //验证码过滤器
  35. ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
  36. //验证码过滤器中使用自己的错误处理
  37. validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
  38. //实现需要认证的接口跳转表单登录,安全=认证+授权
  39. //http.httpBasic() //这个就是默认的弹框认证
  40. //
  41. http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)//把验证码过滤器加载登录过滤器前边
  42. .formLogin() //表单认证
  43. .loginPage("/authentication/require") //处理用户认证BrowserSecurityController
  44. //登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改
  45. .loginProcessingUrl("/authentication/form")
  46. .successHandler(imoocAuthenticationSuccessHandler)//自定义的认证后处理器
  47. .failureHandler(imoocAuthenticationFailureHandler) //登录失败后的处理
  48. .and()
  49. .authorizeRequests() //下边的都是授权的配置
  50. // /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页
  51. .antMatchers("/authentication/require",
  52. securityProperties.getBrowser().getLoginPage(),//放过登录页不过滤,否则报错
  53. "/verifycode/image").permitAll() //验证码
  54. .anyRequest() //任何请求
  55. .authenticated() //都需要身份认证
  56. .and()
  57. .csrf().disable() //关闭csrf防护
  58. ;
  59. }
  60. }
复制代码

登陆页:登陆页做的比较粗糙,其实验证码可以在验证码input失去焦点的时候做校验,还可以做个点击图片刷新验证码功能,这里就不做了。

  1. <body>
  2. demo 登录页. <br>
  3. <form action="/authentication/form" method="post">
  4. <table>
  5. <tr>
  6. <td>用户名:</td>
  7. <td><input type="text" name="username"/></td>
  8. <td></td>
  9. </tr>
  10. <tr>
  11. <td>密码:</td>
  12. <td><input type="password" name="password"/></td>
  13. <td></td>
  14. </tr>
  15. <tr>
  16. <td>验证码:</td>
  17. <td>
  18. <input width="100" type="text" name="imageCode"/>
  19. </td>
  20. <td>
  21. <img src="/verifycode/image"/>
  22. </td>
  23. </tr>
  24. <tr>
  25. <td colspan="2" align="right"><button type="submit">登录</button></td>
  26. </tr>
  27. </table>
  28. </form>
  29. </body>
复制代码

访问 http://localhost:8080/demo-login.html:

响应自定义的异常信息

大体功能已经没问题了。但是不够通用,比如验证码图片的宽高、过期时间、过滤的url、验证码成逻辑都是写死的。这些可以做成活的,现在把验证码做成一个过滤器的好处体现出来了。我们可以配置需要过滤的url,有时候可能不只是登陆页需要验证码,这样更加通用。

1,通用性改造 之 验证码基本参数可配

做成可配置的,那个应用引用该模块,他自己配置去,不配置就使用默认配置。而且,配置既可以在请求url中声明,也可以在应用中声明,老师的确是老师,代码通用性真好!

想要实现的效果是,在application.properties里做这样的配置:

  1. #验证码 图片宽、高、字符个数
  2. imooc.security.code.image.width = 100
  3. imooc.security.code.image.height = 30
  4. imooc.security.code.image.length = 6
复制代码

然后就能控制验证码的效果,因为验证码还分图片验证码、短信验证码,所以多做了一级.code.image,这就用到了springboot的自定义配置文件,需要声明对应的java类:

需要在SecurityProperties里声明code属性:

  1. package com.imooc.security.core.properties;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. import org.springframework.context.annotation.Configuration;
  4. /**
  5. * 自定义配置项
  6. * ClassName: SecurityProperties
  7. * @Description: 自定义配置项
  8. * 这个类会读取application.properties里所有以imooc.security开头的配置项
  9. *
  10. * imooc.security.browser.loginPage = /demo-login.html
  11. * 其中的browser的配置会读取到BrowserProperties中去
  12. * 这是以点分割的,一级一级的和类的属性对应
  13. * @author lihaoyang
  14. * @date 2018年2月28日
  15. */
  16. @ConfigurationProperties(prefix="imooc.security")
  17. public class SecurityProperties {
  18. private BrowserProperties browser = new BrowserProperties();
  19. private ValidateCodeProperties code = new ValidateCodeProperties();
  20. public BrowserProperties getBrowser() {
  21. return browser;
  22. }
  23. public void setBrowser(BrowserProperties browser) {
  24. this.browser = browser;
  25. }
  26. public ValidateCodeProperties getCode() {
  27. return code;
  28. }
  29. public void setCode(ValidateCodeProperties code) {
  30. this.code = code;
  31. }
  32. }
复制代码

ValidateCodeProperties:

  1. package com.imooc.security.core.properties;
  2. /**
  3. * 验证码配置
  4. * ClassName: ValidateCodeProperties
  5. * @Description: 验证码配置,验证码有图片验证码、短信验证码等,所以再包一层
  6. * @author lihaoyang
  7. * @date 2018年3月2日
  8. */
  9. public class ValidateCodeProperties {
  10. //默认配置
  11. private ImageCodeProperties image = new ImageCodeProperties();
  12. public ImageCodeProperties getImage() {
  13. return image;
  14. }
  15. public void setImage(ImageCodeProperties image) {
  16. this.image = image;
  17. }
  18. }
复制代码

ImageCodeProperties:

  1. package com.imooc.security.core.properties;
  2. /**
  3. * 图片验证码配置类
  4. * ClassName: ImageCodeProperties
  5. * @Description: 图片验证码配置类
  6. * @author lihaoyang
  7. * @date 2018年3月2日
  8. */
  9. public class ImageCodeProperties {
  10. //图片宽
  11. private int width = 67;
  12. //图片高
  13. private int height = 23;
  14. //验证码字符个数
  15. private int length = 4;
  16. //过期时间
  17. private int expireIn = 60;
  18. public int getWidth() {
  19. return width;
  20. }
  21. public void setWidth(int width) {
  22. this.width = width;
  23. }
  24. public int getHeight() {
  25. return height;
  26. }
  27. public void setHeight(int height) {
  28. this.height = height;
  29. }
  30. public int getLength() {
  31. return length;
  32. }
  33. public void setLength(int length) {
  34. this.length = length;
  35. }
  36. public int getExpireIn() {
  37. return expireIn;
  38. }
  39. public void setExpireIn(int expireIn) {
  40. this.expireIn = expireIn;
  41. }
  42. }
复制代码

请求级的配置,如果请求里带的有验证码的参数,就用请求里的:

在ValidateCodeController的createImageCode方法做控制,判断请求参数是否有这些参数,有的话,传给验证码生成类VerifyCode,在生成的时候就能动态控制了。

  1. private ImageCode createImageCode(HttpServletRequest request, HttpServletResponse response) {
  2. //先从request里读取有没有长、宽、字符个数参数,有的话就用,没有用默认的
  3. int width = ServletRequestUtils.getIntParameter(request, "width",securityProperties.getCode().getImage().getWidth());
  4. int height = ServletRequestUtils.getIntParameter(request, "height",securityProperties.getCode().getImage().getHeight());
  5. int charLength = this.securityProperties.getCode().getImage().getLength();
  6. VerifyCode verifyCode = new VerifyCode(width,height,charLength);
  7. return new ImageCode(verifyCode.getImage(),verifyCode.getText(),this.securityProperties.getCode().getImage().getExpireIn());
  8. }
复制代码

VerifyCode:

  1. public VerifyCode(int w, int h, int charLength) {
  2. super();
  3. this.w = w;
  4. this.h = h;
  5. this.charLength = charLength;
  6. }
复制代码

实验:在demo项目做应用级配置

登录表单做请求级配置

  1. <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:

  1. /**
  2. * 处理登录验证码过滤器
  3. * ClassName: ValidateCodeFilter
  4. * @Description:
  5. * 继承OncePerRequestFilter:spring提供的工具,保证过滤器每次只会被调用一次
  6. * 实现 InitializingBean接口的目的:
  7. * 在其他参数都组装完毕的时候,初始化需要拦截的urls的值
  8. * @author lihaoyang
  9. * @date 2018年3月2日
  10. */
  11. public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{
  12. //认证失败处理器
  13. private AuthenticationFailureHandler authenticationFailureHandler;
  14. //获取session工具类
  15. private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
  16. //需要拦截的url集合
  17. private Set<String> urls = new HashSet<>();
  18. //读取配置
  19. private SecurityProperties securityProperties;
  20. //spring工具类
  21. private AntPathMatcher antPathMatcher = new AntPathMatcher();
  22. @Override
  23. public void afterPropertiesSet() throws ServletException {
  24. super.afterPropertiesSet();
  25. //读取配置的拦截的urls
  26. String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(), ",");
  27. for (String configUrl : configUrls) {
  28. urls.add(configUrl);
  29. }
  30. //登录的请求一定拦截
  31. urls.add("/authentication/form");
  32. }
  33. @Override
  34. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  35. throws ServletException, IOException {
  36. /**
  37. * 可配置的验证码校验
  38. * 判断请求的url和配置的是否有匹配的,匹配上了就过滤
  39. */
  40. boolean action = false;
  41. for(String url:urls){
  42. if(antPathMatcher.match(url, request.getRequestURI())){
  43. action = true;
  44. }
  45. }
  46. if(action){
  47. try {
  48. validate(new ServletWebRequest(request));
  49. } catch (ValidateCodeException e) {
  50. //调用错误处理器,最终调用自己的
  51. authenticationFailureHandler.onAuthenticationFailure(request, response, e);
  52. return ;//结束方法,不再调用过滤器链
  53. }
  54. }
  55. //不是登录请求,调用其它过滤器链
  56. filterChain.doFilter(request, response);
  57. }
  58. //省略无关代码,,,
  59. }
复制代码

BrowserSecurityConfig:

配置url:

  1. #验证码拦截的接口配置
  2. 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:

  1. package com.imooc.security.core.validate.code;
  2. import org.springframework.web.context.request.ServletWebRequest;
  3. /**
  4. * 验证码生成接口
  5. * ClassName: ValidateCodeGenerator
  6. * @Description: TODO
  7. * @author lihaoyang
  8. * @date 2018年3月2日
  9. */
  10. public interface ValidateCodeGenerator {
  11. /**
  12. * 图片验证码生成接口
  13. * @Description: TODO
  14. * @param @param request
  15. * @param @return
  16. * @return ImageCode
  17. * @throws
  18. * @author lihaoyang
  19. * @date 2018年3月2日
  20. */
  21. ImageCode generator(ServletWebRequest request);
  22. }
复制代码

图片验证码生成器实现ImageCodeGenerator:

  1. package com.imooc.security.core.validate.code;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.bind.ServletRequestUtils;
  5. import org.springframework.web.context.request.ServletWebRequest;
  6. import com.imooc.security.core.properties.SecurityProperties;
  7. /**
  8. * 图片验证码生成类
  9. * ClassName: ImageCodeGenerator
  10. * @Description: TODO
  11. * @author lihaoyang
  12. * @date 2018年3月2日
  13. */
  14. public class ImageCodeGenerator implements ValidateCodeGenerator {
  15. @Autowired
  16. private SecurityProperties securityProperties;
  17. @Override
  18. public ImageCode generator(ServletWebRequest request) {
  19. //先从request里读取有没有长、宽、字符个数参数,有的话就用,没有用默认的
  20. int width = ServletRequestUtils.getIntParameter(request.getRequest(), "width",securityProperties.getCode().getImage().getWidth());
  21. int height = ServletRequestUtils.getIntParameter(request.getRequest(), "height",securityProperties.getCode().getImage().getHeight());
  22. int charLength = this.securityProperties.getCode().getImage().getLength();
  23. VerifyCode verifyCode = new VerifyCode(width,height,charLength);
  24. return new ImageCode(verifyCode.getImage(),verifyCode.getText(),this.securityProperties.getCode().getImage().getExpireIn());
  25. }
  26. public SecurityProperties getSecurityProperties() {
  27. return securityProperties;
  28. }
  29. public void setSecurityProperties(SecurityProperties securityProperties) {
  30. this.securityProperties = securityProperties;
  31. }
  32. }
复制代码

ValidateCodeBeanConfig:

  1. package com.imooc.security.core.validate.code;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import com.imooc.security.core.properties.SecurityProperties;
  7. /**
  8. * 配置验证码生成接口ValidateCodeGenerator的实际实现类的Bean
  9. * ClassName: ValidateCodeBeanConfig
  10. * @Description:
  11. * 配置验证码生成接口ValidateCodeGenerator的实际实现类的Bean
  12. * 如图片验证码的实现、短信验证码的实现
  13. * @author lihaoyang
  14. * @date 2018年3月5日
  15. */
  16. @Configuration
  17. public class ValidateCodeBeanConfig {
  18. @Autowired
  19. private SecurityProperties securityProperties;
  20. /**
  21. * @Description:
  22. * @ConditionalOnMissingBean注解意思是当spring容器不存在imageCodeGenerator时才给配置一个该bean
  23. * 作用是使程序更具可扩展性,该配置类是配置在core模块,这就意味着,如果引用该模块的项目
  24. * 如果有一个自己的实现,实现了ValidateCodeGenerator接口,定义了自己的实现,名字也叫imageCodeGenerator时,
  25. * 就用应用级别的实现,没有的话就用这个默认实现。
  26. * @param @return
  27. * @return ValidateCodeGenerator
  28. * @throws
  29. * @author lihaoyang
  30. * @date 2018年3月5日
  31. */
  32. @Bean
  33. @ConditionalOnMissingBean(name="imageCodeGenerator")
  34. public ValidateCodeGenerator imageCodeGenerator(){
  35. ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
  36. codeGenerator.setSecurityProperties(securityProperties);
  37. return codeGenerator;
  38. }
  39. }
复制代码

这样,如果哪个模块引用了这个验证码模块,他自定义了实现,如:

  1. package com.imooc.code;
  2. import org.springframework.stereotype.Component;
  3. import org.springframework.web.context.request.ServletWebRequest;
  4. import com.imooc.security.core.validate.code.ImageCode;
  5. import com.imooc.security.core.validate.code.ValidateCodeGenerator;
  6. @Component("imageCodeGenerator")
  7. public class DemoImageCodeGenerator implements ValidateCodeGenerator {
  8. @Override
  9. public ImageCode generator(ServletWebRequest request) {
  10. System.err.println("demo项目实现的生成验证码,,,");
  11. return null;
  12. }
  13. }
复制代码

这样ValidateCodeBeanConfig在配置验证码bean时,就会使用使用者自定义的实现。

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

总结

以上所述是小编给大家介绍的Spring Security 图片验证码功能的实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对程序员之家网站的支持!



回复

使用道具 举报