当前位置:网站首页>Graphic verification code verification
Graphic verification code verification
2022-07-19 04:07:00 【Leisurely summer】
Graphic verification code is generally used to prevent malicious , The human eye seems to struggle , Not to mention machines . Many websites in order to prevent users from using robot automatic registration 、 Sign in 、 irrigation , They all use captcha technology . The so-called verification code , It's a random string of numbers or symbols , Create a picture , Add some noise to the picture , There are also graphic verification codes that currently require manual sliding . This kind of third-party platform that can be done specially . For example, extreme experience (https://www.geetest.com/), Then this course mainly focuses on graphic verification code .
spring security Adding a verification code can be roughly divided into three steps :
1. Generate verification code image according to random number ;
2. Display the captcha image on the login page ;
3. Verification code verification is added to the authentication process .
Spring Security The authentication verification of is made by UsernamePasswordAuthenticationFilter Filter finished , So our verification logic should be before this filter . Only after the verification code passes can the subsequent operation be started . The process is as follows :
Code implementation :
Verification code generation class
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;
/**
* Process the request to generate the verification code
*/
@RestController
@RequestMapping("/code")
public class ValidateCodeController {
public final static String REDIS_KEY_IMAGE_CODE = "REDIS_KEY_IMAGE_CODE";
public final static int expireIn = 60; // Verification code valid time 60s
// Use sessionStrategy Store the generated captcha object in Session in , And pass IO The stream outputs the generated image to the login page .
@Autowired
public StringRedisTemplate stringRedisTemplate;
@RequestMapping("/image")
public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
// Get access to IP
String remoteAddr = request.getRemoteAddr();
// Generate captcha object
ImageCode imageCode = createImageCode();
// The generated verification code object is stored in redis in KEY by REDIS_KEY_IMAGE_CODE+IP Address
stringRedisTemplate.boundValueOps(REDIS_KEY_IMAGE_CODE + "-" + remoteAddr)
.set(imageCode.getCode(), expireIn, TimeUnit.SECONDS);
// adopt IO The stream outputs the generated image to the login page
ImageIO.write(imageCode.getImage(), "jpeg", response.getOutputStream());
}
/**
* Object used to generate verification code
*
* @return
*/
private ImageCode createImageCode() {
int width = 100; // Verification code picture width
int height = 36; // Captcha image length
int length = 4; // Number of verification codes
// Create a buffered image object
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Get the... Drawn on the image Graphics object
Graphics g = image.getGraphics();
Random random = new Random();
// Set the color 、 And draw lines randomly
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);
}
// Generate random number And draw
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());
}
/**
* Get random Demo
*
* @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);
}
}
Custom verification code filter 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;
/**
* Custom verification code filter ,OncePerRequestFilter A request will only pass through the filter once
*/
@Service
public class ValidateCodeFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// Determine if it is a login request
if (request.getRequestURI().equals("/login") &&
request.getMethod().equalsIgnoreCase("post")) {
String imageCode = request.getParameter("imageCode");
System.out.println(imageCode);
// Specific verification process
try {
validate(request, imageCode);
} catch (ValidateCodeException e) {
myAuthenticationService.onAuthenticationFailure(request, response, e);
return;
}
}
// If it's not a login request , Direct release
filterChain.doFilter(request, response);
}
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
MyAuthenticationService myAuthenticationService;
private void validate(HttpServletRequest request, String imageCode) {
// from redis Get the verification code from
String redisKey = ValidateCodeController.REDIS_KEY_IMAGE_CODE + "-" + request.getRemoteAddr();
String redisImageCode = stringRedisTemplate.boundValueOps(redisKey).get();
// Verification code judgment
if (!StringUtils.hasText(redisImageCode)) {
throw new ValidateCodeException(" The value of the verification code cannot be empty ");
}
if (redisImageCode == null) {
throw new ValidateCodeException(" The verification code has expired ");
}
if (!redisImageCode.equals(imageCode)) {
throw new ValidateCodeException(" The verification code is incorrect ");
}
// from redis Delete the verification code
stringRedisTemplate.delete(redisKey);
}
}
Custom verification code exception class
package com.lagou.exception;
import org.springframework.security.core.AuthenticationException;
/**
* Verification code exception class
*/
public class ValidateCodeException extends AuthenticationException {
public ValidateCodeException(String msg) {
super(msg);
}
}
security Configuration class ( Add the order of our custom filters )
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 class
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyUserDetailsService myUserDetailsService;
@Autowired
MyAuthenticationService myAuthenticationService;
/**
* Identity security manager
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
@Override
public void configure(WebSecurity web) throws Exception {
// Solve the problem of static resources being intercepted
web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/code/**");
}
@Autowired
ValidateCodeFilter validateCodeFilter;
/**
* http Request method
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
/* http.httpBasic() // Turn on httpBasic authentication
.and().authorizeRequests().anyRequest().authenticated(); // All requests require authentication before accessing */
// Add the verification code filter to UsernamePasswordAuthenticationFilter In front of the filter
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
http.formLogin() // Open form authentication ( Default mode )
.loginPage("/toLoginPage") // Set a custom login page
.loginProcessingUrl("/login") // Path to form submission
.usernameParameter("username")
.passwordParameter("password") // Customize input Of name value
.successForwardUrl("/") // The path to jump after successful login
.successHandler(myAuthenticationService) // Processing after successful login
.failureHandler(myAuthenticationService) // Handling after login failure
.and().logout().logoutUrl("/logout") // Specify exit path , The default is /logout
.logoutSuccessHandler(myAuthenticationService) // Processing logic after exit
.and().rememberMe() // Turn on remember me
.tokenValiditySeconds(1209600) // token Failure time , The default is two weeks
.rememberMeParameter("remember-me") // Represents a form input Inside name value , No, the default is remember-me
.tokenRepository(getPersistentTokenRepository())
.and().authorizeRequests().antMatchers("/toLoginPage").permitAll() // Release login page
.anyRequest().authenticated();
// close csrf protective
http.csrf().disable();
// Load the iframe page
http.headers().frameOptions().sameOrigin();
}
/**
* be responsible for token Operation with database
*
* @return
*/
@Autowired
DataSource dataSource;
@Bean
public PersistentTokenRepository getPersistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource); // Set data source
tokenRepository.setCreateTableOnStartup(false); // Automatically create a table for us when starting , First start setting true, The second startup is set to false Or comment out
return tokenRepository;
}
}
take MyAuthenticationService Get the abnormal information automatically , Instead of our fixed death
/**
* Processing logic after login failure
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println(" Continue processing after login failure ...............");
// Redirect to login page
// redirectStrategy.sendRedirect(request, response, "/toLoginPage");
Map<Object, Object> result = new HashMap<>();
result.put("code", HttpStatus.UNAUTHORIZED.value()); // 401
result.put("message", exception.getMessage()); // Where to modify , Now the exception information is from exception In order to get
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(result));
}
边栏推荐
- Chapter 5 performance platform godeye source code analysis - third party module
- 【数据库】期末必知必会-----第一章 数据库概述
- 【数据库】期末必知必会-----第九章 数据库设计
- donet framework4.X==windows窗体应用新建项目,通过System.Data.SqlClient连接sqlserver进行查询
- 论文精读系列文章
- Get to know esp8266 (II) -- build a network server to realize remote control
- Matlab drawing activation function sigmoid, relu
- How session and cookies work
- ASP.NET1==visual studio创建asp.net demo
- Kubernetes learning persistent storage storageclass (4)
猜你喜欢
XDC 2022 Intel 技术专场:英特尔软硬件技术构筑云计算架构基石
Container adapter - stack, queue, priority queue
dapr系列(一)
How to do clear scanning: try scanning tailor scantailor advanced | including the usage of scantailor
Get to know esp8266 (II) -- build a network server to realize remote control
小程序毕设作品之微信电子书阅读小程序毕业设计(8)毕业设计论文模板
Teaching reform and software platform of entrepreneurship practice simulation
C语言详解系列——循环语句的练习与巩固,二分查找的讲解
The software supply chain security risk that makes enterprise digitalization and it executives bear the brunt of the cauldron points to the north
【超能云终端创领先机】本地计算云端管理,英特尔助力教育数字化
随机推荐
如何更有效的过滤病毒/垃圾邮件!
Matlab drawing activation function sigmoid, relu
Swagger
hello world驱动
micro、M3O微服务系列(三)
sql界面切换不能获取焦点
第2章——创建与维护MySQL数据库
Tutorial: Adaptive Replication and Partitioning in Data Systems
Chapter 0 performance platform godeye source code analysis - Course Introduction
PAC十年:见证HPC从CPU时代走向XPU纪元
论文精读系列文章
如何使用谷歌地球客户端及kml下载
GNN系列 GCN简述 推导理解 及 DGL 源码解析
最小生成树
Tutorial: Adaptive Replication and Partitioning in Data Systems
可省近90%服务器,反欺诈效率却大增,PayPal打破「AI内存墙」的方案为何如此划算?
Welcome to Hensen_ Blog directory of (full site navigation)
【数据库】期末必知必会-----第十一章 并发控制
【数据库】期末必知必会-----第八章 数据库安全
小程序毕设作品之微信在线教育视频点播学习小程序毕业设计(3)后台功能