【转载】spring-security-oauth2(四) 图片验证码

图片验证码

  1. 图片验证码生成接口
  2. 认证流程加入图片验证码校验
  3. 图片验证码重构

1.图片验证码生成接口

  1. 调用com.google.code.kaptcha.Producer生成图片验证码
  2. 将随机数存到session缓存中
  3. 将生成的图片写到响应流中

图片验证码封装类  ImageCaptchaVo


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import lombok.Data;
  3. import java.awt.image.BufferedImage;
  4. import java.time.LocalDateTime;
  5. /**
  6. * 图片验证码信息对象
  7. * @author CaiRui
  8. * @Date 2018/12/9 18:03
  9. */
  10. @Data
  11. public class ImageCaptchaVo {
  12. /**
  13. * 图片验证码
  14. */
  15. private BufferedImage image;
  16. /**
  17. * 验证码
  18. */
  19. private String code;
  20. /**
  21. * 失效时间 这个通常放在缓存中或维护在数据库中
  22. */
  23. private LocalDateTime expireTime;
  24. public ImageCaptchaVo(BufferedImage image, String code, int expireAfterSeconds){
  25. this.image = image;
  26. this.code = code;
  27. //多少秒后
  28. this.expireTime = LocalDateTime.now().plusSeconds(expireAfterSeconds);
  29. }
  30. public ImageCaptchaVo(BufferedImage image, String code, LocalDateTime expireTime){
  31. this.image = image;
  32. this.code = code;
  33. this.expireTime = expireTime;
  34. }
  35. /**
  36. * 是否失效
  37. * @return
  38. */
  39. public boolean isExpried() {
  40. return LocalDateTime.now().isAfter(expireTime);
  41. }
  42. }

图片验证码服务类  CaptchaController  注意这个路径/captcha/image要在BrowserSecurityConfig配置中放行


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.social.connect.web.HttpSessionSessionStrategy;
  4. import org.springframework.social.connect.web.SessionStrategy;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.context.request.ServletWebRequest;
  8. import javax.imageio.ImageIO;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import java.io.IOException;
  12. /**
  13. * 验证码控制器
  14. * @author CaiRui
  15. * @date 2018-12-10 12:13
  16. */
  17. @RestController
  18. public class CaptchaController {
  19. public static final String IMAGE_CAPTCHA_SESSION_KEY= "image_captcha_session_key";
  20. private static final String FORMAT_NAME= "JPEG";
  21. @Autowired
  22. private CaptchaGenerate captchaGenerate;
  23. //spring session 工具类
  24. private SessionStrategy sessionStrategy= new HttpSessionSessionStrategy();
  25. /**
  26. * 获取图片验证码
  27. * @param request
  28. * @param response
  29. * @throws IOException
  30. */
  31. @GetMapping( "/captcha/image")
  32. public void createKaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
  33. //1.接口生成验证码
  34. ImageCaptchaVo imageCaptcha= (ImageCaptchaVo) captchaGenerate.generate();
  35. //2.保存到session中
  36. sessionStrategy.setAttribute( new ServletWebRequest(request), IMAGE_CAPTCHA_SESSION_KEY, imageCaptcha);
  37. //3.写到响应流中
  38. response.setHeader( "Cache-Control", "no-store, no-cache"); // 没有缓存
  39. response.setContentType( "image/jpeg");
  40. ImageIO.write(imageCaptcha.getImage(),FORMAT_NAME,response.getOutputStream());
  41. }
  42. }

  CaptchaGenerate 接口及其实现类


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. /**
  3. * 验证码生成接口
  4. *
  5. * @author CaiRui
  6. * @date 2018-12-10 12:03
  7. */
  8. public interface CaptchaGenerate {
  9. /**
  10. * 生成图片验证码
  11. *
  12. * @return
  13. */
  14. ImageCaptchaVo generate();
  15. }

 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import com.google.code.kaptcha.Producer;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import java.awt.image.BufferedImage;
  6. /**
  7. * 图片验证码生成接口
  8. *
  9. * @author CaiRui
  10. * @date 2018-12-10 12:07
  11. */
  12. @Service( "imageCaptchaGenerate")
  13. public class ImageCaptchaGenerate implements CaptchaGenerate {
  14. @Autowired
  15. private Producer producer; //config bean中配置
  16. @Override
  17. public ImageCaptchaVo generate() {
  18. String code = producer.createText();
  19. BufferedImage bufferedImage = producer.createImage(code);
  20. return new ImageCaptchaVo(bufferedImage, code, 60 * 5); //5分钟过期
  21. }
  22. }

