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. os.system() 和 os.popen()
  2. html兼容不同屏幕 代码,rem的正确使用姿势 -- 完美解决H5页面不同尺寸屏幕的适配问题...
  3. Isolation Forest
  4. 关于Lucene的自定义Sort排序
  5. RandomizedSearchCV 和GridSearchCV
  6. shell命令之---LVM文件系统
  7. java调用python库pyd_Java调用Python的两种方式
  8. flash加xml图片叠加焦点图,左右箭头翻页
  9. LeetCode 65. 有效数字(逻辑题,难)
  10. Web Hacking 101 中文版 十五、代码执行
  11. VS2008源代码管理软件组合-visualSVN Server+TortoiseSVN+AnkhSvn
  12. vue store的值刷新就被覆盖解决方案
  13. 如何为我们的程序编写开发文档——Java文档注释
  14. hbase mysql hdfs_Alex的Hadoop菜鸟教程:第8课Sqoop1导入Hbase以及Hive
  15. 整理农行面试软开最常问到的题---------框架
  16. Jmeter链接MySQL读写数据
  17. excel减法函数_电子表格减法公式
  18. Linux I2C 核心、总线、与设备驱动
  19. php如何把word转图片
  20. 学习OpenCV3 面阵相机标定方法

热门文章

  1. 百度语音接口api调用
  2. Lastpass——密码管理工具
  3. HTML5/CSS3基础——div盒子水平垂直居中的三种方案
  4. 基于SpringBoot+Gradle+Zxing+JQuery(原生JS)开发条形码/二维码扫描工具,且采用原生JS调用浏览器摄像头
  5. excel 宏 加1的计算机,巧用宏命令来为Excel工作表公式加密码 -电脑资料
  6. createfile 无权限_Microsoft Windows CreateFile API命名管道权限提升漏洞 | 学步园
  7. 解读《今日头条2018手机行业白皮书》:手机品牌们如何逆周期生长
  8. ‘堆’出你的洪荒之力
  9. css和js用哪个,yepnope(相对路径,css和js)的使用
  10. 【独家】华为OD机试 - 基站维修工程师(C 语言解题)