SpringSceurity(3)---图形验证码功能实现

有关springSceurity之前有写过两篇文章:

1、SpringSecurity(1)---认证+授权代码实现

2、SpringSecurity(2)---记住我功能实现

这篇我们来讲图形验证码功能实现。

一、思路

我整理下springSceurity整合图形验证码的大致思路:

1、首先对于验证码本身而言,应该有三部分组成 1、存放验证码的背景图片 2、验证码 3、验证码的有效时间。

2、对于springSceurity而言,验证码的执行校验顺序肯定是在UsernamePasswordAuthenticationFilter之前的,因为如果验证码都不对,那么
根本都不需要验证账号密码。所以我们需要自定义一个验证码过滤器,并且配置在UsernamePasswordAuthenticationFilter之前执行。 3、对于获取验证码的接口,肯定是不需要进行认证限制的。 4、对于获取验证码的接口的时候,需要把该验证码信息+当前浏览器的SessonId绑定在一起存在Seesion中,为了后面校验的时候通过SessonId
去取这个验证码信息。 5、登陆请求接口,除了带上用户名和密码之外,还需要带上验证码信息。在进入验证码过滤器的时候,首先通过SessonId获取存在Sesson中的
验证码信息,拿到验证码信息之后首先还要校验该验证码是否在有效期内。之后再和当前登陆接口带来的验证码进行对比,如果一致,那么当前
验证码这一关就过了,就开始验证下一步账号和密码是否正确了。

整个流程大致就是这样。下面现在是具体代码,然后进行测试。

二、代码展示

这里只展示一些核心代码,具体完整项目会放到github上。

1、ImageCodeProperties

这个是一个bean实体,是一个图形验证码的默认配置。

@Data
public class ImageCodeProperties {
/**
* 验证码宽度
*/
private int width = 67;
/**
* 验证码高度
*/
private int height = 23;
/**
* 验证码长度
*/
private int length = 4;
/**
* 验证码过期时间
*/
private int expireIn = 60;
/**
* 需要验证码的请求url字符串,用英文逗号隔开
*/
private String url = "/login"; }

2、ImageCode

这个是图片验证码的完整信息,也会将这个完整信息存放于Sesson中。

图片验证码信息 由三部分组成 :

1.图片信息(长、宽、背景色等等)。2.code就是真正的验证码,用来验证用。3.该验证码的有效时间。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ImageCode { private BufferedImage image;
private String code;
private LocalDateTime expireTime; public ImageCode(BufferedImage image, String code, int expireIn) {
this.image = image;
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
}
/**
* 校验是否过期
*/
public boolean isExpired() {
return LocalDateTime.now().isAfter(expireTime);
}
}

3、ValidateCodeGeneratorService

获取验证码的接口

public interface ValidateCodeGeneratorService {

    /**
* 生成图片验证码
*
* @param request 请求
* @return ImageCode实例对象
*/
ImageCode generate(ServletWebRequest request);
}

4、ImageCodeGeneratorServiceImpl

获取图片验证码的接口的实现类

@Data
public class ImageCodeGeneratorServiceImpl implements ValidateCodeGeneratorService { private static final String IMAGE_WIDTH_NAME = "width";
private static final String IMAGE_HEIGHT_NAME = "height";
private static final Integer MAX_COLOR_VALUE = 255; private ImageCodeProperties imageCodeProperties; @Override
public ImageCode generate(ServletWebRequest request) {
//设置图片的宽度和高度
int width = ServletRequestUtils.getIntParameter(request.getRequest(), IMAGE_WIDTH_NAME, imageCodeProperties.getWidth());
int height = ServletRequestUtils.getIntParameter(request.getRequest(), IMAGE_HEIGHT_NAME, imageCodeProperties.getHeight());
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
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 < imageCodeProperties.getLength(); 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(), imageCodeProperties.getExpireIn());
} /**
* 生成随机背景条纹
*
* @param fc 前景色
* @param bc 背景色
* @return RGB颜色
*/
private Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > MAX_COLOR_VALUE) {
fc = MAX_COLOR_VALUE;
}
if (bc > MAX_COLOR_VALUE) {
bc = MAX_COLOR_VALUE;
}
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);
}
}

5、ValidateCodeController

获取验证码的请求接口。

