Spring Security添加图形验证码

大致思路:
1.根据随机数生成验证码图片
2.将验证码图片显示到登录页面
3.认证流程中加入验证码校验

依赖

         <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.social</groupId><artifactId>spring-social-config</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.7</version></dependency>

首先定义一个验证码对象

package com.DNYDYS.validate.code;import java.awt.image.BufferedImage;
import java.time.LocalDateTime;public class ImageCode {//image图片private BufferedImage image;//code验证码private String code;//expireTime过期时间private LocalDateTime expireTime;public ImageCode(BufferedImage image, String code, int expireIn) {this.image = image;this.code = code;this.expireTime = LocalDateTime.now().plusSeconds(expireIn);}public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {this.image = image;this.code = code;this.expireTime = expireTime;}//判断验证码是否已过期boolean isExpire() {return LocalDateTime.now().isAfter(expireTime);}public BufferedImage getImage() {return image;}public void setImage(BufferedImage image) {this.image = image;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public LocalDateTime getExpireTime() {return expireTime;}public void setExpireTime(LocalDateTime expireTime) {this.expireTime = expireTime;}
}

处理生成验证码请求

package com.DNYDYS.controller;import com.DNYDYS.validate.code.ImageCode;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.annotation.GetMapping;
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;@RestController
public class ValidateController {public final static String SESSION_KEY_IMAGE_CODE = "SESSION_KEY_IMAGE_CODE";private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();//生成验证码对象@GetMapping("/code/image")public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {ImageCode imageCode = createImageCode();sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, imageCode);ImageIO.write(imageCode.getImage(), "jpeg", response.getOutputStream());}private ImageCode createImageCode() {int width = 100; // 验证码图片宽度int height = 36; // 验证码图片长度int length = 4; // 验证码位数int expireIn = 60; // 验证码有效时间 60sBufferedImage 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 < 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);}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);}}

不被拦截

package com.DNYDYS.security.browser;import com.DNYDYS.handler.MyAuthenticationFailureHandler;
import com.DNYDYS.handler.MyAuthenticationSucessHandler;
import com.DNYDYS.validate.code.ValidateCodeFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyAuthenticationSucessHandler authenticationSucessHandler;@Autowiredprivate MyAuthenticationFailureHandler authenticationFailureHandler;@Autowiredprivate ValidateCodeFilter validateCodeFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器.formLogin() // 表单登录// http.httpBasic() // HTTP Basic.loginPage("/authentication/require") // 登录跳转 URL.loginProcessingUrl("/login") // 处理表单登录 URL.successHandler(authenticationSucessHandler) // 处理登录成功.failureHandler(authenticationFailureHandler) // 处理登录失败.and().authorizeRequests() // 授权配置.antMatchers("/authentication/require","/login.html","/code/image").permitAll() // 无需认证的请求路径.anyRequest()  // 所有请求.authenticated() // 都需要认证.and().csrf().disable();}
}

认证流程添加验证码校验

package com.DNYDYS.validate.code;import org.springframework.security.core.AuthenticationException;public class ValidateCodeException extends AuthenticationException {private static final long serialVersionUID = 5022575393500654458L;ValidateCodeException(String message) {super(message);}
}

由于Spring Security并没有直接提供验证码校验相关的过滤器接口,所以我们需要自己定义一个验证码校验的过滤器ValidateCodeFilter

package com.DNYDYS.validate.code;import com.DNYDYS.controller.ValidateController;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
public class ValidateCodeFilter extends OncePerRequestFilter {@Autowiredprivate AuthenticationFailureHandler authenticationFailureHandler;private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {if (StringUtils.equalsIgnoreCase("/login", httpServletRequest.getRequestURI())&& StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), "post")) {try {validateCode(new ServletWebRequest(httpServletRequest));} catch (ValidateCodeException e) {authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);return;}}filterChain.doFilter(httpServletRequest, httpServletResponse);}private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "imageCode");if (StringUtils.isBlank(codeInRequest)) {throw new ValidateCodeException("验证码不能为空!");}if (codeInSession == null) {throw new ValidateCodeException("验证码不存在!");}if (codeInSession.isExpire()) {sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);throw new ValidateCodeException("验证码已过期!");}if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {throw new ValidateCodeException("验证码不正确!");}sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);}}

拦截登录接口

package com.DNYDYS.security.browser;import com.DNYDYS.handler.MyAuthenticationFailureHandler;
import com.DNYDYS.handler.MyAuthenticationSucessHandler;
import com.DNYDYS.validate.code.ValidateCodeFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyAuthenticationSucessHandler authenticationSucessHandler;@Autowiredprivate MyAuthenticationFailureHandler authenticationFailureHandler;@Autowiredprivate ValidateCodeFilter validateCodeFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器.formLogin() // 表单登录// http.httpBasic() // HTTP Basic.loginPage("/authentication/require") // 登录跳转 URL.loginProcessingUrl("/login") // 处理表单登录 URL.successHandler(authenticationSucessHandler) // 处理登录成功.failureHandler(authenticationFailureHandler) // 处理登录失败.and().authorizeRequests() // 授权配置.antMatchers("/authentication/require","/login.html","/code/image").permitAll() // 无需认证的请求路径.anyRequest()  // 所有请求.authenticated() // 都需要认证.and().csrf().disable();}
}