Producer配置类 KaptchaGenerateConfig


 
 
  1. package com.rui.tiger.auth.core.config;
  2. import com.google.code.kaptcha.impl.DefaultKaptcha;
  3. import com.google.code.kaptcha.util.Config;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import java.util.Properties;
  7. /**
  8. * 验证码生成配置类
  9. * @author CaiRui
  10. * @date 2018-12-10 12:09
  11. */
  12. @Configuration
  13. public class KaptchaGenerateConfig {
  14. //TODO 配置项放在配置文件中
  15. @Bean
  16. public DefaultKaptcha producer() {
  17. Properties properties = new Properties();
  18. properties.put( "kaptcha.border", "no");
  19. properties.put( "kaptcha.textproducer.font.color", "black");
  20. properties.put( "kaptcha.textproducer.char.space", "5");
  21. Config config = new Config(properties);
  22. DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
  23. defaultKaptcha.setConfig(config);
  24. return defaultKaptcha;
  25. }
  26. }

2.认证流程加入图片验证码校验

通过上篇的源码分析spring-security原理,其实就是过滤器链上的各个过滤器协同工作,思路如下:

  • 编写我们的自定义图片验证码过滤器
  • 将它放在UsernamePasswordAuthenticationFilter表单过滤器之前

验证码过滤器 


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import org.apache.commons.lang.StringUtils;
  5. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  6. import org.springframework.social.connect.web.HttpSessionSessionStrategy;
  7. import org.springframework.social.connect.web.SessionStrategy;
  8. import org.springframework.web.bind.ServletRequestBindingException;
  9. import org.springframework.web.bind.ServletRequestUtils;
  10. import org.springframework.web.context.request.ServletWebRequest;
  11. import org.springframework.web.filter.OncePerRequestFilter;
  12. import javax.servlet.FilterChain;
  13. import javax.servlet.ServletException;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.IOException;
  17. /**
  18. * 图片验证码过滤器
  19. * OncePerRequestFilter 过滤器只会调用一次
  20. *
  21. * @author CaiRui
  22. * @date 2018-12-10 12:23
  23. */
  24. public class CaptchaFilter extends OncePerRequestFilter {
  25. //一般在配置类中进行注入
  26. @Setter
  27. @Getter
  28. private AuthenticationFailureHandler failureHandler;
  29. private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
  30. @Override
  31. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  32. //表单登录的post请求
  33. if (StringUtils.equals( "/authentication/form", request.getRequestURI())
  34. && StringUtils.equalsIgnoreCase( "post", request.getMethod())) {
  35. try {
  36. validate(request);
  37. } catch (CaptchaException captchaException) {
  38. //失败调用我们的自定义失败处理器
  39. failureHandler.onAuthenticationFailure(request, response, captchaException);
  40. //后续流程终止
  41. return;
  42. }
  43. }
  44. filterChain.doFilter(request, response);
  45. }
  46. /**
  47. * 图片验证码校验
  48. *
  49. * @param request
  50. */
  51. private void validate(HttpServletRequest request) throws ServletRequestBindingException {
  52. // 拿到之前存储的imageCode信息
  53. ServletWebRequest swr = new ServletWebRequest(request);
  54. ImageCaptchaVo imageCodeInSession = (ImageCaptchaVo) sessionStrategy.getAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
  55. String codeInRequest = ServletRequestUtils.getStringParameter(request, "imageCode");
  56. if (StringUtils.isBlank(codeInRequest)) {
  57. throw new CaptchaException( "验证码的值不能为空");
  58. }
  59. if (imageCodeInSession == null) {
  60. throw new CaptchaException( "验证码不存在");
  61. }
  62. if (imageCodeInSession.isExpried()) {
  63. sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
  64. throw new CaptchaException( "验证码已过期");
  65. }
  66. if (!StringUtils.equals(imageCodeInSession.getCode(), codeInRequest)) {
  67. throw new CaptchaException( "验证码不匹配");
  68. }
  69. //验证通过 移除缓存
  70. sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
  71. }
  72. }