@RestController
public class ValidateCodeController { /**
* 前缀
*/
public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
private static final String FORMAT_NAME = "JPEG";
@Autowired
private ValidateCodeGeneratorService imageCodeGenerator;
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @GetMapping("/code/image")
public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 第一步:根据请求生成一个图形验证码对象
ImageCode imageCode = imageCodeGenerator.generate(new ServletWebRequest(request));
// 第二步:将图形验证码对象存到session中,第一个参数可以从传入的请求中获取session
sessionStrategy.setAttribute(new ServletRequestAttributes(request), SESSION_KEY, imageCode);
// 第三步:将生成的图片写到接口的响应中
ImageIO.write(imageCode.getImage(), FORMAT_NAME, response.getOutputStream());
} }

到这里,我们可用请求获取图片验证码的信息了。接下来我们就要登陆请求部分的代码。

6、ValidateCodeFilter

自定义过滤器,这里面才是核心的代码,首先继承OncePerRequestFilter(直接继承Filter也是可用的),实现InitializingBean是为了初始化一些初始数据。

这里走的逻辑就是把存在session中的图片验证码和当前请求的验证码进行比较,如果相同则放行,否则直接抛出异常

@Data
@Slf4j
@Component
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean { private static final String SUBMIT_FORM_DATA_PATH = "/login"; /**
* 失败处理器
*/
@Autowired
private AuthenctiationFailHandler authenctiationFailHandler;
/**
* 验证码属性类
*/
@Autowired
private ImageCodeProperties imageCodeProperties; /**
* 存放需要走验证码请求url
*/
private Set<String> urls = new HashSet<>(); /**
* 处理session工具类
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 正则配置工具
*/
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
/**
* 在初始化bean的时候都会执行该方法
*/
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
String[] configUrls = StringUtils.split(imageCodeProperties.getUrl(), ",");
// 登录的链接是必须要进行验证码验证的
urls.addAll(Arrays.asList(configUrls));
} /**
* 拦截请求进来的方法。
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
boolean action = false;
for (String url : urls) {
// 如果实际访问的URL可以与用户在imageCodeProperties中url配置的相同,那么就进行验证码校验
log.info("request.getRequestURI = {}",request.getRequestURI());
if (antPathMatcher.match(url, request.getRequestURI())) {
action = true;
}
}
//说明需要校验
if (action) {
try {
validate(new ServletWebRequest(request));
} catch (ValidateCodeException e) {
authenctiationFailHandler.onAuthenticationFailure(request, response, e);
return;
}
} //进入下一个过滤器
filterChain.doFilter(request, response);
} /**
* 验证码校验逻辑
*
*/
private void validate(ServletWebRequest request) throws ServletRequestBindingException {
// 从session中获取图片验证码
ImageCode imageCodeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY); // 从请求中获取用户填写的验证码
String imageCodeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
if (StringUtils.isBlank(imageCodeInRequest)) {
throw new ValidateCodeException("验证码不能为空");
}
if (null == imageCodeInSession) {
throw new ValidateCodeException("验证码不存在");
}
if (imageCodeInSession.isExpired()) {
sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
throw new ValidateCodeException("验证码已过期");
} log.info("session中获取的验证码={},sessionId ={}",imageCodeInSession.getCode(),request.getSessionId());
log.info("登陆操作传来的验证码={}",imageCodeInRequest);
if (!StringUtils.equalsIgnoreCase(imageCodeInRequest, imageCodeInSession.getCode())) {
throw new ValidateCodeException("验证码不匹配");
}
// 验证成功,删除session中的验证码
sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
}
}

7、WebSecurityConfig

SpringSecurity的Java 配置类也需要做一点点改动。那就是需要设置ValidateCodeFilter要在UsernamePasswordAuthenticationFilter之前进行拦截过滤。

到这里整个图形验证码的功能就开发完成了,具体代码放在github上下面进行测试。

三、测试

说明下我这里懒的写前端相关代码了,所以直接用posman用请求来获取验证码,获取验证码之后再进行登陆操作。

1、获取验证码

这里验证码code为:1848

2、登陆成功

输入的验证码也是1848,显示登陆成功。

3、登陆失败

因为配置的时候图片验证码有效期为60秒,所以在我们获取验证码后,过60秒再去登陆,就能发现,验证码已过期。

整个验证码功能大致就是这样。

Github地址 : spring-boot-security-study-03

别人骂我胖,我会生气,因为我心里承认了我胖。别人说我矮,我就会觉得好笑,因为我心里知道我不可能矮。这就是我们为什么会对别人的攻击生气。
攻我盾者,乃我内心之矛(19)

