Appearance
图形验证码
我来介绍使用laravel11与 react 和扩展包 mews/captcha 实现网站的图形验证码。如果你使用其他语言,也可以帮助你提供思路。
后端
首先访问 mews/captcha 安装并配置扩展包
composer require mews/captcha
然后执行命令生成配置文件
php artisan vendor:publish
创建控制器 CaptchaController.php 用来生成验证码图像,对!是blob图像不是链接。
为什么生成 图像数据呢?因为在前后端分离时,如果使用url,前端vite使用代理 和使用 url 时,是不同的域,会造成 session 会话是不同的,结果是验证码验证失败。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Mews\Captcha\Facades\Captcha;
use Symfony\Component\Mime\Encoder\Base64Encoder;
//图形验证码
class CaptchaController extends Controller
{
public function __invoke()
{
//生成数学计算验证码
return Captcha::create('math');
}
}
前端
下面的前端环境是 vite、react ,我们首先配置 vite的代理
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from 'path'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), TanStackRouterVite(),],
resolve: {
alias: { '@': path.resolve(__dirname, 'src') },
},
server: {
proxy: {
'/api': {
target: 'http://php.test',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
下面创建 axios 的请求接口,使用的是 tanstack query 库,来获取后端的验证码图形数据
import { useAxios } from "@/hooks/useAxios";
import { useSuspenseQuery } from "@tanstack/react-query";
//图像验证码
export const useGetCaptcha = () => {
const { axiosInstance } = useAxios()
return useSuspenseQuery({
queryKey: ['captcha'],
queryFn: async () => {
const data = (await axiosInstance.get<Blob>('/captcha_img', {
responseType: 'blob'
})).data
return URL.createObjectURL(data)
}
})
}
接下来开发图形验证码组件,以下代码是我写的项目的代码,包括以下内容
- 表单验证处理
- 获取与显示图形验证码
import { useGetCaptcha } from '@/services/captcha'
import { useValidateStore } from '@/store/useValidateStore'
import { useQueryClient } from '@tanstack/react-query'
import classNames from 'classnames'
import { Input } from '../ui/input'
import { FieldValidateError } from './FieldValidateError'
import { IFieldComponent } from './types'
export function FieldImageCaptcha({ form, name, className, ...props }: IFieldComponent) {
const removeError = useValidateStore((s) => s.removeError)
//获取验证码图像数据 blob
const { data: captcha } = useGetCaptcha()
const queryClient = useQueryClient()
return (
<div className={classNames(className)}>
<form.Field
name={name}
children={(field) => (
<div>
<div className='flex items-stretch gap-2'>
<div className='flex-1'>
<Input
{...props}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
onFocus={() => removeError(name)}
/>
</div>
<div className='overflow-hidden border rounded-lg cursor-pointer '>
<img
src={captcha}
onClick={async () => {
await queryClient.invalidateQueries({ queryKey: ['captcha'] })
}}
/>
</div>
</div>
<FieldValidateError errors={field.state.meta.errors} name={name} />
</div>
)}
/>
</div>
)
}
其实你只需要关注以下两行就行。
//根据上面的接口获取验证码图像数据
const { data: captcha } = useGetCaptcha()
...
//显示验证码图像
<img
src={captcha}
onClick={async () => {
await queryClient.invalidateQueries({ queryKey: ['captcha'] })
}}
/>