Go使用base64captcha生成验证码并保存到redis,以及一些其它的逻辑。
使用了go-logger库作为日志采集工具,需要go版本 >= 1.22.4,推荐使用g管理go版本工具
前端代码使用vite脚手架简单生成Vue3+ElementPlus的小demo
http.js---->封装Axios
import axios from 'axios';
const instance = axios.create({
baseURL: 'http://192.168.40.182:8001', // 替换为你的API基础URL
timeout: 1000, // 请求超时时间
headers: {
'Content-Type': 'application/json', // 根据需要设置内容类型
}
});
export default instance;
router.js---->vue-router 声明路由
import { createWebHashHistory, createRouter } from 'vue-router'
import Login from './components/Login.vue'
import Home from './components/Home.vue'
import Index from './components/Index.vue'
const routes = [
{ path: '/', component: Index },
{ path: '/login', component: Login },
{ path: '/home', component: Home }
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router
vite-config.js---->自动导入UI插件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
server: {
host: '0.0.0.0',
port: '8000'
}
})
main.js---->将路由挂载App实例
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
Login.vue---->登陆组件
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import instance from '../http'
import { useRouter } from 'vue-router'
const ruleFormRef = ref<FormInstance>()
const isTimeDisable = ref(false)
const ruleForm = reactive({
username: '',
password: '',
code: '',
})
const router = useRouter() // 获取路由实例
// 用于存储验证码的Base64数据
const captchaImage = ref('')
// 验证码倒计时
let codeTime = ref(60)
// 验证码的内容
const codeContent = ref('点击获取验证码')
const CheckUser = (rule: any, value: any, callback: any) => {
if (ruleForm.username === '') {
callback(new Error('用户名不能为空'))
}
callback()
}
const CheckPass = (rule: any, value: any, callback: any) => {
if (ruleForm.password === '') {
callback(new Error('密码不能为空'))
}
callback()
}
const CheckCode = (rule: any, value: any, callback: any) => {
if (ruleForm.code === '') {
callback(new Error('验证码不能为空'))
}
callback()
}
// 前端规则验证
const rules = reactive({
username: [{ validator: CheckUser, trigger: 'blur' }],
password: [{ validator: CheckPass, trigger: 'blur' }],
code: [{ validator: CheckCode, trigger: 'blur' }],
})
// 重置输入
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
// 提交数据表单到HOME
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate(async (valid) => {
if (valid) {
// 表单填写合法,发送请求到后端
try{
const response = await instance.post('/login',ruleForm)
ElMessage.success({message: response.data.msg})
localStorage.setItem('accessToken',response.data.accessToken)
setTimeout(() => {
// 跳转至Home
router.push({ path: '/home', query: { access: response.data.accessToken } })
}, 1000);
}catch(err){
console.log(err.response.data.msg)
ElMessage.error(err.response.data.msg)
}
} else {
alert('数据不合法')
}
})
}
// 获取验证码
const refreshCaptcha = async () => {
if (ruleForm.username == "") {
alert("请先输入用户名")
return
}
// 根据需要修改
const requestData = {username:ruleForm.username}
// 验证码倒计时
const timer = setInterval(() => {
if (codeTime.value > 0) {
isTimeDisable.value = true
codeContent.value = `验证码于(${codeTime.value}s)后过期`;
codeTime.value --
}else {
clearInterval(timer);
isTimeDisable.value = false
codeContent.value = "重新获取验证码"
codeTime.value = 60
}
},1000)
try {
const response = await instance.get('/code', requestData);
// 后端返回的验证码是以 Base64 形式存储在data.captcha字段中
captchaImage.value = response.data.captchaImg;
} catch (error) {
alert(error); // 捕获并打印错误
}
}
</script>
<template>
<el-card style="width: 440px;height: 350px;">
<template #header>
<div class="card-header">
<span >登陆验证功能</span>
</div>
</template #body>
<el-form
ref="ruleFormRef"
:model="ruleForm"
status-icon
:rules="rules"
label-width="10px"
class="demo-ruleForm"
>
<el-form-item label="" prop="username">
<el-input v-model="ruleForm.username" autocomplete="off" placeholder="请输入用户名"/>
</el-form-item>
<el-form-item label="" prop="password">
<el-input v-model="ruleForm.password" type="password" autocomplete="off" placeholder="请输入密码" />
</el-form-item>
<el-form-item label="" prop="code" >
<el-input v-model="ruleForm.code" autocomplete="off" style="width:150px" placeholder="请输入验证码"/>
<div class="code">
<img v-if="captchaImage":src="captchaImage" alt=""/>
<el-button :disabled="isTimeDisable" @click="refreshCaptcha" style="height: 10px; font-size: smaller; margin-left:-30px; border:none" >{{codeContent}}</el-button>
</div>
</el-form-item>
<div class="foot">
<el-button type="primary" @click="submitForm(ruleFormRef)">
提交
</el-button>
<el-button @click="resetForm(ruleFormRef)">
重置
</el-button>
</div>
</el-form>
</el-card>
</template>
<style scoped>
.foot {
margin-top:40px;
}
.code {
display: flex; flex-direction:column; justify-content: space-between; margin-left: 30%;
}
.code img{
margin-left: -40px;
width: 140px;
height: 50px;
}
</style>
Index.vue---->登陆引导
<script setup lang='ts'>
</script>
<template>
<div>This is Index</div>
<router-link to="/login">点击跳转至登陆页</router-link>
</template>
<style lang='scss' scoped>
</style>
Home.vue---->需要权限访问页
<script setup lang='ts'>
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
const accessToken = ref(localStorage.getItem('accessToken')); // 假设你将 accessToken 存储在 localStorage
onMounted(() => {
if (!accessToken.value) {
ElMessage.warning('无权限');
}
});
</script>
<template>
<div v-if="accessToken">This is Home</div>
<div v-else style="display: none;"></div> <!-- 为了保持结构一致,您也可以选择不渲染这个 div -->
</template>
<style lang='scss' scoped>
</style>
后端代码使用base64captcha生成存储至redis集群中,只需调用方法
目录结构说明
D:.
├─captcha_Handler # 验证码逻辑
├─constants # 一些常量
├─Controller # 接口定义
├─Db_Handler # 数据库逻辑
├─Middle # 定义中间件
├─model # 一些模型
├─Redis_Handler # redis逻辑
├─Log_Handler # 日志实例
└─Tools # 一些工具
redis_Handler---->redis实例
package Redis_Handler
import (
"context"
"github.com/donnie4w/go-logger/logger"
"github.com/redis/go-redis/v9"
"time"
)
type RedisClient struct {
Ctx context.Context
RedisClusterCfg []string
Client *redis.ClusterClient
Logger *logger.Logging
}
// NewRedisClient 初始化redis客户端实例
func NewRedisClient(RedisClusterCfg []string, ctx context.Context, myLog *logger.Logging) *RedisClient {
return &RedisClient{
RedisClusterCfg: RedisClusterCfg,
Client: redis.NewClusterClient(&redis.ClusterOptions{
Addrs: RedisClusterCfg,
PoolSize: 10, // 最大连接数
MinIdleConns: 2, // 最小闲置连接数
DialTimeout: 30 * time.Second, // 闲置连接超时
}),
Ctx: ctx,
Logger: myLog,
}
}
// SetKV 存储数据 有效期60秒
func (r *RedisClient) SetKV(key, value string) error {
err := r.Client.Set(r.Ctx, key, value, 60*time.Second).Err()
if err != nil {
r.Logger.Errorf("存储验证码失败")
return err
}
return nil
}
// GetKV 获取数据 有效期30秒
func (r *RedisClient) GetKV(key string) (string, error) {
cmd := r.Client.Get(r.Ctx, key)
if cmd.Err() != nil {
r.Logger.Errorf("获取验证码失败")
return "", cmd.Err()
}
return cmd.Val(), nil
}
func (r *RedisClient) DelKV(key string) error {
cmd := r.Client.Del(r.Ctx, key)
if cmd.Err() != nil {
r.Logger.Errorf("删除验证码失败")
return cmd.Err()
}
return nil
}
// DelRepeatKey 数据冗余清洗
func (r *RedisClient) DelRepeatKey(ClientIp string) error {
exists, err := r.Client.Exists(r.Ctx, ClientIp).Result()
if err != nil {
r.Logger.Errorf("数据冗余清洗失败")
return err
}
if exists != 0 {
OldCaptchaId := r.Client.Get(r.Ctx, ClientIp).Val()
err := r.DelKV(OldCaptchaId)
if err != nil {
r.Logger.Errorf("验证码校验完成后删除失败")
}
}
return nil
}
db_handler---->数据库实例
package Db_Handler
import (
"com.login.www/model"
"database/sql"
"errors"
"fmt"
"github.com/donnie4w/go-logger/logger"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"strconv"
"time"
)
type DbClient struct {
DB *gorm.DB
Dsn string
sqlDB *sql.DB
logger *logger.Logging
}
// NewDbClient 初始化数据库客户端实例
func NewDbClient(dsn string, myLog *logger.Logging) *DbClient {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
})
if err != nil {
myLog.Infof("连接数据库失败")
panic("failed to connect database")
}
sqlDB, err := db.DB()
if err != nil {
myLog.Infof("连接数据库失败")
panic("failed to get database connection")
}
// 设置连接池
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(20) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生命周期
return &DbClient{
Dsn: dsn,
DB: db,
sqlDB: sqlDB,
logger: myLog,
}
}
func (db *DbClient) Close() error {
if db.sqlDB == nil {
db.logger.Error("数据库关闭失败")
return errors.New("database connection is not initialized")
}
return db.sqlDB.Close() // 使用 db.sqlDB
}
func (db *DbClient) GetUserUsername(username string) (string, error) {
var user model.User
if err := db.DB.Where("username = ?", username).Unscoped().Find(&user).Error; err != nil {
return "", err
}
return user.Username, nil
}
func (db *DbClient) GetUserPassword(username string) (string, error) {
var user model.User
if err := db.DB.Where("username = ?", username).Unscoped().Find(&user).Error; err != nil {
return "", err
}
return user.Password, nil
}
func (db *DbClient) GetUserId(username string) (string, error) {
var user model.User
if err := db.DB.Where("username = ?", username).Unscoped().Find(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
fmt.Println("如果用户不存在,用户ID的值是:", user.Userid)
// 如果记录不存在,返回特定错误
return "", fmt.Errorf("user not found")
}
return "", err // 返回其他可能的错误
}
return strconv.Itoa(user.Userid), nil
}
middle_auth---->有关JWT的中间件
package Middle
import (
tools "com.login.www/Tools"
"com.login.www/constants"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"time"
)
type JwtPayLoad struct {
UserId int `json:"userid"`
UserName string `json:"username"`
}
type CustomClaims struct {
JwtPayLoad
jwt.RegisteredClaims
}
// GenerateTokens 生成JWT token
func GenerateTokens(userid int, username string) (string, error) {
// 创建登陆token
accessClaims := CustomClaims{
JwtPayLoad: JwtPayLoad{
UserId: userid,
UserName: username,
},
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(constants.AccessTokenExpiration)),
},
}
accessTokenObj := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims)
accessToken, err := accessTokenObj.SignedString(constants.JwtSecretKey)
if err != nil {
return "", err
}
return accessToken, nil
}
// ValidateToken token校验
func ValidateToken(tokenString string) (*jwt.Token, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return constants.JwtSecretKey, nil
})
if err != nil {
return nil, err
}
return token, nil
}
// TokenAuthMiddleware 验证有没有Token
func TokenAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 获取accessToken
tokenString := c.GetHeader("Authorization")
// 如果没有accessToken
if tokenString == "" {
c.JSON(constants.ErrorCodeAccessTokenExpired, gin.H{"code": constants.ErrorCodeAccessTokenExpired, "msg": "没有accessToken"})
c.Abort()
return
}
// 去除 "Bearer " 前缀
tokenString = tools.CleanStr(tokenString)
fmt.Println(tokenString)
token, err := ValidateToken(tokenString)
if err != nil || !token.Valid {
fmt.Println(err)
c.JSON(constants.ErrorCodeAccessTokenExpired, gin.H{"code": constants.ErrorCodeAccessTokenExpired, "error": "accessToken不正确或已过期"})
c.Abort()
return
}
c.Next()
}
}
middle_core---->解决跨域问题
package Middle
import (
"github.com/gin-gonic/gin"
"net/http"
)
// Cors 解决前后端跨域
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
c.Header("Access-Control-Allow-Methods", "*")
c.Header("Access-Control-Allow-Headers", "*")
c.Header("Access-Control-Expose-Headers", "*")
c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
constants
package constants
import (
"github.com/mojocn/base64Captcha"
"image/color"
"time"
)
const (
// AccessTokenExpiration JWT过期时间
AccessTokenExpiration = 10 * time.Minute
// ErrorCodeAccessTokenExpired 返回码
ErrorCodeAccessTokenExpired = 292
// Dsn 数据源连接串
Dsn = "dbuser1:NSD2021@tedu.cn@tcp(192.168.40.199:13306)/user?charset=utf8mb4&parseTime=True&loc=Local"
)
var (
// JwtSecretKey JWT加盐
JwtSecretKey = []byte("you_secret_salt")
// RedisCfg redis配置
RedisCfg = []string{
"192.168.40.180:7000",
"192.168.40.180:7001",
"192.168.40.180:7002",
"192.168.40.181:7000",
"192.168.40.181:7001",
"192.168.40.181:7002",
}
// MaxHeight ... 验证码的各项参数
MaxHeight = 40
MaxWidth = 160
Fonts = []string{"wqy-microhei.ttc"}
FontsStorage base64Captcha.FontsStorage
NoiseCount = 10
ShowLineOptions = 0.9
Bg = color.RGBA{
R: 0,
G: 255,
B: 255,
A: 0,
}
)
tools_CreateLogPath---->获取日志路径
package tools
import (
"os"
"path/filepath"
)
func CreateLogPath() string {
rootPath, _ := os.Getwd()
logDir := filepath.Join(rootPath, "record.log")
return logDir
}
tools_getclient---->获取客户端的IP地址,根据这个IP来清洗redis中冗余的数据
package tools
import "github.com/gin-gonic/gin"
// ClientIP 获取客户端真实IP
func ClientIP(c *gin.Context) string {
ip := c.Request.Header.Get("X-Forwarded-For")
if ip == "" {
ip = c.Request.Header.Get("X-Real-IP")
}
if ip == "" {
ip = c.ClientIP() // 最后fallback
}
return ip
}
tools_strclean---->删除前缀,提取Token
package tools
import "strings"
// CleanStr 提取token
func CleanStr(str string) string {
str = strings.Trim(str, `"`)
str = strings.TrimPrefix(str, "Bearer ")
return str
}
controller_code.go---->生成验证码接口
package Controller
import (
tools "com.login.www/Tools"
"com.login.www/captcha_Handler"
"github.com/donnie4w/go-logger/logger"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
"image/color"
"net/http"
)
// Code 生成验证码接口
func Code(c *gin.Context, maxHeight, maxWidth, noiseCount, showLineOptions int, bg color.RGBA, fontsStorage base64Captcha.FontsStorage, fonts []string, rdsStore *captcha_Handler.RedisStore, myLog *logger.Logging) {
clientId := tools.ClientIP(c)
captchaID, captchaImg, captchaAnswer, err := captcha_Handler.GenerateCaptcha(maxHeight, maxWidth, noiseCount, showLineOptions, bg, fontsStorage, fonts, clientId, rdsStore, myLog)
if err != nil {
myLog.Errorf("用户%s获取验证码失败", clientId)
c.JSON(http.StatusInternalServerError, gin.H{"error": "生成验证码失败"})
return
}
myLog.Infof("用户%s获取验证码成功", clientId)
c.JSON(http.StatusOK, gin.H{"captcha_id": captchaID, "captchaImg": captchaImg, "captchaAnswer": captchaAnswer})
}
controller_Login---->登陆校验接口
package Controller
import (
"com.login.www/Db_Handler"
"com.login.www/Middle"
tools "com.login.www/Tools"
"com.login.www/captcha_Handler"
"com.login.www/model"
"github.com/donnie4w/go-logger/logger"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
// Login 校验登陆接口
func Login(c *gin.Context, db *Db_Handler.DbClient, rdsStore *captcha_Handler.RedisStore, myLog *logger.Logging) {
clientIp := tools.ClientIP(c)
var loginForm model.LoginForm
if err := c.ShouldBindJSON(&loginForm); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"msg": "输入的参数不合法,请重新输入"})
return
}
// 创建 RedisStore 实例
captchaId, err := captcha_Handler.GetCodeFromClientIp(clientIp, rdsStore)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "验证码已过期,请重试"})
return
}
ok := rdsStore.Verify(captchaId, loginForm.Code, true)
if !ok {
c.JSON(499, gin.H{"msg": "验证码填写错误,请重试"})
return
}
_, err = db.GetUserUsername(loginForm.Username)
if err != nil {
myLog.Infof("客户%s登陆失败,用户没有注册?", loginForm.Username)
c.JSON(http.StatusForbidden, gin.H{
"msg": "用户尚未注册",
})
return
}
UserPasswd, err := db.GetUserPassword(loginForm.Username)
if loginForm.Password != UserPasswd {
myLog.Infof("客户%s登陆失败,密码不对", loginForm.Username)
c.JSON(http.StatusForbidden, gin.H{
"msg": "登陆失败,用户密码不正确,请重新填写",
})
return
}
captcha_Handler.RemoveKeyByClientId(clientIp, rdsStore)
myLog.Infof("客户%s删除冗余验证码成功", loginForm.Username)
userId, _ := db.GetUserId(loginForm.Username)
userid, err := strconv.Atoi(userId)
tokens, err := Middle.GenerateTokens(userid, loginForm.Username)
myLog.Infof("客户%s登陆系统成功", loginForm.Username)
c.JSON(200, gin.H{
"msg": "登陆成功",
"accessToken": tokens,
})
}
controller_Home---->需要权限访问的接口
package Controller
import (
"github.com/gin-gonic/gin"
)
// Home 访问权限页
func Home(c *gin.Context) {
// 逻辑
}
captcha_Handler---->验证码的逻辑
package captcha_Handler
import (
"com.login.www/Redis_Handler"
"com.login.www/model"
"errors"
"fmt"
"github.com/donnie4w/go-logger/logger"
"github.com/mojocn/base64Captcha"
"image/color"
)
type RedisStore struct {
Store *model.Store
rdsClient *Redis_Handler.RedisClient
myLog *logger.Logging
}
// NewRedisStore 是 RedisStore 的构造函数
func NewRedisStore(redisClient *Redis_Handler.RedisClient, myLog *logger.Logging) *RedisStore {
return &RedisStore{rdsClient: redisClient, myLog: myLog}
}
func (r RedisStore) Set(id string, value string) error {
err := r.rdsClient.SetKV(id, value)
if err != nil {
return errors.New("保存验证码到redis失败了")
}
return nil
}
func (r RedisStore) Get(id string, clear bool) string {
val, err := r.rdsClient.GetKV(id)
if err != nil {
return err.Error()
}
if clear {
//clear为true,验证通过,删除这个验证码
err := r.rdsClient.DelKV(id)
if err != nil {
err := errors.New("移除通过的验证码失败")
return err.Error()
}
}
return val
}
func (r RedisStore) Verify(id, answer string, clear bool) bool {
v := r.Get(id, false)
if v == answer {
if clear { // 只有在验证通过且需要清除时,才删除
_ = r.rdsClient.DelKV(id)
}
return true
}
return false
}
// GenerateCaptcha 生成验证码
func GenerateCaptcha(maxHeight, maxWidth, noiseCount, showLineOptions int, bg color.RGBA, fontsStorage base64Captcha.FontsStorage, fonts []string, clientId string, store *RedisStore, myLog *logger.Logging) (string, string, string, error) {
err := store.rdsClient.DelRepeatKey(clientId)
if err != nil {
myLog.Errorf("客户端%s删除冗余验证码失败: %s", clientId, err)
return "", "", "", fmt.Errorf("删除冗余验证码失败: %w", err)
}
driver := base64Captcha.NewDriverMath(maxHeight, maxWidth, noiseCount, showLineOptions, &bg, fontsStorage, fonts)
captcha := base64Captcha.NewCaptcha(driver, store)
captchaID, img, answer, err := captcha.Generate()
if err != nil {
myLog.Infof("客户端%s生成验证码并存入redis失败,error:%s:", clientId, err)
}
myLog.Infof("客户端%s生成验证码并存入redis成功:", clientId)
// 绑定用户IP地址和验证码ID
err = store.rdsClient.SetKV(clientId, captchaID)
if err != nil {
myLog.Errorf("客户端%s生成验证码失败: %s", clientId, err)
return "", "", "", fmt.Errorf("生成验证码失败: %w", err)
}
myLog.Infof("客户端%s生成验证码成功", clientId)
return captchaID, img, answer, nil
}
// GetCodeFromClientIp 根据用户名获取验证码ID
func GetCodeFromClientIp(clientId string, store *RedisStore) (captchaId string, err error) {
captchaId, err = store.rdsClient.GetKV(clientId)
if err != nil {
return "", err
}
return captchaId, nil
}
func RemoveKeyByClientId(clientIp string, store *RedisStore) {
_ = store.rdsClient.DelKV(clientIp)
}
model_LoginForm---->登陆表单
package model
// LoginForm 登陆表单
type LoginForm struct {
Username string `form:"username" binding:"required" json:"username"`
Password string `form:"password" binding:"required" json:"password"`
Code string `form:"code" binding:"required" json:"code"`
}
model_RedisStore---->实现base64包接口
package model
// Store 实现验证码接口
type Store interface {
//Set 验证码存入验证码池的方法,id为键,验证码为值
Set(id string, value string) error
//Get 获取验证码的方法
Get(id string, clear bool) string
//Verify 校验验证码的方法
Verify(id, answer string, clear bool) bool
}
model_User---->用户模型
package model
import "gorm.io/gorm"
type User struct {
gorm.Model
Userid int `json:"userid,omitempty" yaml:"userid" gorm:"userid"`
Username string `json:"username,omitempty" yaml:"username" gorm:"username"`
Password string `json:"password,omitempty" yaml:"password" gorm:"password"`
}
main.go
package main
import (
"com.login.www/Controller"
"com.login.www/Db_Handler"
"com.login.www/Log_Handler"
"com.login.www/Middle"
"com.login.www/Redis_Handler"
tools "com.login.www/Tools"
"com.login.www/captcha_Handler"
"com.login.www/constants"
"context"
"github.com/donnie4w/go-logger/logger"
"github.com/gin-gonic/gin"
)
var (
db *Db_Handler.DbClient
rdb *Redis_Handler.RedisClient
ctx = context.Background()
rdsStore *captcha_Handler.RedisStore
myLog *logger.Logging
)
func init() {
// 初始化日志输出
myLog = Log_Handler.InitLogger(tools.CreateLogPath())
// 初始化 Db 客户端
db = Db_Handler.NewDbClient(constants.Dsn, myLog)
// 初始化 Redis 客户端
rdb = Redis_Handler.NewRedisClient(constants.RedisCfg, ctx, myLog)
// 初始化RedisStore
rdsStore = captcha_Handler.NewRedisStore(rdb, myLog)
}
func main() {
r := gin.Default()
r.Use(Middle.Cors())
myLog.Info("开放跨域请求")
r.GET("/code", func(c *gin.Context) {
Controller.Code(c, constants.MaxHeight, constants.MaxWidth, constants.NoiseCount, int(constants.ShowLineOptions), constants.Bg, constants.FontsStorage, constants.Fonts, rdsStore, myLog)
})
r.POST("/login", func(c *gin.Context) {
Controller.Login(c, db, rdsStore, myLog)
})
r.Use(Middle.TokenAuthMiddleware())
r.POST("/home", Controller.Home)
myLog.Infof("应用程序在8001端口启动中")
if err := r.Run("192.168.40.1:8001").Error(); err != "" {
myLog.Errorf("应用启动失败:%s", err)
}
}