Skip to content

Commit

Permalink
验证码登录
Browse files Browse the repository at this point in the history
  • Loading branch information
tangllty committed Nov 2, 2023
1 parent 78d89a2 commit 9fb9ffa
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.tang.admin.controller;

import java.util.Base64;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.tang.commons.constants.CachePrefix;
import com.tang.commons.model.CaptchaModel;
import com.tang.commons.utils.AjaxResult;
import com.tang.commons.utils.RedisUtils;
import com.tang.commons.utils.captcha.CaptchaType;
import com.tang.commons.utils.captcha.CaptchaUtils;

/**
* 验证码逻辑控制层
*
* @author Tang
*/
@RestController
@RequestMapping("/captcha")
public class CaptchaController {

private final RedisUtils redisUtils;

public CaptchaController(RedisUtils redisUtils) {
this.redisUtils = redisUtils;
}

@GetMapping
public AjaxResult getCaptcha() {
var captcha = CaptchaUtils.generate(CaptchaType.NUMBER);
var base64 = Base64.getEncoder().encodeToString(captcha.getImage());
redisUtils.set(CachePrefix.CAPTCHA + captcha.getId(), captcha.getText(), 60);
return AjaxResult.success(new CaptchaModel(captcha.getId(), base64));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ private CachePrefix() {
*/
public static final String DICT_TYPE = SYSTEM + "dict_type:";

/**
* 验证码缓存前缀
*/
public static final String CAPTCHA = SYSTEM + "captcha:";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.tang.commons.exception;

/**
* 验证码异常
*
* @author Tang
*/
public class CaptchaException extends RuntimeException {

@java.io.Serial
private static final long serialVersionUID = 7891001344481580446L;

public CaptchaException() {
}

public CaptchaException(String message) {
super(message);
}

public CaptchaException(Throwable cause) {
super(cause);
}

public CaptchaException(String message, Throwable cause) {
super(message, cause);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.tang.commons.model;

import java.io.Serializable;

/**
* 验证码模型
*
* @author Tang
*/
public class CaptchaModel implements Serializable {

@java.io.Serial
private static final long serialVersionUID = 2521646489620204864L;

/**
* 验证码 ID
*/
private Long id;

/**
* 验证码图片
*/
private String text;

public CaptchaModel() {
}

public CaptchaModel(Long id, String text) {
this.id = id;
this.text = text;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
public class LoginModel implements Serializable {

@java.io.Serial
private static final long serialVersionUID = -5428882047565357006L;
private static final long serialVersionUID = -5942371013228306925L;

/**
* 用户名
Expand All @@ -42,6 +42,11 @@ public class LoginModel implements Serializable {
@NotBlank(message = "登陆方式不能为空")
private String loginType;

/**
* 验证码
*/
private transient CaptchaModel captcha;


public String getUsername() {
return username;
Expand Down Expand Up @@ -75,4 +80,12 @@ public void setLoginType(String loginType) {
this.loginType = loginType;
}

public CaptchaModel getCaptcha() {
return captcha;
}

public void setCaptcha(CaptchaModel captcha) {
this.captcha = captcha;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.tang.commons.utils.captcha;

/**
* 验证码模型
*
* @author Tang
*/
public class Captcha {

/**
* 验证码 ID
*/
private Long id;

/**
* 答案
*/
private String text;

/**
* 图片
*/
private byte[] image;

public Captcha() {
}

public Captcha(Long id, String text, byte[] image) {
this.id = id;
this.text = text;
this.image = image;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}

public byte[] getImage() {
return image;
}

public void setImage(byte[] image) {
this.image = image;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.tang.commons.utils.captcha;

/**
* @author Tang
*/
public enum CaptchaType {

/**
* 数字
*/
NUMBER,

/**
* 字母(大小写敏感)
*/
LETTER,

/**
* 字母(大小写不敏感)
*/
LETTER_IGNORE_CASE,

/**
* 混合(大小写不敏感)
*/
MIXED

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.tang.commons.utils.captcha;

import java.awt.Color;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;

import org.slf4j.Logger;

import com.tang.commons.utils.LogUtils;
import com.tang.commons.utils.id.IdUtils;

/**
* 验证码工具类
*
* @author Tang
*/
public class CaptchaUtils {

private CaptchaUtils() {
}

private static final Logger LOGGER = LogUtils.getLogger();

/**
* 验证码图片宽度
*/
private static final int WIDTH = 130;

/**
* 验证码图片高度
*/
private static final int HEIGHT = 48;

/**
* 随机数
*/
private static final Random RANDOM = new Random();

/**
* 验证码图片
*/
private static final BufferedImage BUFFERED_IMAGE = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

/**
* 生成验证码
*
* @param type 验证码类型
* @return 验证码
*/
public static Captcha generate(CaptchaType type) {
final char[] characters = switch (type) {
case NUMBER -> "0123456789".toCharArray();
case LETTER -> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
case LETTER_IGNORE_CASE -> new char[0];
case MIXED -> new char[0];
};

var graphics = BUFFERED_IMAGE.getGraphics();
// 填充背景色
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, WIDTH, HEIGHT);
// 绘制边框
graphics.setColor(Color.BLACK);
graphics.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);
// 绘制干扰线
graphics.setColor(Color.BLACK);
for (int i = 0; i < 8; i++) {
graphics.drawLine(RANDOM.nextInt(WIDTH), RANDOM.nextInt(HEIGHT), RANDOM.nextInt(WIDTH), RANDOM.nextInt(HEIGHT));
}
// 绘制干扰点
graphics.setColor(Color.BLACK);
for (int i = 0; i < 6; i++) {
graphics.drawOval(RANDOM.nextInt(WIDTH), RANDOM.nextInt(HEIGHT), RANDOM.nextInt(WIDTH / 2), RANDOM.nextInt(HEIGHT / 2));
}
var captcha = new StringBuilder();
// 绘制验证码
for (int i = 0; i < 4; i++) {
var character = characters[RANDOM.nextInt(characters.length)];
captcha.append(character);
graphics.setColor(new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255)));
graphics.setFont(new Font("Arial", Font.BOLD, 30));
graphics.drawString(String.valueOf(character), 20 + i * 30, 35);
}
graphics.dispose();

return new Captcha(IdUtils.snowflake(), captcha.toString(), toByteArray());
}

/**
* 将验证码图片转换为字节数组
*
* @return 字节数组
*/
private static byte[] toByteArray() {
try (var byteArrayOutputStream = new ByteArrayOutputStream()) {
ImageIO.write(BUFFERED_IMAGE, "png", byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
LOGGER.error("生成验证码失败", e);
return new byte[0];
} finally {
BUFFERED_IMAGE.flush();
}
}

}
Loading

0 comments on commit 9fb9ffa

Please sign in to comment.