自定义验证码异常 


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import org.springframework.security.core.AuthenticationException;
  3. /**
  4. * 自定义验证码异常
  5. * @author CaiRui
  6. * @date 2018-12-10 12:43
  7. */
  8. public class CaptchaException extends AuthenticationException {
  9. public CaptchaException(String msg, Throwable t) {
  10. super(msg, t);
  11. }
  12. public CaptchaException(String msg) {
  13. super(msg);
  14. }
  15. }

将过滤器加入到浏览器权限配置中


 
 
  1. package com.rui.tiger.auth.browser.config;
  2. import com.rui.tiger.auth.core.authentication.TigerAuthenticationFailureHandler;
  3. import com.rui.tiger.auth.core.authentication.TigerAuthenticationSuccessHandler;
  4. import com.rui.tiger.auth.core.captcha.CaptchaFilter;
  5. import com.rui.tiger.auth.core.properties.SecurityProperties;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  10. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  11. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  12. import org.springframework.security.crypto.password.PasswordEncoder;
  13. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  14. /**
  15. * 浏览器security配置类
  16. *
  17. * @author CaiRui
  18. * @date 2018-12-4 8:41
  19. */
  20. @Configuration
  21. public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
  22. @Autowired
  23. private SecurityProperties securityProperties;
  24. @Autowired
  25. private TigerAuthenticationFailureHandler tigerAuthenticationFailureHandler;
  26. @Autowired
  27. private TigerAuthenticationSuccessHandler tigerAuthenticationSuccessHandler;
  28. /**
  29. * 密码加密解密
  30. *
  31. * @return
  32. */
  33. @Bean
  34. public PasswordEncoder passwordEncoder() {
  35. return new BCryptPasswordEncoder();
  36. }
  37. @Override
  38. protected void configure(HttpSecurity http) throws Exception {
  39. //加入图片验证码过滤器
  40. CaptchaFilter captchaFilter= new CaptchaFilter();
  41. captchaFilter.setFailureHandler(tigerAuthenticationFailureHandler);
  42. //图片验证码放在认证之前
  43. http.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)
  44. .formLogin()
  45. .loginPage( "/authentication/require") //自定义登录请求
  46. .loginProcessingUrl( "/authentication/form") //自定义登录表单请求
  47. .successHandler(tigerAuthenticationSuccessHandler)
  48. .failureHandler(tigerAuthenticationFailureHandler)
  49. .and()
  50. .authorizeRequests()
  51. .antMatchers(securityProperties.getBrowser().getLoginPage(),
  52. "/authentication/require", "/captcha/image") //此路径放行 否则会陷入死循环
  53. .permitAll()
  54. .anyRequest()
  55. .authenticated()
  56. .and()
  57. .csrf().disable() //跨域关闭
  58. ;
  59. }
  60. }

前段标准登录界面加入验证码改造


 
 
  1. <html>
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>登录 </title>
  5. </head>
  6. <body>
  7. <h2>标准登录页面 </h2>
  8. <h3>表单登录 </h3>
  9. <form action="/authentication/form" method="post">
  10. <table>
  11. <tr>
  12. <td>用户名: </td>
  13. <td> <input type="text" name="username"> </td>
  14. </tr>
  15. <tr>
  16. <td>密码: </td>
  17. <td> <input type="password" name="password"> </td>
  18. </tr>
  19. <tr>
  20. <td>图形验证码: </td>
  21. <td>
  22. <input type="text" name="imageCode">
  23. <img src="/captcha/image">
  24. </td>
  25. </tr>
  26. <tr>
  27. <td colspan="2"> <button type="submit">登录 </button> </td>
  28. </tr>
  29. </table>
  30. </form>
  31. </body>
  32. </html>

ok  我们项目重新启动下 来看下自定义的验证码过滤器是否可用,直接浏览器输入我们的登录地址http://localhost:8070/tiger-login.html

可以看到图片验证码已经成功显示出来了,我们来看看验证逻辑是否可用,试下不输入验证码

