最近做项目时使用到了通过验证码图片code参数实现登录验证的技术,这里记录一下,以供参考

效果图

登录界面

验证码错误界面

点击刷新验证码

后端接口编写

验证码工具类

先定义一个工具类,用于生成验证码图片


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 阿楠
 * code生成工具类
 */
public class CodeUtils {
    /**
     * 生成验证码图片的宽度
     */
    private int width = 100;

    /**
     * 生成验证码图片的高度
     */
    private int height = 30;

    /**
     * 字符样式
     */
    private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };

    /**
     * 定义验证码图片的背景颜色为白色
     */
    private Color bgColor = new Color(255, 255, 255);

    /**
     * 生成随机
     */
    private Random random = new Random();

    /**
     * 定义code字符
     */
    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);
    }
}

Controller定义请求

在前端调用可生成验证码图片,同时将验证码的code存在session中,用于登录判断时的验证

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

登录验证

登录接口这里同时也使用了token,仅使用code验证可以直接看注释
//判断code是否正确,之后的代码,使用if判断code错误时的输出,当session里没有code或者前端传递来的code参数跟后端session里的code参数不一致的时候,可判断验证码错误
注意:①||后面的!表示非②toUpperCase()用于不区分字符大小写

	/**
     * 登录实现token,并验证code
     * @param param
     * @param request
     * @return
     */
    @PostMapping(value = "/login")
    public Object login(@RequestBody Map<String,String> param,
                      HttpServletRequest request,
                      HttpServletResponse response){
        JSONObject jsonObject = new JSONObject();
        String root_name = param.get("root_name");
        String root_password = param.get("root_password");
        Root root = rootService.checkRoot(root_name,root_password);
        //判断code是否正确
        if(request.getSession(true)
                .getAttribute("code")==null
                ||
                !param.get("code").toUpperCase()
                .equals(request.getSession(true)
                        .getAttribute("code")
                        .toString().toUpperCase())){
            jsonObject.put("message", "code error!");
            return jsonObject;
        }
        //登录判断
        else if (root == null){
            jsonObject.put("message", "login error!");
        }else {
            //登陆成功利用session存储账号密码
            HttpSession session =request.getSession(true);
            session.setAttribute("root",root);
            String token = rootService.getToken(root);
            jsonObject.put("message", "login success!");
            jsonObject.put("token", token);
            jsonObject.put("root_img", root.getRoot_img());

            Cookie cookie = new Cookie("token", token);
            cookie.setPath("/");
            response.addCookie(cookie);
        }
        return jsonObject;
    }

前端

配置

注意因为是前后端分离开发,每次前端的ajax请求都是新的,每次请求的sessionID都不一样,所以每次使用的session不是同一个,以至于登录时session里面的值为null。
需要在vue项目的main.js中配置axios.defaults.withCredentials为true,使得每次请求都带上浏览器的cookie,这样后端使用的session就是同一个了。

axios.defaults.withCredentials = true;

图片请求及点击刷新

在登录框中定义验证码图片

<el-form-item prop="code">
    <span><i class="iconfont icon-icon_anquan" style="font-size: 25px"></i></span>
    <el-input type="text" auto-complete="false" 
    		  v-model="loginForm.code" placeholder="点击图片更换验证码"
              style="width: 60%;margin-left: 10px"
              @keyup.enter.native="submitLogin('loginForm')"></el-input>
    <el-image class="codeImg" :src="imgUrl" @click="resetImg"></el-image>
</el-form-item>

这里定义一个验证码图片样式

.codeImg {
        margin-top: 5px;
        float: right;
    }

在data参数中定义对应的imgUrl,new Date()生成随机事件用于更换验证码
在提交表单里面定义一个code参数用于传参

imgUrl:'http://localhost:8181/root/code?time='+new Date(),
loginForm: {
    root_name: 'anan',
    root_password: '123456',
    code:''
},

定义点击更换验证码图片的方法resetImg(),因为imgUrl本身就是随机,所以这里直接重新随机一个imgUrl实现验证码图片的刷新

resetImg(){
    this.imgUrl = "http://localhost:8181/root/code?time="+new Date();
}

这里可以稍微的测试一下生成图片中的code参数

在这里插入图片描述

刷新一下登录页面,查看生成的code参数,生成成功,我们就可以接下去做登录验证啦!

在这里插入图片描述
在这里插入图片描述

请求登录验证

这里附上提交登录表单的接口请求,传递code参数给后端验证实现登录
如果res.data.message为code error!,表示code验证错误,扔出message提示验证码错误,并重置imgUrl刷新验证码图片

_this.axios.post('login后端接口',
                 {root_name: this.loginForm.root_name,
                  root_password: this.loginForm.root_password,
                  code: this.loginForm.code},
                  {'Content-Type': 'application/json; charset=utf-8'}
                 ).then(res => {
             console.log(res);
             if (res.data.message === 'code error!'){
                 _this.$message.error('验证码错误!');
                 _this.imgUrl=
                 "http://localhost:8181/root/code?time="
                 +new Date();}
             else if (res.data.message === 'login error!'){
                       _this.$message
                       .error('用户名或密码错误,请重新登录!');}
             else {
                   //存session
                   sessionStorage.setItem('root_name', _this.loginForm.root_name);
                   sessionStorage.setItem('root_password', _this.loginForm.root_password);
                   sessionStorage.setItem('root_img', res.data.root_img);
				   sessionStorage.setItem("root_token",res.data.token);

                   _this.$message({
                       message: '登录成功!',
                       type: 'success',
                   });
                   _this.$router.push({path:'/home'});}
            }
Logo

前往低代码交流专区

更多推荐