图形验证码验证

图形验证码一般是防止恶意,人眼看起来都费劲,何况是机器。不少网站为了防止用户利用机器人自动注册、登录、灌水,都采用了验证码技术。所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片, 图片里加上一些干扰, 也有目前需要手动滑动的图形验证码. 这种可以有专门去做的第三方平台. 比如极验(https://www.geetest.com/), 那么本次课程讲解主要针对图形验证码.

spring security添加验证码大致可以分为三个步骤:

1. 根据随机数生成验证码图片;

2. 将验证码图片显示到登录页面;

3. 认证流程中加入验证码校验。 

Spring Security的认证校验是由UsernamePasswordAuthenticationFilter过滤器完成的,所以我们的验证码校验逻辑应该在这个过滤器之前。验证码通过后才能到后续的操作. 流程如下: 

代码实现: 

验证码生成类

package com.lagou.controller;import com.lagou.domain.ImageCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;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;
import java.util.concurrent.TimeUnit;/*** 处理生成验证码的请求*/
@RestController
@RequestMapping("/code")
public class ValidateCodeController {public final static String REDIS_KEY_IMAGE_CODE = "REDIS_KEY_IMAGE_CODE";public final static int expireIn = 60;  // 验证码有效时间 60s//使用sessionStrategy将生成的验证码对象存储到Session中,并通过IO流将生成的图片输出到登录页面上。@Autowiredpublic StringRedisTemplate stringRedisTemplate;@RequestMapping("/image")public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {//获取访问IPString remoteAddr = request.getRemoteAddr();//生成验证码对象ImageCode imageCode = createImageCode();//生成的验证码对象存储到redis中 KEY为REDIS_KEY_IMAGE_CODE+IP地址stringRedisTemplate.boundValueOps(REDIS_KEY_IMAGE_CODE + "-" + remoteAddr).set(imageCode.getCode(), expireIn, TimeUnit.SECONDS);//通过IO流将生成的图片输出到登录页面上ImageIO.write(imageCode.getImage(), "jpeg", response.getOutputStream());}/*** 用于生成验证码对象** @return*/private ImageCode createImageCode() {int width = 100;    // 验证码图片宽度int height = 36;    // 验证码图片长度int length = 4;     // 验证码位数//创建一个带缓冲区图像对象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());}/*** 获取随机演示** @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);}}

自定义验证码过滤器ValidateCodeFilter

package com.lagou.filter;import com.lagou.controller.ValidateCodeController;
import com.lagou.exception.ValidateCodeException;
import com.lagou.service.impl.MyAuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
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;/*** 自定义验证码过滤器,OncePerRequestFilter 一次请求只会经过一次过滤器*/
@Service
public class ValidateCodeFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 判断是否是登录请求if (request.getRequestURI().equals("/login") &&request.getMethod().equalsIgnoreCase("post")) {String imageCode = request.getParameter("imageCode");System.out.println(imageCode);// 具体的验证流程try {validate(request, imageCode);} catch (ValidateCodeException e) {myAuthenticationService.onAuthenticationFailure(request, response, e);return;}}// 如果不是登录请求,直接放行filterChain.doFilter(request, response);}@AutowiredStringRedisTemplate stringRedisTemplate;@AutowiredMyAuthenticationService myAuthenticationService;private void validate(HttpServletRequest request, String imageCode) {// 从redis中获取验证码String redisKey = ValidateCodeController.REDIS_KEY_IMAGE_CODE + "-" + request.getRemoteAddr();String redisImageCode = stringRedisTemplate.boundValueOps(redisKey).get();// 验证码的判断if (!StringUtils.hasText(redisImageCode)) {throw new ValidateCodeException("验证码的值不能为空");}if (redisImageCode == null) {throw new ValidateCodeException("验证码已过期");}if (!redisImageCode.equals(imageCode)) {throw new ValidateCodeException("验证码不正确");}// 从redis中删除验证码stringRedisTemplate.delete(redisKey);}
}

自定义验证码异常类

package com.lagou.exception;import org.springframework.security.core.AuthenticationException;/*** 验证码异常类*/
public class ValidateCodeException extends AuthenticationException {public ValidateCodeException(String msg) {super(msg);}
}

security配置类(里面添加我们自定义过滤器的顺序)

package com.lagou.config;import com.lagou.filter.ValidateCodeFilter;
import com.lagou.service.impl.MyAuthenticationService;
import com.lagou.service.impl.MyUserDetailsService;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;import javax.sql.DataSource;/*** spring security配置类*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredMyUserDetailsService myUserDetailsService;@AutowiredMyAuthenticationService myAuthenticationService;/*** 身份安全管理器** @param auth* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(myUserDetailsService);}@Overridepublic void configure(WebSecurity web) throws Exception {// 解决静态资源被拦截的问题web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/code/**");}@AutowiredValidateCodeFilter validateCodeFilter;/*** http请求方法** @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {/* http.httpBasic() // 开启httpBasic认证.and().authorizeRequests().anyRequest().authenticated(); // 所有请求都需要认证后才能访问 */// 将验证码过滤器添加在UsernamePasswordAuthenticationFilter过滤器的前面http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);http.formLogin() // 开启表单认证(默认方式).loginPage("/toLoginPage") // 设置自定义的登录页面.loginProcessingUrl("/login") // 表单提交的路径.usernameParameter("username").passwordParameter("password") // 自定义input的name值.successForwardUrl("/") // 登录成功之后跳转的路径.successHandler(myAuthenticationService)   // 登录成功后的处理.failureHandler(myAuthenticationService) // 登录失败后的处理.and().logout().logoutUrl("/logout") // 指定退出路径,默认为/logout.logoutSuccessHandler(myAuthenticationService) // 退出之后的处理逻辑.and().rememberMe() // 开启记住我功能.tokenValiditySeconds(1209600) // token失效时间,默认是两周.rememberMeParameter("remember-me") // 表示表单input里面的name值,不写默认就是remember-me.tokenRepository(getPersistentTokenRepository()).and().authorizeRequests().antMatchers("/toLoginPage").permitAll() // 放行登录页面.anyRequest().authenticated();// 关闭csrf防护http.csrf().disable();// 加载同源域名下的iframe页面http.headers().frameOptions().sameOrigin();}/*** 负责token与数据库之间的操作** @return*/@AutowiredDataSource dataSource;@Beanpublic PersistentTokenRepository getPersistentTokenRepository() {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource); // 设置数据源tokenRepository.setCreateTableOnStartup(false); // 启动时自动帮我们创建一张表,第一次启动设置true,第二次启动设置为false或者注释掉return tokenRepository;}
}

将MyAuthenticationService的异常信息进行自动获取,而不是我们固定死

    /*** 登录失败后的处理逻辑*/@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {System.out.println("登录失败后继续处理...............");// 重定向到login页面// redirectStrategy.sendRedirect(request, response, "/toLoginPage");Map<Object, Object> result = new HashMap<>();result.put("code", HttpStatus.UNAUTHORIZED.value()); // 401result.put("message", exception.getMessage()); // 修改的地方,现在异常信息从exception中获取response.setContentType("application/json;charset=UTF-8");response.getWriter().write(objectMapper.writeValueAsString(result));}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/253885.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

gin集成图形验证码

简介 github 文档 生成图形验证码 package mainimport ("github.com/gin-gonic/gin""github.com/mojocn/base64Captcha""net/http" )var store base64Captcha.DefaultMemStorefunc main() {r : gin.Default()group : r.Group("/captcha…

SpringBoot生成图形验证码

需求&#xff1a;验证码一码一用&#xff0c;验证之后&#xff0c;不管是成功还是失败&#xff0c;都需要重新获取或者刷新二维码。 大致思路&#xff1a;后端生成验证码后还需要生成一个UUID与之对应&#xff0c;存储到缓存&#xff08;记得添加过期时间&#xff09;&#xf…

自己实现图形验证码

如果不想重复造轮子&#xff0c;参考上一篇文章&#xff1a;SpringBoot生成图形验证码_Muscleheng的博客-CSDN博客 这里不需要依赖开源组件包&#xff0c;完全自己实现图形验证码功能 两步完成&#xff1a; 第一步&#xff1a;编写图形验证码工具 package com.zhh.demo.com…

图形验证码的使用

在用户登录的时候&#xff0c;除了要输入用户名和密码&#xff0c;有时候还需要输入验证码进行验证&#xff0c;如下&#xff1a; 现在一般用短信验证码比较多&#xff0c;但是图形验证码也有使用。记录一下图形验证码的使用过程。 1、验证码生成器 先定义一个验证码的生成器…

vue图形验证码

Vue图形验证码 组件 可以自行封装一下&#xff0c;放在components目录下。 <template><span class"s-canvas" click"changeCode"><canvas id"s-canvas" :width"contentWidth" :height"contentHeight">…

图形验证码安全

目录 图形验证码 图形验证码的作用和原理 图形验证码的分类 图形验证码的验证过程 图形验证码的安全问题 静态图形验证码的破解 利用Python脚本破解静态图形验证码 图形验证码 我们经常在登录app或者网页的时候&#xff0c;都会需要我们输入图形验证码上的内容&#xf…

图形验证码

一、图形验证码是什么&#xff1f; 图形验证码是一些没有规则的图文的组合&#xff0c;参考下图 二、图形验证码有什么用&#xff1f; 防止恶意攻击者采用恶意工具批量注册账号或是大量频繁调用某些请求&#xff0c;给服务器造成压力&#xff0c;占用大量的系统资源。 三、图形…

C语言——数据在内存中的存储(上)

数据在内存中的存储 1. 数据类型的介绍 之前已经介绍过C语言中的基本数据类型了&#xff0c;主要有&#xff1a; char //字符数据类型short //短整型int //整形long //长整型long long //更长的整形float //单精度浮点数double //双精度浮点数 注意&#xff1a;C语言中是是没…

php图形 验证码代码,PHP制作图形验证码代码分享,php图形验证码代码_PHP教程

PHP制作图形验证码代码分享,php图形验证码代码 效果: myvcode.class.php:封装创建验证码的类 /* * file:myvcode.class.php * 验证码类,类名Vcode */ class Vcode {private $width; /*验证码宽度*/ private $height; /*验证码高度*/ private $codeNum; /*验证码字符个数*/ p…

Android图形验证码

1. 前言 图形验证码可以让服务器以图片的形式传给客户端&#xff0c;也可以让客户端自己实现。那客户端要怎么做呢&#xff1f;其实很简单&#xff0c;可以使用Android的Canvas、Paint和Random来实现。用Random来随机生成数字、字母、颜色、画笔原点等等&#xff0c;设置Paint…

java生成图形验证码

随时随地阅读更多技术实战干货&#xff0c;获取项目源码、学习资料&#xff0c;请关注源代码社区公众号(ydmsq666) 首先&#xff0c;需要生成验证码字符串&#xff0c;方式很多&#xff0c;下面提供一种&#xff0c;根据指定源的方式来生成验证码 /*** 使用系统默认字符源生成验…

图形验证码最佳攻略2

下面是注册 如果是手机用户注册,需要发送短信验证码 说明: 发送图形验证码是为了拦截发送短信的.但是不拦截"注册帐号" 但是,用户体验很别扭,因为图形验证码很显然是错误的,但是却可以注册成功. 如果点击注册帐号 ,也要校验图形验证码,那就让用户输入两次图形验证码,…

小程序图形验证码输入校验例子

前言 本教程是基于 “apifm-wxapi” 模块&#xff0c;教你快速实现小程序开发&#xff0c;所以你可能需要先了解以下知识点&#xff1a; 《创建 HelloWorld 项目》《使用 “apifm-wxapi” 快速开发小程序》《免费注册开通后台&#xff0c;获得专属域名》 功能说明 图形验证码的…

Part1:使用 TensorFlow 和 Keras 的 NeRF计算机图形学和深度学习——计算机图形学世界中相机的工作原理

Part1&#xff1a;使用 TensorFlow 和 Keras 的 NeRF计算机图形学和深度学习 1. 效果图2. 原理2.0 前向成像模型2.1 世界坐标系2.2 相机坐标系2.3 坐标变换2.4 投影转换2.5 数据 3. 源码参考 是否有一种方法可以仅从一个场景多张不同视角的照片中捕获整个3D场景&#xff1f; 有…

IDEA+Mysql+Sqlserver安装步骤_kaic

下载Intellij 开发工具&#xff0c;如果有请检查软件是否过期&#xff0c;如果过期卸载电脑上的Intellij软件。卸载步骤&#xff1a; 打开控制面板&#xff0c;选择卸载程序找到Intellij右键卸载 如果没有请保存文档中的Intellij.zip压缩包,进行安装&#xff0c;安装步骤 双…

电脑重置网络

1、键盘WinR键&#xff0c;弹出窗口 2、然后在里面输入cmd输入CMD 3、出现的命令提示框内输入“netsh winsock reset"按”Enter“键 4、重启电脑后生效。

重置电脑

步骤1&#xff1a; 以Win10系统为例 直接在搜索框输入:reset 或者直接选择「设置」-「更新和安全」-「恢复」 步骤2&#xff1a; 步骤3&#xff1a; 根据个人情况选择【保留我的文件】或者【删除所有内容】

如何让Win10 重置此电脑功能无法使用

环境&#xff1a; Win10 专业版 问题描述&#xff1a; 通过设置-更新和安全-恢复-重置此电脑&#xff0c;如何让Win10 重置此电脑功能无法使用 解决方案&#xff1a; 1.PE下删除Win Re 恢复分区&#xff0c;然后重置就无法使用

重置计算机后无法开机,win10重置此电脑失败怎么办_win10重置此电脑失败无法开机修复方法...

当win10系统使用时间长了&#xff0c;难免会有一些电脑故障的出现&#xff0c;这时有些用户就会选择使用重置电脑的方式来解决&#xff0c;但是最近有用户再给自己的win10系统进行重置时总是出现失败的情况&#xff0c;那么win10重置此电脑失败怎么办呢&#xff1f;下面就来告诉…

win10怎么重置计算机,如何重置Win10系统电脑

当系统出现一些难以解决的问题时&#xff0c;我们一般会选择重置电脑来解决问题&#xff0c;比起重装电脑来说简单很多&#xff0c;但是也要注意有可能会失败。下面小编就以win10为例&#xff0c;给大家讲讲怎么重置电脑的操作步骤吧。 操作步骤&#xff1a; 1、打开屏幕侧方的…