这是因为我们自定义的失败处理器,打印了全部的错误堆栈信息我们来调整下,调整后如下


 
 
  1. package com.rui.tiger.auth.core.authentication;
  2. import com.alibaba.fastjson.JSON;
  3. import com.rui.tiger.auth.core.model.enums.LoginTypeEnum;
  4. import com.rui.tiger.auth.core.properties.SecurityProperties;
  5. import com.rui.tiger.auth.core.support.SimpleResponse;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.http.HttpStatus;
  9. import org.springframework.security.core.AuthenticationException;
  10. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  11. import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
  12. import org.springframework.stereotype.Component;
  13. import javax.servlet.ServletException;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.IOException;
  17. /**
  18. * 认证失败处理器
  19. * @author CaiRui
  20. * @date 2018-12-6 12:40
  21. */
  22. @Component( "tigerAuthenticationFailureHandler")
  23. @Slf4j
  24. public class TigerAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
  25. @Autowired
  26. private SecurityProperties securityProperties;
  27. @Override
  28. public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  29. log.info( "登录失败");
  30. if (LoginTypeEnum.JSON.equals(securityProperties.getBrowser().getLoginType())) {
  31. response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
  32. response.setContentType( "application/json;charset=UTF-8");
  33. response.getWriter().write(JSON.toJSONString( new SimpleResponse(exception.getMessage())));
  34. } else {
  35. // 如果用户配置为跳转,则跳到Spring Boot默认的错误页面
  36. super.onAuthenticationFailure(request, response, exception);
  37. }
  38. }
  39. }

这次我们试下一个错误的验证码登录看下

 

 ok 我们的自定义验证码生效,其它的情况可以自行调试 下面我们将对验证码进行重构

3.图片验证码重构

  1. 验证码生成的基本参数可以配置
  2. 验证码拦截的接口可以配置
  3. 验证码的生成逻辑可以配置

3.1验证码生成的基本参数可以配置

图片验证码基本配置类


 
 
  1. package com.rui.tiger.auth.core.properties;
  2. /**
  3. * 图片验证码配置类,根据情况添加具体配置项
  4. *
  5. * @author CaiRui
  6. * @date 2018-12-11 9:02
  7. * @see com.google.code.kaptcha.Constants
  8. */
  9. public class ImageCaptchaProperties {
  10. /**
  11. * 验证码长度
  12. */
  13. private int size = 4;
  14. /**
  15. * 宽度
  16. */
  17. private int width;
  18. /**
  19. * 高度
  20. */
  21. private int height;
  22. /**
  23. * 失效秒数
  24. */
  25. private int expireAfterSecondes = 3 * 60; //默认三分钟
  26. /**
  27. * 验证码拦截的路径 多个路径以,(逗号)进行分割
  28. */
  29. private String interceptImageUrl;
  30. public int getSize() {
  31. return size;
  32. }
  33. public void setSize(int size) {
  34. this.size = size;
  35. }
  36. public int getWidth() {
  37. return width;
  38. }
  39. public void setWidth(int width) {
  40. this.width = width;
  41. }
  42. public int getHeight() {
  43. return height;
  44. }
  45. public void setHeight(int height) {
  46. this.height = height;
  47. }
  48. public int getExpireAfterSecondes() {
  49. return expireAfterSecondes;
  50. }
  51. public void setExpireAfterSecondes(int expireAfterSecondes) {
  52. this.expireAfterSecondes = expireAfterSecondes;
  53. }
  54. public String getInterceptImageUrl() {
  55. return interceptImageUrl;
  56. }
  57. public void setInterceptImageUrl(String interceptImageUrl) {
  58. this.interceptImageUrl = interceptImageUrl;
  59. }
  60. }

 加入到总配置类中


 
 
  1. package com.rui.tiger.auth.core.properties;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. /**
  4. * 权限配置文件父类(注意这里不用lombok 会读取不到)
  5. * 这里会有很多权限配置子模块
  6. *
  7. * @author CaiRui
  8. * @date 2018-12-6 8:41
  9. */
  10. @ConfigurationProperties(value = "tiger.auth", ignoreInvalidFields = true)
  11. public class SecurityProperties {
  12. /**
  13. * 浏览器配置类
  14. */
  15. private BrowserProperties browser = new BrowserProperties();
  16. /**
  17. * 图片验证码配置类
  18. */
  19. private ImageCaptchaProperties imageCaptcha = new ImageCaptchaProperties();
  20. public BrowserProperties getBrowser() {
  21. return browser;
  22. }
  23. public void setBrowser(BrowserProperties browser) {
  24. this.browser = browser;
  25. }
  26. public ImageCaptchaProperties getImageCaptcha() {
  27. return imageCaptcha;
  28. }
  29. public void setImageCaptcha(ImageCaptchaProperties imageCaptcha) {
  30. this.imageCaptcha = imageCaptcha;
  31. }
  32. }

