当前位置:网站首页>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));
    }

 

原网站

版权声明
本文为[Leisurely summer]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/200/202207170323285205.html