SpringBoot implements login verification code (with integrated Spring Security)

1. Tools for generating verification codes

This tool category is very common, and there are many on the Internet. It is to draw a simple verification code and write the verification code to the front-end page through the stream.

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

/**
 * @Author: gaoyang
 * @Date: 2020/7/8  14:20
 * @Description: 生成验证码的工具类
 */

public class VerificationCode {
    
    

    private int width = 100;// 生成验证码图片的宽度
    private int height = 30;// 生成验证码图片的高度
    private String[] fontNames = {
    
     "宋体", "楷体", "隶书", "微软雅黑" };
    private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色
    private Random random = new Random();
    private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private String text;// 记录随机字符串

    /**
     * 获取一个随意颜色
     *
     * @return
     */
    private Color randomColor() {
    
    
        int red = random.nextInt(150);
        int green = random.nextInt(150);
        int blue = random.nextInt(150);
        return new Color(red, green, blue);
    }

    /**
     * 获取一个随机字体
     *
     * @return
     */
    private Font randomFont() {
    
    
        String name = fontNames[random.nextInt(fontNames.length)];
        int style = random.nextInt(4);
        int size = random.nextInt(5) + 24;
        return new Font(name, style, size);
    }

    /**
     * 获取一个随机字符
     *
     * @return
     */
    private char randomChar() {
    
    
        return codes.charAt(random.nextInt(codes.length()));
    }

    /**
     * 创建一个空白的BufferedImage对象
     *
     * @return
     */
    private BufferedImage createImage() {
    
    
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.setColor(bgColor);// 设置验证码图片的背景颜色
        g2.fillRect(0, 0, width, height);
        return image;
    }

    public BufferedImage getImage() {
    
    
        BufferedImage image = createImage();
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 4; i++) {
    
    
            String s = randomChar() + "";
            sb.append(s);
            g2.setColor(randomColor());
            g2.setFont(randomFont());
            float x = i * width * 1.0f / 4;
            g2.drawString(s, x, height - 8);
        }
        this.text = sb.toString();
        drawLine(image);
        return image;
    }

    /**
     * 绘制干扰线
     *
     * @param image
     */
    private void drawLine(BufferedImage image) {
    
    
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        int num = 5;
        for (int i = 0; i < num; i++) {
    
    
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g2.setColor(randomColor());
            g2.setStroke(new BasicStroke(1.5f));
            g2.drawLine(x1, y1, x2, y2);
        }
    }

    public String getText() {
    
    
        return text;
    }

    public static void output(BufferedImage image, OutputStream out) throws IOException {
    
    
        ImageIO.write(image, "JPEG", out);
    }

}

2. Verification code test interface

import com.javaboy.vhr.utils.RespBean;
import com.javaboy.vhr.utils.VerificationCode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * @Author: gaoyang
 * @Date: 2020/10/24  15:05
 * @Description:
 */
@Api(tags = "未登录API")
@RestController
public class LoginController {
    
    

    @ApiOperation(value = "生成验证码图片")
    @GetMapping("/verifyCode")
    public void verifyCode(HttpServletRequest request, HttpServletResponse resp) throws IOException {
    
    
        VerificationCode code = new VerificationCode();
        BufferedImage image = code.getImage();
        String text = code.getText();
        HttpSession session = request.getSession(true);
        session.setAttribute("verify_code", text);
        VerificationCode.output(image,resp.getOutputStream());
    }

}

Now that the verification code generation has been implemented, the following are the things that need to be done to integrate into Spring Security.

3. Verification code filter

import com.fasterxml.jackson.databind.ObjectMapper;
import com.javaboy.vhr.utils.RespBean;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author: gaoyang
 * @date: 2020-12-27 14:20
 * @description: 验证码过滤器
 */
@Component
public class VerificationCodeFilter extends GenericFilter {
    
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        if ("POST".equals(req.getMethod()) && "/doLogin".equals(req.getServletPath())) {
    
    
            //登录请求
            String code = req.getParameter("code");
            String verify_code = (String) req.getSession().getAttribute("verify_code");
            if (code == null || "".equals(code) || !verify_code.toLowerCase().equals(code.toLowerCase())) {
    
    
                //验证码不正确
                resp.setContentType("application/json;charset=utf-8");
                PrintWriter out = resp.getWriter();
                out.write(new ObjectMapper().writeValueAsString(RespBean.error("验证码错误!")));
                out.flush();
                out.close();
                return;
            } else {
    
    
                filterChain.doFilter(req, resp);
            }
        } else {
    
    
            filterChain.doFilter(req, resp);
        }

    }
}

The custom filter inherits from GenericFilterBean and implements the doFilter method in it. In the doFilter method, when the request method is POST and the request address is /doLogin, the code field value in the parameter is obtained. This field saves the user's front-end page Pass the verification code, and then get the verification code saved in the session. If the user does not send the verification code, the verification code cannot be empty exception will be thrown. If the user passes the verification code, it will be judged whether the verification code is correct or not. If it is correct, throw an exception, otherwise execute chain.doFilter(request, response); to make the request go down.

4. Spring Security configuration class introduces verification code filter

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    @Resource
    private VerificationCodeFilter verificationCodeFilter;

    @Override
    public void configure(WebSecurity web) throws Exception {
    
    
        web.ignoring().antMatchers(
                "/verifyCode");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        // 验证码拦截器
        http.addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                ...
                ...
                .permitAll()
                .and()
                .csrf().disable();
    }

}

Only part of the core code is posted here, namely http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);, after that, the entire configuration is complete.
Next, in the login, you need to pass in the verification code. If it is not passed or passed incorrectly, an exception will be thrown. For example, if it is not passed, the following exception will be thrown:

5. Effect picture

Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_42201180/article/details/111801133