验证码生成参数从配置中获取


 
 
  1. package com.rui.tiger.auth.core.config;
  2. import com.google.code.kaptcha.Constants;
  3. import com.google.code.kaptcha.impl.DefaultKaptcha;
  4. import com.google.code.kaptcha.util.Config;
  5. import com.rui.tiger.auth.core.properties.SecurityProperties;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import java.util.Properties;
  10. /**
  11. * 验证码生成配置类
  12. *
  13. * @author CaiRui
  14. * @date 2018-12-10 12:09
  15. * @see Constants
  16. */
  17. @Configuration
  18. public class CaptchaGenerateConfig {
  19. @Autowired
  20. private SecurityProperties securityProperties; //验证码配置获取
  21. // 参见 https://blog.csdn.net/larger5/article/details/79522105 DefaultKaptcha配置生成
  22. @Bean
  23. public DefaultKaptcha producer() {
  24. Properties properties = new Properties();
  25. properties.put( "kaptcha.border", "no");
  26. //常量配置Constants和直接字符串配置都可以
  27. properties.put(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
  28. //验证码长度 put 不生效 注意
  29. //properties.put("kaptcha.textproducer.char.length", securityProperties.getImageCaptcha().getSize());
  30. properties.setProperty( "kaptcha.textproducer.char.length", String.valueOf(securityProperties.getImageCaptcha().getSize()));
  31. Config config = new Config(properties);
  32. DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
  33. defaultKaptcha.setConfig(config);
  34. return defaultKaptcha;
  35. }
  36. }

3.2 验证码拦截的接口可以配置

过滤器加入拦截的url匹配,对原来的进行重构


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import com.rui.tiger.auth.core.properties.SecurityProperties;
  3. import lombok.Getter;
  4. import lombok.Setter;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.apache.commons.lang.StringUtils;
  7. import org.springframework.beans.factory.InitializingBean;
  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.util.AntPathMatcher;
  12. import org.springframework.web.bind.ServletRequestBindingException;
  13. import org.springframework.web.bind.ServletRequestUtils;
  14. import org.springframework.web.context.request.ServletWebRequest;
  15. import org.springframework.web.filter.OncePerRequestFilter;
  16. import javax.servlet.FilterChain;
  17. import javax.servlet.ServletException;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.servlet.http.HttpServletResponse;
  20. import java.io.IOException;
  21. import java.util.HashSet;
  22. import java.util.Set;
  23. import java.util.stream.Collectors;
  24. import java.util.stream.Stream;
  25. /**
  26. * 图片验证码过滤器
  27. * OncePerRequestFilter 过滤器只会调用一次
  28. *
  29. * @author CaiRui
  30. * @date 2018-12-10 12:23
  31. */
  32. @Setter
  33. @Getter
  34. @Slf4j
  35. public class CaptchaFilter extends OncePerRequestFilter implements InitializingBean {
  36. //一般在配置类中进行注入
  37. private AuthenticationFailureHandler failureHandler;
  38. private SecurityProperties securityProperties;
  39. /**
  40. * 验证码拦截的路径
  41. */
  42. private Set<String> interceptUrlSet = new HashSet<>();
  43. //session工具类
  44. private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
  45. //路径匹配工具类
  46. private AntPathMatcher antPathMatcher = new AntPathMatcher();
  47. /**
  48. * @throws ServletException
  49. */
  50. @Override
  51. public void afterPropertiesSet() throws ServletException {
  52. super.afterPropertiesSet();
  53. //其它配置的需要验证码验证的路径
  54. String configInterceptUrl = securityProperties.getImageCaptcha().getInterceptImageUrl();
  55. if (StringUtils.isNotBlank(configInterceptUrl)) {
  56. String[] configInterceptUrlArray = StringUtils.split(configInterceptUrl, ",");
  57. interceptUrlSet = Stream.of(configInterceptUrlArray).collect(Collectors.toSet());
  58. }
  59. //登录请求验证
  60. interceptUrlSet.add( "/authentication/form");
  61. }
  62. @Override
  63. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  64. log.info( "验证码验证请求路径:[{}]", request.getRequestURI());
  65. boolean action = false; // 默认不放行
  66. for (String url : interceptUrlSet) {
  67. if (antPathMatcher.match(url, request.getRequestURI())) {
  68. action = true;
  69. }
  70. }
  71. if (action) {
  72. try {
  73. validate(request);
  74. } catch (CaptchaException captchaException) {
  75. //失败调用我们的自定义失败处理器
  76. failureHandler.onAuthenticationFailure(request, response, captchaException);
  77. //后续流程终止
  78. return;
  79. }
  80. }
  81. //后续过滤器继续执行
  82. filterChain.doFilter(request, response);
  83. }
  84. /**
  85. * 图片验证码校验
  86. *
  87. * @param request
  88. */
  89. private void validate(HttpServletRequest request) throws ServletRequestBindingException {
  90. // 拿到之前存储的imageCode信息
  91. ServletWebRequest swr = new ServletWebRequest(request);
  92. ImageCaptchaVo imageCodeInSession = (ImageCaptchaVo) sessionStrategy.getAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
  93. String codeInRequest = ServletRequestUtils.getStringParameter(request, "imageCode");
  94. if (StringUtils.isBlank(codeInRequest)) {
  95. throw new CaptchaException( "验证码的值不能为空");
  96. }
  97. if (imageCodeInSession == null) {
  98. throw new CaptchaException( "验证码不存在");
  99. }
  100. if (imageCodeInSession.isExpried()) {
  101. sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
  102. throw new CaptchaException( "验证码已过期");
  103. }
  104. if (!StringUtils.equals(imageCodeInSession.getCode(), codeInRequest)) {
  105. throw new CaptchaException( "验证码不匹配");
  106. }
  107. //验证通过 移除缓存
  108. sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
  109. }
  110. }

 配置类中加入调整后的过滤器相关配置


 
 
  1. package com.rui.tiger.auth.browser.config;
  2. import com.rui.tiger.auth.core.authentication.TigerAuthenticationFailureHandler;
  3. import com.rui.tiger.auth.core.authentication.TigerAuthenticationSuccessHandler;
  4. import com.rui.tiger.auth.core.captcha.CaptchaFilter;
  5. import com.rui.tiger.auth.core.properties.SecurityProperties;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  10. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  11. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  12. import org.springframework.security.crypto.password.PasswordEncoder;
  13. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  14. /**
  15. * 浏览器security配置类
  16. *
  17. * @author CaiRui
  18. * @date 2018-12-4 8:41
  19. */
  20. @Configuration
  21. public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
  22. @Autowired
  23. private SecurityProperties securityProperties;
  24. @Autowired
  25. private TigerAuthenticationFailureHandler tigerAuthenticationFailureHandler;
  26. @Autowired
  27. private TigerAuthenticationSuccessHandler tigerAuthenticationSuccessHandler;
  28. /**
  29. * 密码加密解密
  30. *
  31. * @return
  32. */
  33. @Bean
  34. public PasswordEncoder passwordEncoder() {
  35. return new BCryptPasswordEncoder();
  36. }
  37. @Override
  38. protected void configure(HttpSecurity http) throws Exception {
  39. //加入图片验证码过滤器
  40. CaptchaFilter captchaFilter = new CaptchaFilter();
  41. captchaFilter.setFailureHandler(tigerAuthenticationFailureHandler);
  42. captchaFilter.setSecurityProperties(securityProperties);
  43. captchaFilter.afterPropertiesSet();
  44. //图片验证码放在认证之前
  45. http.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)
  46. .formLogin()
  47. .loginPage( "/authentication/require") //自定义登录请求
  48. .loginProcessingUrl( "/authentication/form") //自定义登录表单请求
  49. .successHandler(tigerAuthenticationSuccessHandler)
  50. .failureHandler(tigerAuthenticationFailureHandler)
  51. .and()
  52. .authorizeRequests()
  53. .antMatchers(securityProperties.getBrowser().getLoginPage(),
  54. "/authentication/require", "/captcha/image") //此路径放行 否则会陷入死循环
  55. .permitAll()
  56. .anyRequest()
  57. .authenticated()
  58. .and()
  59. .csrf().disable() //跨域关闭
  60. ;
  61. }
  62. }

