SpringSecurity登录添加验证码

登录添加验证码是一个非常常见的需求,网上也有非常成熟的解决方案,其实,要是自己自定义登录实现这个并不难,但是如果需要在SpringSecurity框架中实现这个功能,还得稍费一点功夫,本文就和小伙伴来分享下在SpringSecurity框架中如何添加验证码。

关于SpringSecurity基本配置,这里就不再多说,小伙伴有不懂的可以参考我的书《SpringBoot+Vue全栈开发实战》,本文主要来看如何加入验证码功能。

准备验证码

要有验证码,首先得先准备好验证码,本文采用Java自画的验证码,代码如下:

 
   
  1. /**

  2. * 生成验证码的工具类

  3. */

  4. public class VerifyCode {


  5. private int width = 100;// 生成验证码图片的宽度

  6. private int height = 50;// 生成验证码图片的高度

  7. private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };

  8. private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色

  9. private Random random = new Random();

  10. private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

  11. private String text;// 记录随机字符串


  12. /**

  13. * 获取一个随意颜色

  14. *

  15. * @return

  16. */

  17. private Color randomColor() {

  18. int red = random.nextInt(150);

  19. int green = random.nextInt(150);

  20. int blue = random.nextInt(150);

  21. return new Color(red, green, blue);

  22. }


  23. /**

  24. * 获取一个随机字体

  25. *

  26. * @return

  27. */

  28. private Font randomFont() {

  29. String name = fontNames[random.nextInt(fontNames.length)];

  30. int style = random.nextInt(4);

  31. int size = random.nextInt(5) + 24;

  32. return new Font(name, style, size);

  33. }


  34. /**

  35. * 获取一个随机字符

  36. *

  37. * @return

  38. */

  39. private char randomChar() {

  40. return codes.charAt(random.nextInt(codes.length()));

  41. }


  42. /**

  43. * 创建一个空白的BufferedImage对象

  44. *

  45. * @return

  46. */

  47. private BufferedImage createImage() {

  48. BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

  49. Graphics2D g2 = (Graphics2D) image.getGraphics();

  50. g2.setColor(bgColor);// 设置验证码图片的背景颜色

  51. g2.fillRect(0, 0, width, height);

  52. return image;

  53. }


  54. public BufferedImage getImage() {

  55. BufferedImage image = createImage();

  56. Graphics2D g2 = (Graphics2D) image.getGraphics();

  57. StringBuffer sb = new StringBuffer();

  58. for (int i = 0; i < 4; i++) {

  59. String s = randomChar() + "";

  60. sb.append(s);

  61. g2.setColor(randomColor());

  62. g2.setFont(randomFont());

  63. float x = i * width * 1.0f / 4;

  64. g2.drawString(s, x, height - 15);

  65. }

  66. this.text = sb.toString();

  67. drawLine(image);

  68. return image;

  69. }


  70. /**

  71. * 绘制干扰线

  72. *

  73. * @param image

  74. */

  75. private void drawLine(BufferedImage image) {

  76. Graphics2D g2 = (Graphics2D) image.getGraphics();

  77. int num = 5;

  78. for (int i = 0; i < num; i++) {

  79. int x1 = random.nextInt(width);

  80. int y1 = random.nextInt(height);

  81. int x2 = random.nextInt(width);

  82. int y2 = random.nextInt(height);

  83. g2.setColor(randomColor());

  84. g2.setStroke(new BasicStroke(1.5f));

  85. g2.drawLine(x1, y1, x2, y2);

  86. }

  87. }


  88. public String getText() {

  89. return text;

  90. }


  91. public static void output(BufferedImage image, OutputStream out) throws IOException {

  92. ImageIO.write(image, "JPEG", out);

  93. }

  94. }