前端页面

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body>
<form class="login-page" action="/login" method="post"><h3>账户登录</h3>用户名:<input type="text"  name="username" required="required"/><br>密  码:<input type="password"  name="password" required="required"/><br><span style="display: inline">验证码:   <input type="text" name="imageCode"  /><img src="/code/image"/></span><br><button type="submit">登录</button>
</form>
</body>
</html>

ok 基本齐活

访问页面(真low)

http://localhost:8080/login.html

用户名随便,密码在代码里直接写死了 admin

登录成功

等一分钟,等验证码过期

输入错误的验证码


ok 齐活儿

浮世万千不得有三
水中月镜中花梦中你
月可求花可得
唯你求而不得

Spring Security添加图形验证码相关推荐

  1. 4.Spring Security 添加图形验证码

    添加验证码大致可以分为三个步骤:根据随机数生成验证码图片:将验证码图片显示到登录页面:认证流程中加入验证码校验.Spring Security的认证校验是由UsernamePasswordAuthen ...

  2. 9.Spring Security添加记住我功能

    在网站的登录页面中,记住我选项是一个很常见的功能,勾选记住我后在一段时间内,用户无需进行登录操作就可以访问系统资源.在Spring Security中添加记住我功能很简单,大致过程是:当用户勾选了记住 ...

  3. 5.Spring Security 短信验证码登录

    Spring Security 短信验证码登录 在 Spring Security 添加图形验证码一节中,我们已经实现了基于 Spring Boot + Spring Security 的账号密码登录 ...

  4. Spring Security 短信验证码登录(5)

    在Spring Security添加图形验证码中,我们已经实现了基于Spring Boot + Spring Security的账号密码登录,并集成了图形验证码功能.时下另一种非常常见的网站登录方式为 ...

  5. SpringSecurity添加图形验证码认证功能

    SpringSecurity添加图形验证码认证功能 第一步:图形验证码接口 1.使用第三方的验证码生成工具Kaptcha https://github.com/penggle/kaptcha @Con ...

  6. spring security 短信验证码登录

    短信登录过滤器  SmsAuthenticationFilter  import org.springframework.lang.Nullable; import org.springframewo ...

  7. Spring boot+ Spring security 实现图片验证码验证

    springboot+security实现用户权限管理后,登陆要求增加图片验证码 pring security使用众多的过滤器对url进行拦截,以此来进行权限管理.Spring security不允许 ...

  8. 使用Spring Security添加RememberMe身份验证

    我在" 将社交登录添加到Jiwhiz博客"中提到,RememberMe功能不适用于Spring Social Security. 好吧,这是因为该应用程序现在不通过用户名和密码对用 ...

  9. vue添加图形验证码功能

    上图看功能,每点击一次切换验证码!前端判断验证码是否输入,后端判断验证码是否正确! html <el-form-item label="验证码" prop="cod ...

最新文章

  1. HP-UX crontab: you are not authorized to use cron
  2. IT常说的协议指的是什么?—Vecloud微云
  3. GNS3 cloud 连接错误_远程桌面连接服务器身份验证错误要求的函数不受支持
  4. 敏捷开发的45个好习惯
  5. java导出excel文件名_怎么解决java导出excel时文件名乱码
  6. mysql中常见的几种索引
  7. windows IIS Web服务器 发布网站
  8. windows系统引导配置命令
  9. 第十六届中国研究生电子设计竞赛记录
  10. 微信公众号用秀米网插入视频
  11. 如何用Python快速优雅的批量修改Word文档样式?
  12. 520了,用32做个简单的小程序
  13. SpringDataRedis使用
  14. 客户端登录阿里云mysql数据库_Mysql数据库之数据库术语和客户端登陆
  15. 提升效率之如何打印出漂亮的带颜色的日志(输出高亮)
  16. tp5:为什么find()出来的数据有时候可以用toArray() 有时候会报错?
  17. C++ Primer Plus (第六版)编程练习记录(chapter14 C++中的代码重用)
  18. Linux聊天室项目知识整理(一)
  19. mysql 分销提成计算_销售人员工资计算表-2017销售人员工资提成计算表免费版-东坡下载...
  20. 安徽汽车网程序员删库跑路?不,真相是这样!

热门文章

  1. 了解css3属性transform
  2. MATLAB安装的时候有关DVD1切换DVD2的问题
  3. 分享76个ASP新闻文章源码,总有一款适合您
  4. 重磅!《国家智能制造标准体系建设指南(2021版)》
  5. Python神器可以拯救小学数学题不会做
  6. 这10款良心电脑软件,亲测,超好用!
  7. Unity代码用VS进行调试
  8. ObRegisterCallbacks返回0xC0000022(拒绝访问)解决方案
  9. 光纤收发器的LFP和EFE
  10. win10 mmcv-full mmdection 安装 问题解决方案