SpringSecurity+Vue:实现添加登录图片验证码

第一步,准备一个可以生成验证码的类或者jar包,我这里就用工具类了,网上随便找的一个工具类

/**
 * 生成图片验证码的工具类
 */
public class VerifyCode {
    private int w = 70;
    private int h = 35;
    private Random r = new Random();
    // {"宋体", "华文楷体", "黑体", "华文新魏", "华文隶书", "微软雅黑", "楷体_GB2312"}
    private String[] fontNames  = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312"};
    //可选字符
    private String codes  = "23456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";
    //背景色
    private Color bgColor  = new Color(255, 255, 255);
    private String text ;

    //生成随机的颜色
    private Color randomColor () {
        int red = r.nextInt(150);
        int green = r.nextInt(150);
        int blue = r.nextInt(150);
        return new Color(red, green, blue);
    }

    //生成随机字体
    private Font randomFont () {
        int index = r.nextInt(fontNames.length);
        String fontName = fontNames[index];
        int style = r.nextInt(4);  //生成随机样式,0:无样式,1:粗体,2:斜体,3:粗体+斜体
        int size = r.nextInt(5) + 24; //生成随机字号
        return new Font(fontName, style, size);
    }

    //画干扰线
    private void drawLine (BufferedImage image) {
        int num  = 3; //总共画三条干扰线
        Graphics2D g2 = (Graphics2D)image.getGraphics();
        for(int i = 0; i < num; i++) { //生成两个点的左边,即4个值
            int x1 = r.nextInt(w);
            int y1 = r.nextInt(h);
            int x2 = r.nextInt(w);
            int y2 = r.nextInt(h);
            g2.setStroke(new BasicStroke(1.5F));
            g2.setColor(Color.BLUE);   //设置干扰线颜色为蓝色
            g2.drawLine(x1, y1, x2, y2);
        }
    }

    //随机生成一个字符
    private char randomChar () {
        int index = r.nextInt(codes.length());
        return codes.charAt(index);
    }

    //创建BufferedImage
    private BufferedImage createImage () {
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D)image.getGraphics();
        g2.setColor(this.bgColor);
        g2.fillRect(0, 0, w, h);
        return image;
    }

    public BufferedImage getImage () {
        BufferedImage image = createImage(); //创建图片缓冲区
        Graphics2D g2 = (Graphics2D)image.getGraphics();//得到绘制环境,画笔
        StringBuilder sb = new StringBuilder();//用来装载生成的验证码文本
        // 向图片中画4个字符
        for(int i = 0; i < 4; i++)  {
            String s = randomChar() + "";   //随机生成一个字符
            sb.append(s);
            float x = i * 1.0F * w / 4; //设置当前字符的x轴坐标
            g2.setFont(randomFont());  //设置随机字体
            g2.setColor(randomColor()); //设置随机颜色
            g2.drawString(s, x, h-5);  //画图
        }
        this.text = sb.toString();  //把生成的字符串赋给this.text
        drawLine(image); //添加干扰线
        return image;
    }

    //返回验证码图片上的文本
    public String getText () {
        return text;
    }

    //保存图片到指定的输出流
    public static void output (BufferedImage image, OutputStream out)
            throws IOException {
        ImageIO.write(image, "JPEG", out);
    }
}

 第二步,写个请求接口,用以获取验证码

/**
 * 验证码的controller
 */