SpringSceurity(3)---图形验证码功能实现的更多相关文章

  1. Tornado框架实现图形验证码功能

    图形验证码是项目开发过程中经常遇到的一个功能,在很多语言中都有对应的不同形式的图形验证码功能的封装,python 中同样也有类似的封装操作,通过绘制生成一个指定的图形数据,让前端HTML页面通过链接获 ...

  2. spring boot:spring security给用户登录增加自动登录及图形验证码功能(spring boot 2.3.1)

    一,图形验证码的用途? 1,什么是图形验证码? 验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers ...

  3. SpringSecurity实现图形验证码功能

    ⒈封装验证码类 package cn.coreqi.security.validate; import java.awt.image.BufferedImage; import java.time.L ...

  4. 一百一十五:CMS系统之实现点击更换图形验证码功能

    把验证码渲染到到页面上 访问,显然,是标签有个内边距 去掉内边距 加一个class 如果放大看的话,还有问题 用js实现点击更换图形验证码:生成查询字符串的形式访问图形验证码接口的url,放到img标 ...

  5. SpringSceurity(4)---短信验证码功能实现

    SpringSceurity(4)---短信验证码功能实现 有关SpringSceurity系列之前有写文章 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSecur ...

  6. Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现

    本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础. 为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能. 验证码(CAPTCHA)是“Com ...

  7. .Net Core 之 图形验证码 本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能。

    本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能. 通过测试的系统: Windows 8.1 64bit Ubuntu Server 16.04 LTS 64 ...

  8. 【无私分享:ASP.NET CORE 项目实战(第十四章)】图形验证码的实现

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 很长时间没有来更新博客了,一是,最近有些忙,二是,Core也是一直在摸索中,其实已经完成了一个框架了,并且正在准备在生产环境中 ...

  9. Java 前端加密传输后端解密以及验证码功能

    目录(?)[-] 加密解密 1 前端js加密概述 2 前后端加密解密 21 引用的js加密库 22 js加密解密 23 Java端加密解密PKCS5Padding与js的Pkcs7一致 验证码 1 概 ...

随机推荐

  1. docker 部署jenkins

    1.拉取镜像 docker pull jenkins/jenkins 2.运行jenkins镜像作为容器 运行命令如下: docker run -d -p 9086:8080 -p 50000:500 ...

  2. mybatis 分页失败 始终pageSize = 2147483647

    是在使用分页查询时 this.jobReadMapper.beginPager(pageParam);XXXXXXXXXXXXXXXXXXXXXXXXthis.xxxReadMapper.queryB ...

  3. 【比较】遗传算法GA和遗传编程GP有什么不同?

    遗传算法GA 本质上有一个固定的长度,这意味着所产生的功能有限的复杂性 通常会产生无效状态,因此需要以非破坏性方式处理这些状态 通常依赖于运算符优先级(例如,在我们的例子中,乘法发生在减法之前),这可 ...

  4. excel导入DataTable

    http://www.cnblogs.com/top5/archive/2010/03/12/1684559.html --下载excel的dll http://bbs.csdn.net/topics ...

  5. 苏浪浪 201771010120 面向对象程序设计(Java)第13周

    /实验十三  图形界面事件处理技术 1.实验目的与要求 (1) 掌握事件处理的基本原理,理解其用途: (2) 掌握AWT事件模型的工作机制: (3) 掌握事件处理的基本编程模型: (4) 了解GUI界 ...

  6. HDU6097 Mindis

    题目链接:https://cn.vjudge.net/problem/HDU-6097 知识点: 计算几何.圆的反演 题目大意: 已知一个圆心在原点的圆的半径,再给定 \(P, Q\) 两点坐标( \ ...

  7. Django + Celery 实现动态配置定时任务

    哈喽,今天给大家分享一篇Django+Celery实现动态配置定时任务,因为最近也是无意间看到一位大佬关于这块的文章,然后自己觉得不错,也想学习写一下,然后最终实现功能是在前端页面统一管理计划任务,大 ...

  8. C++软件开发面试题总结

    面试题有难有易,不能因为容易,我们就轻视,更不能因为难,我们就放弃.我们面对高薪就业的态度永远不变,那就是坚持.坚持.再坚持.出现问题,找原因:遇到困难,想办法.我们一直坚信只有在坚持中才能看到希望, ...

  9. [COCOS2DX-LUA]0-004.cocos2dx中的DrawNode的init的方法问题

    1.诱因 近期,项目接入了Bugly, 上报了一些平常测试不出来,或者很难重现的bug,这类bug非常难排查.原因有二,第一,问题无法重现,第二,修改了无法立即验证结果.有一个问题困恼了我很久,就是一 ...

  10. parrot os安装vmware pro

    转载https://www.linuxtechi.com/install-vmware-workstation-14-debian-9/ Open the terminal and execute t ...