测试配置文件调整如下


 
 
  1. #数据源
  2. spring:
  3. datasource:
  4. driverClassName: com.mysql.jdbc.Driver
  5. url: jdbc: mysql:/ /my.yunout.com:3306/tiger_study?allowMultiQueries= true&useUnicode= true&characterEncoding=UTF- 8
  6. username: root
  7. password: root
  8. # 配置Druid连接池
  9. type: com.alibaba.druid.pool.DruidDataSource
  10. session:
  11. store- type: none
  12. # Tomcat
  13. server:
  14. port: 8070
  15. connection- timeout: 5000ms
  16. #自定义权限配置
  17. tiger:
  18. auth:
  19. browser:
  20. #loginPage: /demo-login.html # 这里可以配置成自己的非标准登录界面
  21. loginType: JSON
  22. imageCaptcha:
  23. interceptImageUrl: /user/*, /pay/confirm # 这些路径验证码也要拦截校验

ok 我们来测试下 是否可用 

3.3 验证码的生成逻辑可以配置

主要是利用@Conditional系列注解,可参看@Conditional系列注解


 
 
  1. package com.rui.tiger.auth.core.config;
  2. import com.google.code.kaptcha.Producer;
  3. import com.rui.tiger.auth.core.captcha.CaptchaGenerate;
  4. import com.rui.tiger.auth.core.captcha.ImageCaptchaGenerate;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
  7. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. /**
  11. * 验证码Bean生成配置类
  12. *
  13. * @author CaiRui
  14. * @date 2018-12-12 8:41
  15. */
  16. @Configuration
  17. public class CaptchaGenerateBeanConfig {
  18. @Bean
  19. // spring 容器中如果存在imageCaptchaGenerate的bean就不会再初始化该bean了
  20. //可参见:https://www.cnblogs.com/yixianyixian/p/7346894.html 这篇博文
  21. @ConditionalOnMissingBean(name = "imageCaptchaGenerate")
  22. public CaptchaGenerate imageCaptchaGenerate() {
  23. ImageCaptchaGenerate imageCaptchaGenerate = new ImageCaptchaGenerate();
  24. return imageCaptchaGenerate;
  25. }
  26. }