@RestController
public class VerifyCodeController {
    /**
     * 获取验证码
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping("/verifyCode/getVerifyCode")
    public void getVerifyCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        VerifyCode verifyCode = new VerifyCode();
        //生成图片
        BufferedImage image = verifyCode.getImage();
        //获取图片上的文本
        String text = verifyCode.getText();
        //把验证码的文本保存到session中,用以校验前端输入的验证码是否正确
        request.getSession().setAttribute("VERIFY_CODE", text);
        
        System.out.println("request.getSession().getAttribute(\"VERIFY_CODE\") = " + request.getSession().getAttribute("VERIFY_CODE"));

        //把生成的图片验证码响应给前端 客户端
        VerifyCode.output(image, response.getOutputStream());
    }
}

 这里的代码逻辑就是:调用方法生成验证码,然后保存在 session 中,并通过流响应给前端。有的小伙伴不懂了,为什么要保存在 session 中呢,那是因为需要和前端传递过来的验证码进行比较。

来看结果:

SpringSecurity+Vue:实现添加登录图片验证码_第1张图片

此时验证码已经成功返回了。

接下来需要写一个验证码的过滤器,来验证验证码是否正确:

/**
 * 验证码的过滤器
 */
@Component//注入到Spring容器中
public class VerificationCodeFilter extends GenericFilter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        /**
         * 强转为 ServletRequest和ServletResponse的子接口:
         *      HttpServletRequest和HttpServletResponse
         */
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;


        //如果是post请求,并且路径是 login 再进行校验,否则放行
        if ("POST".equals(req.getMethod()) && "/login".equals(req.getServletPath())) {
            System.out.println("VerificationCodeFilter-VERIFY_CODE = " + req.getSession().getAttribute("VERIFY_CODE"));
            //获取 session 中的验证码字符串
            String verifyCode = (String) req.getSession().getAttribute("VERIFY_CODE");
            //获取客户端传递过来的验证码
            String vc = req.getParameter("verifyCode");
            /**
             * 如果 客户端传递过来的验证码
             *   为"" 为 null 和session的值不一致 视为 验证码错误
             */
            if (vc == null ||"".equals(vc) ||  !vc.toLowerCase().equals(verifyCode.toLowerCase())) {
                //设置响应头 要不然会出现乱码情况
                resp.setHeader("Content-Type", "text/html;charset=utf-8");
                

                //要往前端返回的数据/抛出异常
            } else {//验证码正确
                //让过滤器往下执行
                chain.doFilter(request, response);
            }
        } else {
            //让过滤器往下执行
            chain.doFilter(request, response);
        }
    }
}

上面代码的逻辑:如果请求路径是 `/login` 并且请求方式是 `POST` 的话,再去校验,然后判断前端传递过来的验证码 是否为空、是否为null、验证码是否正确。条件满足其一就返回错误信息。

接下来就是 SpringSecurity 的配置类了:

/**
 * SpringSecurity 的配置类
 */
@Configuration
@EnableWebSecurity//开启web安全
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private VerificationCodeFilter verificationCodeFilter;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 在 UsernamePasswordAuthenticationFilter 执行前,执行校验验证码的逻辑
         * 如果验证码输入错误,那就不用再继续接下来的流程了
         */

        //所有请求都需要登录校验
        http.authorizeRequests()
                /**
                 * 注意代码中配置的三条规则的顺序非常重要,和 Shiro 类似,Spring Security
                 * 在匹配的时候也是按照从上往下的顺序来匹配,一旦匹配到了就不继续匹配了,
                 * 「所以拦截规则的顺序不能写错」。
                 */
                .antMatchers("/login", "/verifyCode/getVerifyCode").permitAll()
                //其余所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                 //在 UsernamePasswordAuthenticationFilter 过滤器前添加
                .addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class);

        //禁用csrf
        http.csrf().disable();
        //允许跨域
        http.cors();
    }
}

接下来测试,登录就必须传验证码了,如果验证码不传或者传错,就会报错:

SpringSecurity+Vue:实现添加登录图片验证码_第2张图片

登录成功的情况:

SpringSecurity+Vue:实现添加登录图片验证码_第3张图片

 现在就到前端 Vue了:

    
        
        
      

vcURL:

//验证码的请求地址
vcURL: 'http://ip:port/path?time=' + new Date().getTime(),

点图片更换验证码的方法:

      //更换验证码  利用了只要变量的值更改,标签的值就会更改的特点,实现了更换验证码
      updateVcURL() {
        // console.info("new Date()", new Date().getTime());
        this.vcURL = "http://ip:port/path?time=" + new Date().getTime();
      }

使用获取当前时间戳的方式实现。只要变量的值更改,它就会重新发送一个请求请求验证码

你可能感兴趣的:(vue.js,前端,javascript)