Spring Security登录添加验证码的实现过程
作者:江南一点雨 发布时间:2021-07-25 23:47:17
登录添加验证码是一个非常常见的需求,网上也有非常成熟的解决方案,其实,要是自己自定义登录实现这个并不难,但是如果需要在 Spring Security 框架中实现这个功能,还得稍费一点功夫,本文就和小伙伴来分享下在 Spring Security 框架中如何添加验证码。
关于 Spring Security 基本配置,这里就不再多说,小伙伴有不懂的可以参考我的书《SpringBoot+Vue全栈开发实战》,本文主要来看如何加入验证码功能。
准备验证码
要有验证码,首先得先准备好验证码,本文采用 Java 自画的验证码,代码如下:
/**
* 生成验证码的工具类
*/
public class VerifyCode {
private int width = 100;// 生成验证码图片的宽度
private int height = 50;// 生成验证码图片的高度
private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色
private Random random = new Random();
private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private String text;// 记录随机字符串
/**
* 获取一个随意颜色
*
* @return
*/
private Color randomColor() {
int red = random.nextInt(150);
int green = random.nextInt(150);
int blue = random.nextInt(150);
return new Color(red, green, blue);
}
/**
* 获取一个随机字体
*
* @return
*/
private Font randomFont() {
String name = fontNames[random.nextInt(fontNames.length)];
int style = random.nextInt(4);
int size = random.nextInt(5) + 24;
return new Font(name, style, size);
}
/**
* 获取一个随机字符
*
* @return
*/
private char randomChar() {
return codes.charAt(random.nextInt(codes.length()));
}
/**
* 创建一个空白的BufferedImage对象
*
* @return
*/
private BufferedImage createImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
g2.setColor(bgColor);// 设置验证码图片的背景颜色
g2.fillRect(0, 0, width, height);
return image;
}
public BufferedImage getImage() {
BufferedImage image = createImage();
Graphics2D g2 = (Graphics2D) image.getGraphics();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 4; i++) {
String s = randomChar() + "";
sb.append(s);
g2.setColor(randomColor());
g2.setFont(randomFont());
float x = i * width * 1.0f / 4;
g2.drawString(s, x, height - 15);
}
this.text = sb.toString();
drawLine(image);
return image;
}
/**
* 绘制干扰线
*
* @param image
*/
private void drawLine(BufferedImage image) {
Graphics2D g2 = (Graphics2D) image.getGraphics();
int num = 5;
for (int i = 0; i < num; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
g2.setColor(randomColor());
g2.setStroke(new BasicStroke(1.5f));
g2.drawLine(x1, y1, x2, y2);
}
}
public String getText() {
return text;
}
public static void output(BufferedImage image, OutputStream out) throws IOException {
ImageIO.write(image, "JPEG", out);
}
}
这个工具类很常见,网上也有很多,就是画一个简单的验证码,通过流将验证码写到前端页面,提供验证码的 Controller 如下:
@RestController
public class VerifyCodeController {
@GetMapping("/vercode")
public void code(HttpServletRequest req, HttpServletResponse resp) throws IOException {
VerifyCode vc = new VerifyCode();
BufferedImage image = vc.getImage();
String text = vc.getText();
HttpSession session = req.getSession();
session.setAttribute("index_code", text);
VerifyCode.output(image, resp.getOutputStream());
}
}
这里创建了一个 VerifyCode 对象,将生成的验证码字符保存到 session 中,然后通过流将图片写到前端,img标签如下:
<img src="/vercode" alt="">
展示效果如下:
自定义过滤器
在登陆页展示验证码这个就不需要我多说了,接下来我们来看看如何自定义验证码处理器:
@Component
public class VerifyCodeFilter extends GenericFilterBean {
private String defaultFilterProcessUrl = "/doLogin";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if ("POST".equalsIgnoreCase(request.getMethod()) && defaultFilterProcessUrl.equals(request.getServletPath())) {
// 验证码验证
String requestCaptcha = request.getParameter("code");
String genCaptcha = (String) request.getSession().getAttribute("index_code");
if (StringUtils.isEmpty(requestCaptcha))
throw new AuthenticationServiceException("验证码不能为空!");
if (!genCaptcha.toLowerCase().equals(requestCaptcha.toLowerCase())) {
throw new AuthenticationServiceException("验证码错误!");
}
}
chain.doFilter(request, response);
}
}
自定义过滤器继承自 GenericFilterBean ,并实现其中的 doFilter 方法,在 doFilter 方法中,当请求方法是 POST ,并且请求地址是 /doLogin 时,获取参数中的 code 字段值,该字段保存了用户从前端页面传来的验证码,然后获取 session 中保存的验证码,如果用户没有传来验证码,则抛出验证码不能为空异常,如果用户传入了验证码,则判断验证码是否正确,如果不正确则抛出异常,否则执行 chain.doFilter(request, response); 使请求继续向下走。
配置
最后在 Spring Security 的配置中,配置过滤器,如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
VerifyCodeFilter verifyCodeFilter;
...
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
...
...
.permitAll()
.and()
.csrf().disable();
}
}
这里只贴出了部分核心代码,即 http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class); ,如此之后,整个配置就算完成了。
接下来在登录中,就需要传入验证码了,如果不传或者传错,都会抛出异常,例如不传的话,抛出如下异常:
来源:https://www.javaboy.org/2019/0613/springsecurity-verifycode.html