这个工具类很常见,网上也有很多,就是画一个简单的验证码,通过流将验证码写到前端页面,提供验证码的Controller如下:

 
   
  1. @RestController

  2. public class VerifyCodeController {

  3. @GetMapping("/vercode")

  4. public void code(HttpServletRequest req, HttpServletResponse resp) throws IOException {

  5. VerifyCode vc = new VerifyCode();

  6. BufferedImage image = vc.getImage();

  7. String text = vc.getText();

  8. HttpSession session = req.getSession();

  9. session.setAttribute("index_code", text);

  10. VerifyCode.output(image, resp.getOutputStream());

  11. }

  12. }

这里创建了一个VerifyCode对象,将生成的验证码字符保存到session中,然后通过流将图片写到前端,img标签如下:

 
   
  1. src="/vercode" alt="">

展示效果如下:

SpringSecurity登录添加验证码_第1张图片

自定义过滤器

在登陆页展示验证码这个就不需要我多说了,接下来我们来看看如何自定义验证码处理器:

 
   
  1. @Component

  2. public class VerifyCodeFilter extends GenericFilterBean {

  3. private String defaultFilterProcessUrl = "/doLogin";


  4. @Override

  5. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)

  6. throws IOException, ServletException {

  7. HttpServletRequest request = (HttpServletRequest) req;

  8. HttpServletResponse response = (HttpServletResponse) res;

  9. if ("POST".equalsIgnoreCase(request.getMethod()) && defaultFilterProcessUrl.equals(request.getServletPath())) {

  10. // 验证码验证

  11. String requestCaptcha = request.getParameter("code");

  12. String genCaptcha = (String) request.getSession().getAttribute("index_code");

  13. if (StringUtils.isEmpty(requestCaptcha))

  14. throw new AuthenticationServiceException("验证码不能为空!");

  15. if (!genCaptcha.toLowerCase().equals(requestCaptcha.toLowerCase())) {

  16. throw new AuthenticationServiceException("验证码错误!");

  17. }

  18. }

  19. chain.doFilter(request, response);

  20. }

  21. }

自定义过滤器继承自GenericFilterBean,并实现其中的doFilter方法,在doFilter方法中,当请求方法是POST,并且请求地址是 /doLogin时,获取参数中的code字段值,该字段保存了用户从前端页面传来的验证码,然后获取session中保存的验证码,如果用户没有传来验证码,则抛出验证码不能为空异常,如果用户传入了验证码,则判断验证码是否正确,如果不正确则抛出异常,否则执行 chain.doFilter(request,response);使请求继续向下走。

配置

最后在SpringSecurity的配置中,配置过滤器,如下:

 
   
  1. @Configuration

  2. public class SecurityConfig extends WebSecurityConfigurerAdapter {


  3. @Autowired

  4. VerifyCodeFilter verifyCodeFilter;

  5. ...

  6. ...

  7. @Override

  8. protected void configure(HttpSecurity http) throws Exception {

  9. http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);

  10. http.authorizeRequests()

  11. .antMatchers("/admin/**").hasRole("admin")

  12. ...

  13. ...

  14. .permitAll()

  15. .and()

  16. .csrf().disable();

  17. }

  18. }

这里只贴出了部分核心代码,即 http.addFilterBefore(verifyCodeFilter,UsernamePasswordAuthenticationFilter.class);,如此之后,整个配置就算完成了。
接下来在登录中,就需要传入验证码了,如果不传或者传错,都会抛出异常,例如不传的话,抛出如下异常:

SpringSecurity登录添加验证码_第2张图片

好了,本文就先说到这里,有问题欢迎留言讨论。

▼往期精彩回顾▼ 2019新年福利,新书免费送! Docker教程 Redis教程 SpringCloud教程 Git教程 MongoDB教程 SpringBoot+Vue前后端分离开源项目-微人事 SpringBoot+Vue前后端分离开源项目-V部落

SpringSecurity登录添加验证码_第3张图片

你可能感兴趣的:(SpringSecurity登录添加验证码)