原来的service去掉注解


 
 
  1. package com.rui.tiger.auth.core.captcha;
  2. import com.google.code.kaptcha.Producer;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import java.awt.image.BufferedImage;
  6. /**
  7. * 图片验证码生成接口
  8. *
  9. * @author CaiRui
  10. * @date 2018-12-10 12:07
  11. */
  12. //@Service("imageCaptchaGenerate")
  13. public class ImageCaptchaGenerate implements CaptchaGenerate {
  14. @Autowired
  15. private Producer producer; //config bean中配置
  16. @Override
  17. public ImageCaptchaVo generate() {
  18. String code = producer.createText();
  19. BufferedImage bufferedImage = producer.createImage(code);
  20. return new ImageCaptchaVo(bufferedImage, code, 60 * 5); //5分钟过期
  21. }
  22. }

demo项目中写个测试类看是否生效,这里我们不生成,只是打印下日志


 
 
  1. package com.rui.tiger.auth.demo.service;
  2. import com.rui.tiger.auth.core.captcha.CaptchaGenerate;
  3. import com.rui.tiger.auth.core.captcha.ImageCaptchaVo;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * @author CaiRui
  8. * @date 2018-12-12 9:05
  9. */
  10. @Component( "imageCaptchaGenerate")
  11. @Slf4j
  12. public class DemoImageCaptchaGenerateTest implements CaptchaGenerate {
  13. /**
  14. * 测试覆盖 测试通过将其@Component去掉 保证正常流程执行
  15. *
  16. * @return
  17. * @see com.rui.tiger.auth.core.config.CaptchaGenerateBeanConfig
  18. */
  19. @Override
  20. public ImageCaptchaVo generate() {
  21. log.info( "自定义验证码实现 覆盖原始的生成测试");
  22. return null;
  23. }
  24. }

 

 

文章转载至:https://blog.csdn.net/ahcr1026212/article/details/84949389

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值