Spring Security -- 添加图形验证码(转载)
添加验证码大致可以分为三个步骤:
- 根据随机数生成验证码图片;
- 将验证码图片显示到登录页面;
- 认证流程中加入验证码校验。
Spring Security的认证校验是由UsernamePasswordAuthenticationFilter过滤器完成的,所以我们的验证码校验逻辑应该在这个过滤器之前。下面一起学习下如何在上一节Spring Security自定义用户认证的基础上加入验证码校验功能。
一、生成校验码
1、导入依赖
验证码功能需要用到spring-social-config依赖:
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-config</artifactId>
<version>1.1.6.RELEASE</version>
</dependency>
2、ImageCode实体类
在com.zy.example.entity包下创建类ImageCode:
package com.zy.example.entity;
import lombok.Data;
import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
/**
* @Author: zy
* @Description: 验证码
* @Date: 2020-2-9
*/
@Data
public class ImageCode {
/**
* 验证码图片
*/
private BufferedImage image;
/**
* code验证码
*/
private String code;
/**
* 过期时间 单位秒
*/
private LocalDateTime expireTime;
/**
* 判断验证码是否过期
* @return
*/
public boolean isExpire() {
return LocalDateTime.now().isAfter(expireTime);
}
/**
* 构造函数
* @param image
* @param code
* @param expireIn
*/
public ImageCode(BufferedImage image, String code, int expireIn) {
this.image = image;
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
}
}
ImageCode对象包含了三个属性:image图片,code验证码和expireTime过期时间。isExpire方法用于判断验证码是否已过期。
3、ValidateCodeController
接着在包com.zy.example.controller下定义一个ValidateCodeController,用于处理生成验证码请求:
package com.zy.example.controller; import com.zy.example.entity.ImageCode; import org.springframework.social.connect.web.HttpSessionSessionStrategy; import org.springframework.social.connect.web.SessionStrategy; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.ServletWebRequest; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; /** * @Author: zy * @Description: 处理生成验证码的请求 * @Date: 2020-2-9 */ @RestController @RequestMapping("/code") public class ValidateCodeController { public final static String SESSION_KEY_IMAGE_CODE = "SESSION_KEY_IMAGE_CODE"; //使用sessionStrategy将生成的验证码对象存储到Session中,并通过IO流将生成的图片输出到登录页面上。 private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @RequestMapping("/image") public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException { //生成验证码对象 ImageCode imageCode = createImageCode(); //生成的验证码对象存储到Session中 sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY_IMAGE_CODE,imageCode); //通过IO流将生成的图片输出到登录页面上 ImageIO.write(imageCode.getImage(), "jpeg", response.getOutputStream()); } /** * 用于生成验证码对象 * @return */ private ImageCode createImageCode() { int width = 100; // 验证码图片宽度 int height = 36; // 验证码图片长度 int length = 4; // 验证码位数 int expireIn = 60; // 验证码有效时间 60s //创建一个带缓冲区图像对象 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //获得在图像上绘图的Graphics对象 Graphics g = image.getGraphics(); Random random = new Random(); //设置颜色、并随机绘制直线 g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ITALIC, 20)); g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } //生成随机数 并绘制 StringBuilder sRand = new StringBuilder(); for (int i = 0; i < length; i++) { String rand = String.valueOf(random.nextInt(10)); sRand.append(rand); g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand.toString(), expireIn); } /** * 获取随机演示 * @param fc * @param bc * @return */ private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); } }
createImageCode方法用于生成验证码对象,org.springframework.social.connect.web.HttpSessionSessionStrategy对象封装了一些处理Session的方法,包含了setAttribute、getAttribute和removeAttribute方法,具体可以查看该类的源码。使用sessionStrategy将生成的验证码对象存储到Session中,并通过IO流将生成的图片输出到登录页面上。
由于现在鉴权体系有一部分是采用无状态的jwt来实现,而没有使用Cookie、Session这种。那我们可以考虑将生成的验证码保存到redis中,而redis的key选用我们的token即可,具体实现就不介绍了,有兴趣自己研究一下。
生成验证码的方法写好后,接下来开始改造登录页面。
二、登录页
在登陆页面login.ftl追加如下代码:
<input type="text" name="imageCode" placeholder="验证码" style="width: 50%;"/>
<img src="/code/image"/>
<br>
<img>标签的src属性对应ValidateController的createImageCode方法:
要使生成验证码的请求不被拦截,需要在BrowserSecurityConfig的configure方法中配置免拦截:
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步