Files
EasyTier/easytier-web/src/restful/captcha/base/captcha.rs
T
2026-04-10 00:22:12 +08:00

310 lines
8.8 KiB
Rust

use super::super::base::randoms::Randoms;
use super::super::utils::color::Color;
use super::super::utils::font;
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use rusttype::Font;
use std::fmt::Debug;
use std::io::Write;
use std::sync::Arc;
/// 验证码抽象类
pub(crate) struct Captcha {
/// 随机数工具类
pub(crate) randoms: Randoms,
/// 常用颜色
color: Vec<Color>,
/// 字体名称
font_names: [&'static str; 1],
/// 验证码的字体
font_name: String,
/// 验证码的字体大小
font_size: f32,
/// 验证码随机字符长度
pub len: usize,
/// 验证码显示宽度
pub width: i32,
/// 验证码显示高度
pub height: i32,
/// 验证码类型
char_type: CaptchaType,
/// 当前验证码
pub(crate) chars: Option<String>,
}
/// 验证码文本类型 The character type of the captcha
pub enum CaptchaType {
/// 字母数字混合
Default = 1,
/// 纯数字
OnlyNumber,
/// 纯字母
OnlyChar,
/// 纯大写字母
OnlyUpper,
/// 纯小写字母
OnlyLower,
/// 数字大写字母
NumAndUpper,
}
/// 内置字体 Fonts shipped with the library
pub enum CaptchaFont {
/// actionj
Font1,
/// epilog
Font2,
/// fresnel
Font3,
/// headache
Font4,
/// lexo
Font5,
/// prefix
Font6,
/// progbot
Font7,
/// ransom
Font8,
/// robot
Font9,
/// scandal
Font10,
}
impl Captcha {
/// 生成随机验证码
pub fn alphas(&mut self) -> Vec<char> {
let mut cs = vec!['\0'; self.len];
for cs_i in cs.iter_mut() {
match self.char_type {
CaptchaType::Default => *cs_i = self.randoms.alpha(),
CaptchaType::OnlyNumber => {
*cs_i = self.randoms.alpha_under(self.randoms.num_max_index)
}
CaptchaType::OnlyChar => {
*cs_i = self
.randoms
.alpha_between(self.randoms.char_min_index, self.randoms.char_max_index)
}
CaptchaType::OnlyUpper => {
*cs_i = self
.randoms
.alpha_between(self.randoms.upper_min_index, self.randoms.upper_max_index)
}
CaptchaType::OnlyLower => {
*cs_i = self
.randoms
.alpha_between(self.randoms.lower_min_index, self.randoms.lower_max_index)
}
CaptchaType::NumAndUpper => {
*cs_i = self.randoms.alpha_under(self.randoms.upper_max_index)
}
}
}
self.chars = Some(cs.iter().collect());
cs
}
/// 获取当前的验证码
pub fn text(&mut self) -> String {
self.check_alpha();
self.chars.clone().unwrap()
}
/// 获取当前验证码的字符数组
pub fn text_char(&mut self) -> Vec<char> {
self.check_alpha();
self.chars.clone().unwrap().chars().collect()
}
/// 检查验证码是否生成,没有则立即生成
pub fn check_alpha(&mut self) {
if self.chars.is_none() {
self.alphas();
}
}
pub fn get_font(&'_ mut self) -> Arc<Font<'_>> {
if let Some(font) = font::get_font(&self.font_name) {
font
} else {
font::get_font(self.font_names[0]).unwrap()
}
}
pub fn get_font_size(&mut self) -> f32 {
self.font_size
}
pub fn set_font_by_enum(&mut self, font: CaptchaFont, size: Option<f32>) {
let font_name = self.font_names[font as usize];
self.font_name = font_name.into();
self.font_size = size.unwrap_or(32.);
}
}
/// 初始化验证码的抽象方法 Traits for initialize a Captcha instance.
pub trait NewCaptcha
where
Self: Sized,
{
/// 用默认参数初始化
///
/// Initialize the Captcha with the default properties.
fn new() -> Self;
/// 使用输出图像大小初始化
///
/// Initialize the Captcha with the size of output image.
fn with_size(width: i32, height: i32) -> Self;
/// 使用输出图像大小和验证码字符长度初始化
///
/// Initialize the Captcha with the size of output image and the character length of the Captcha.
///
/// <br/>
///
/// 特别地/In particular:
///
/// - 对算术验证码[ArithmeticCaptcha](crate::captcha::arithmetic::ArithmeticCaptcha)而言,这里的`len`是验证码中数字的数量。
///
/// For [ArithmeticCaptcha](crate::captcha::arithmetic::ArithmeticCaptcha), the `len` presents the count of the digits
/// in the Captcha.
fn with_size_and_len(width: i32, height: i32, len: usize) -> Self;
/// 使用完整的参数来初始化,包括输出图像大小、验证码字符长度和输出字体及其大小
///
/// Initialize the Captcha with full properties, including the size of output image, the character length of the Captcha,
/// and the font used in Captcha with the font size.
///
/// 关于`len`字段的注意事项,请参见[with_size_and_len](Self::with_size_and_len)中的说明。Refer to the document of
/// [with_size_and_len](Self::with_size_and_len) for the precautions of the `len` property.
fn with_all(width: i32, height: i32, len: usize, font: CaptchaFont, font_size: f32) -> Self;
}
impl NewCaptcha for Captcha {
fn new() -> Self {
let color = [
(0, 135, 255),
(51, 153, 51),
(255, 102, 102),
(255, 153, 0),
(153, 102, 0),
(153, 102, 153),
(51, 153, 153),
(102, 102, 255),
(0, 102, 204),
(204, 51, 51),
(0, 153, 204),
(0, 51, 102),
]
.iter()
.map(|v| (*v).into())
.collect();
let font_names = ["robot.ttf"];
let font_name = font_names[0].into();
let font_size = 32.;
let len = 5;
let width = 130;
let height = 48;
let char_type = CaptchaType::Default;
let chars = None;
Self {
randoms: Randoms::new(),
color,
font_names,
font_name,
font_size,
len,
width,
height,
char_type,
chars,
}
}
fn with_size(width: i32, height: i32) -> Self {
let mut _self = Self::new();
_self.width = width;
_self.height = height;
_self
}
fn with_size_and_len(width: i32, height: i32, len: usize) -> Self {
let mut _self = Self::new();
_self.width = width;
_self.height = height;
_self.len = len;
_self
}
fn with_all(width: i32, height: i32, len: usize, font: CaptchaFont, font_size: f32) -> Self {
let mut _self = Self::new();
_self.width = width;
_self.height = height;
_self.len = len;
_self.set_font_by_enum(font, None);
_self.font_size = font_size;
_self
}
}
/// 验证码的抽象方法 Traits which a Captcha must implements.
pub trait AbstractCaptcha: NewCaptcha {
/// 错误类型
type Error: std::error::Error + Debug + Send + Sync + 'static;
/// 输出验证码到指定位置
///
/// Write the Captcha image to the specified place.
fn out(&mut self, out: impl Write) -> Result<(), Self::Error>;
/// 获取验证码中的字符(即正确答案)
///
/// Get the characters (i.e. the correct answer) of the Captcha
fn get_chars(&mut self) -> Vec<char>;
/// 输出Base64编码。注意,返回值会带编码头(例如`data:image/png;base64,`),可以直接在浏览器中显示;如不需要编码头,
/// 请使用[base64_with_head](Self::base64_with_head)方法并传入空参数以去除编码头。
///
/// Get the Base64 encoded image. Reminds: the returned Base64 strings will begin with an encoding head like
/// `data:image/png;base64,`, which make it possible to display in browsers directly. If you don't need it, you may
/// use [base64_with_head](Self::base64_with_head) and pass a null string.
fn base64(&mut self) -> Result<String, Self::Error>;
/// 获取验证码的MIME类型
///
/// Get the MIME Content type of the Captcha.
fn get_content_type(&mut self) -> String;
/// 输出Base64编码(指定编码头)
///
/// Get the Base64 encoded image, with specified encoding head.
fn base64_with_head(&mut self, head: &str) -> Result<String, Self::Error> {
let mut output_stream = Vec::new();
self.out(&mut output_stream)?;
Ok(String::from(head) + &BASE64_STANDARD.encode(&output_stream))
}
}