使用简单运算进行登录验证

需求:使用简单的加减乘除运算进行后端系统的登录验证。

效果:

为避免数字 0 出现在除法运算的除数的位置,我们仅支持加减乘三种运算,由于是后台内部人员使用的系统,所以没做太难的处理。

具体的代码实现和上一篇文章基本相同 图片验证码_jiaomubai的博客-CSDN博客

同样提供两个接口:

1.获取验证码接口:

import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@Slf4j
public class CaptchaUtils {

    private static int width = 75;
    private static int height = 35;

    private static char[] ch = "0123456789".toCharArray();
    private static char[] sf = "+-x".toCharArray();


    public static Map getCaptcha() throws Exception {
        // 在内存中创建图象
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);

        // 获取图形上下文
        Graphics g = image.getGraphics();

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

        // 设定背景色
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);

        // 设定字体
        g.setFont(new Font("Times New Roman", Font.PLAIN, 18));

        // 画边框
        // g.setColor(new Color());
        // g.drawRect(0,0,width-1,height-1);

        // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
        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);
        }

        // 取随机产生的认证码(4位数字)
        StringBuffer sb = new StringBuffer();
        int index, len = ch.length;
        index = random.nextInt(len);
        g.setColor(new Color(random.nextInt(88), random.nextInt(188),
                random.nextInt(255)));
        g.setFont(new Font("Arial", Font.ITALIC, 20));// 输出的字体和大小
        g.drawString("" + ch[index], (0 * 15), 26);// 写什么数字,在图片的什么位置画
        sb.append(ch[index]);

        int index1, len1 = sf.length;
        index1 = random.nextInt(len1);
        g.setColor(new Color(random.nextInt(88), random.nextInt(188),
                random.nextInt(255)));
        g.setFont(new Font("Arial", Font.ITALIC, 20));// 输出的字体和大小
        g.drawString("" + sf[index1], (1 * 15), 26);// 写什么数字,在图片的什么位置画
        sb.append(sf[index1]);


        int index2, len2 = ch.length;
        index2 = random.nextInt(len2);
        g.setColor(new Color(random.nextInt(88), random.nextInt(188),
                random.nextInt(255)));
        g.setFont(new Font("Arial", Font.ITALIC, 20));// 输出的字体和大小

        if(String.valueOf(sf[index1]).equals("-")){
            if(Integer.parseInt(String.valueOf(ch[index])) < Integer.parseInt(String.valueOf(ch[index2]))){
                index2 = index;
            }
        }

        g.drawString("" + ch[index2], (2 * 15) , 26);// 写什么数字,在图片的什么位置画
        sb.append(ch[index2]);


        g.setColor(new Color(random.nextInt(88), random.nextInt(188),
                random.nextInt(255)));
        g.setFont(new Font("Arial", Font.ITALIC, 20));// 输出的字体和大小
        g.drawString("=", (3 * 15) , 26);// 写什么数字,在图片的什么位置画
        sb.append("=");

        g.setColor(new Color(random.nextInt(88), random.nextInt(188),
                random.nextInt(255)));
        g.setFont(new Font("Arial", Font.ITALIC, 20));// 输出的字体和大小
        g.drawString("?", (4 * 15) , 26);// 写什么数字,在图片的什么位置画
        sb.append("?");

        g.drawLine(5, 3, 60, 35);
        Map<String, Object> result = new HashMap<>();
        int jg = 0;
        if(String.valueOf(sf[index1]).equals("+")){
            jg = Integer.parseInt(String.valueOf(ch[index])) + Integer.parseInt(String.valueOf(ch[index2]));
        }
        if(String.valueOf(sf[index1]).equals("-")){
            jg = Integer.parseInt(String.valueOf(ch[index])) - Integer.parseInt(String.valueOf(ch[index2]));
        }
        if(String.valueOf(sf[index1]).equals("x")){
            jg = Integer.parseInt(String.valueOf(ch[index])) * Integer.parseInt(String.valueOf(ch[index2]));
        }

        // 图象生效
        g.dispose();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        String base64 = "";
        try {
            // 输出图象到页面
            ImageIO.write(image, "JPEG", stream);
            BASE64Encoder encoder = new BASE64Encoder();
            base64 = encoder.encodeBuffer(stream.toByteArray());
            base64 = base64.replaceAll("\n", "").replaceAll("\r", "");
            result.put("result", jg);
            result.put("image", "data:image/png;base64," + base64);
        } catch (IOException e) {
            log.info("生成图片验证码异常", e);
            throw e;
        } finally {
            if (stream != null) {
                stream.flush();
                stream.close();
            }
        }
        return result;
    }

    public static 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);
    }
    
}

以上工具类来源于网络,具体的实现没做详细研究。下面是正式的获取验证码接口:

@RequestMapping("/getCode")
public ResultVO getCode(HttpServletResponse response) {
    Map<String, String> result = new HashMap<>();
    try {
        // 获取图片
        Map<String, Object> imageMap = CaptchaUtils.getCaptcha();
        response.setContentType("image/jpeg");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        int imageResult = (int)imageMap.get("result");
        logger.info("图片验正码计算结果为:{}", imageResult);
        String codeKey = UUID.randomUUID().toString();
        redisSupport.set(codeKey, imageResult);
        result.put("code",(String) imageMap.get("image"));
        result.put("codeKey", codeKey);
    } catch (Exception e) {
        logger.error("生成图片验正码异常", e);
        return new ResultVO<>(ResultVO.FAIL, "获取图片验正码失败");
    }
    return new ResultVO<>(result, ResultVO.SUCCESS, "获取图片验正码成功");
}

其中使用到的 CaptchaUtils 工具就是第一部分展示的工具类。其中使用的 redisSupport 可根据实际情况进行替换,其实就是 Redis 的一个工具包,后续会单独贴出来。 Redis 工具包请参考文章:Redis 工具实用篇_jiaomubai的博客-CSDN博客

2.登录接口

@PostMapping("/login")
public void login(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("code") Integer code, @RequestParam("codeKey") String codeKey, HttpServletResponse resp, HttpServletRequest request) {
    JSONObject result = new JSONObject();
    try {
        Integer redisCode = (Integer) redisSupport.get(codeKey);
        log.info("redisCode:{}",redisCode);
        if(ObjectUtils.isEmpty(code)  || ObjectUtils.isEmpty(redisCode) || !code.equals(redisCode)){
            result.put("error_description", "验证结果错误,请重新输入");
            result.put("error", "invalid_grant");
            resp.setStatus(400);
            redisSupport.del(codeKey);
            return;
        }
    } catch (Exception e) {
        log.error("登录接口错误,错误信息:{}", e);
    } finally {
        try {
            resp.setContentType(ContentType.JSON.getValue());
            ServletOutputStream out = resp.getOutputStream();
            OutputStreamWriter ow = new OutputStreamWriter(out, "UTF-8");
            ow.write(result.toJSONString());
            ow.flush();
            ow.close();
        } catch (Exception e) {
            log.error("登录接口错误,错误信息:{}", e);
        }
    }
}

其中入参中 username 为登录的用户名,password 为登录密码,code 为用户输入的图片验证码的答案,codekey 为与 code 关联的 key,因为在获取验证码的接口中(/getCode)我们已经将 code 和 codekey 存储在了 Redis 中,所以此处可直接根据 codekey 去获取 Redis 中的与 codekey 对应的验证码,之后与用户输入的 code 进行比较即可。其中使用的 redisSupport 可根据实际情况进行替换,其实就是 Redis 的一个工具包,下一篇文章会单独贴出来。 Redis 工具包请参考文章:Redis 工具实用篇_jiaomubai的博客-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值