猜你喜欢
- 这篇山寨一个新版QQ的列表滑动删除,上篇有说到QQ的滑动删除,推测原理就是ListView本身每个item存在一个Button,只不过普通的
- Java平台的垃圾收集机制显著提高了开发者的效率,但是一个实现糟糕的垃圾收集器可能过多地消耗应用程序的资源。在Java虚拟机性能优化系列的第
- 依赖如下:<dependency> <groupId>org.springframework.boot&
- 我们在C#编程中常见的信息提示框(MessageBox)是微软NET自带的一个用于弹出警告、错误或者讯息一类的“模式”对话框。此类对话框一旦
- spring boot诞生的背景在spring boot出现以前,使用spring框架的程序员是这样配置web应用环境的,需要大量的xml配
- wait()方法表示,放弃当前对资源的占有权,等啊等啊,一直等到有人通知我,我才会运行后面的代码。notify()方法表示,当前的线程已经放
- 项目需求中有个功能模块需要用到时间选择控件,但是android系统自带的太丑了,只能自己优化下,结合WheelView实现滚轮选择日期,好像
- 本文实例为大家分享了java五子棋游戏的具体代码,供大家参考,具体内容如下AI思路:通过判断棋盘上每个空位的分数,去分数最高的几个点,随机下
- 本文实例为大家分享了Unity shader实现高斯模糊效果的具体代码,供大家参考,具体内容如下正常图:高斯模糊效果图:shader代码:S
- 写在前面Activity是Android四大组件之一,用于直接跟用户进行交互,本篇文章将介绍Activity的启动流程。用户启动Activi
- 仅做学习交流,如有侵犯联系必删。前言一篇酷狗app安卓逆向的文章,难度适中。样本: 酷狗app v10.8.8工具: jadx、Pixel3
- 这篇文章主要介绍了Spring Batch批处理框架使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 本文实例为大家分享了Android Studio实现帧动画的具体代码,供大家参考,具体内容如下按一定的顺序播放静态的图片1、几张联系的图片2
- 之前一段时间,在朋友的推荐下,玩了探探这一款软件,初玩的时候,就发现,这款软件与一般的社交软件如陌陌之类的大相径庭,让我耳目一新,特别是探探
- 1、不要为抽象类提供公开的构造方法抽象类可以有构造方法,但是抽象类不能实例化。如果编程人员没有制定构造方法,编译器会自动生成一个默认的pro
- 使用过Mybatis的同学,应该都知道,我们只需要编写mybatis对应的接口和mapper XML文件即可,并不需要手动编写mapper接
- 前言在我们的日常的编程当中,并发是始终离不开的主题,而在并发多线程当中,线程池又是一个不可规避的问题。多线程可以提高我们并发程序的效率,可以
- 在练习Java的Scanner时,EditPlus如何读取从键盘输入的数呢?例如如下程序,编译通过,运行时却输入不了数据:package m
- 前言WorkManager 是 Android Jetpack 中的新组件,用于负责管理后台任务。关于这个组件的介绍就不多说了,网上到处都是
- springmvc常用注解,操作传入参数@RequestParam一般用于jsp参数名和后台方法参数指定,对应/*