Skip to content

Commit 0ef9aac

Browse files
奇淼(piexlmaxyexklongzhang83noovertime7
authored
* feat: flipped-aurora#807 自定义验证码开关,可以自行配置 * 添加数据库列表支持:SQL SERVER * [user.vue]: 新增邮箱手机合法性校验,邮箱手机非必填 * [menu.js]: 修复错误的API注释 * 调整配置文件 是其可以支持oracle和mssql Co-authored-by: Yexk <yexk@yexk.cn> Co-authored-by: 逆光飞翔 <191180776@qq.com> Co-authored-by: chenteng <cc17854330572@163.com>
1 parent 8170daf commit 0ef9aac

File tree

19 files changed

+293
-46
lines changed

19 files changed

+293
-46
lines changed

server/api/v1/system/sys_captcha.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package system
22

33
import (
4+
"time"
5+
46
"github.com/flipped-aurora/gin-vue-admin/server/global"
57
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
68
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
@@ -21,9 +23,22 @@ type BaseApi struct{}
2123
// @Security ApiKeyAuth
2224
// @accept application/json
2325
// @Produce application/json
24-
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度"
26+
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码"
2527
// @Router /base/captcha [post]
2628
func (b *BaseApi) Captcha(c *gin.Context) {
29+
// 判断验证码是否开启
30+
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
31+
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
32+
key := c.ClientIP()
33+
v, ok := global.BlackCache.Get(key)
34+
if !ok {
35+
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
36+
}
37+
38+
var oc bool
39+
if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
40+
oc = true
41+
}
2742
// 字符,公式,验证码配置
2843
// 生成默认数字的driver
2944
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
@@ -39,5 +54,17 @@ func (b *BaseApi) Captcha(c *gin.Context) {
3954
CaptchaId: id,
4055
PicPath: b64s,
4156
CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
57+
OpenCaptcha: oc,
4258
}, "验证码获取成功", c)
4359
}
60+
61+
// 类型转换
62+
func interfaceToInt(v interface{}) (i int) {
63+
switch v := v.(type) {
64+
case int:
65+
i = v
66+
default:
67+
i = 0
68+
}
69+
return
70+
}

server/api/v1/system/sys_user.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package system
22

33
import (
44
"strconv"
5+
"time"
56

67
"github.com/flipped-aurora/gin-vue-admin/server/global"
78
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
@@ -26,6 +27,8 @@ import (
2627
func (b *BaseApi) Login(c *gin.Context) {
2728
var l systemReq.Login
2829
err := c.ShouldBindJSON(&l)
30+
key := c.ClientIP()
31+
2932
if err != nil {
3033
response.FailWithMessage(err.Error(), c)
3134
return
@@ -35,22 +38,42 @@ func (b *BaseApi) Login(c *gin.Context) {
3538
response.FailWithMessage(err.Error(), c)
3639
return
3740
}
38-
if store.Verify(l.CaptchaId, l.Captcha, true) {
41+
42+
// 判断验证码是否开启
43+
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
44+
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
45+
v, ok := global.BlackCache.Get(key)
46+
if !ok {
47+
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
48+
}
49+
50+
var oc bool
51+
if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
52+
oc = true
53+
}
54+
55+
if !oc || store.Verify(l.CaptchaId, l.Captcha, true) {
3956
u := &system.SysUser{Username: l.Username, Password: l.Password}
4057
user, err := userService.Login(u)
4158
if err != nil {
4259
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
60+
// 验证码次数+1
61+
global.BlackCache.Increment(key, 1)
4362
response.FailWithMessage("用户名不存在或者密码错误", c)
4463
return
4564
}
4665
if user.Enable != 1 {
4766
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
67+
// 验证码次数+1
68+
global.BlackCache.Increment(key, 1)
4869
response.FailWithMessage("用户被禁止登录", c)
4970
return
5071
}
5172
b.TokenNext(c, *user)
5273
return
5374
}
75+
// 验证码次数+1
76+
global.BlackCache.Increment(key, 1)
5477
response.FailWithMessage("验证码错误", c)
5578
}
5679

server/config.docker.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ captcha:
5151
key-long: 6
5252
img-width: 240
5353
img-height: 80
54+
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
55+
open-captcha-timeout: 3600 # open-captcha大于0时才生效
5456

5557
# mysql connect configuration
5658
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)

server/config.yaml

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ email:
3535

3636
# system configuration
3737
system:
38-
env: public # Change to "develop" to skip authentication for development mode
38+
env: public # Change to "develop" to skip authentication for development mode
3939
addr: 8888
4040
db-type: mysql
41-
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
42-
use-redis: false # 使用redis
41+
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置
42+
use-redis: false # 使用redis
4343
use-multipoint: false
4444
# IP限制次数 一个小时15000次
4545
iplimit-count: 15000
@@ -51,6 +51,8 @@ captcha:
5151
key-long: 6
5252
img-width: 240
5353
img-height: 80
54+
open-captcha: 0 # 0代表一直开启,大于0代表限制次数
55+
open-captcha-timeout: 3600 # open-captcha大于0时才生效
5456

5557
# mysql connect configuration
5658
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
@@ -79,10 +81,31 @@ pgsql:
7981
max-open-conns: 100
8082
log-mode: ""
8183
log-zap: false
82-
84+
oracle:
85+
path: ""
86+
port: ""
87+
config: ""
88+
db-name: ""
89+
username: ""
90+
password: ""
91+
max-idle-conns: 10
92+
max-open-conns: 100
93+
log-mode: ""
94+
log-zap: false
95+
mssql:
96+
path: ""
97+
port: ""
98+
config: ""
99+
db-name: ""
100+
username: ""
101+
password: ""
102+
max-idle-conns: 10
103+
max-open-conns: 100
104+
log-mode: ""
105+
log-zap: false
83106
db-list:
84107
- disable: true # 是否禁用
85-
type: "" # 数据库的类型,目前支持mysql、pgsql
108+
type: "" # 数据库的类型,目前支持mysql、pgsql、mssql、oracle
86109
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
87110
path: ""
88111
port: ""
@@ -95,7 +118,6 @@ db-list:
95118
log-mode: ""
96119
log-zap: false
97120

98-
99121
# local configuration
100122
local:
101123
path: uploads/file
@@ -175,7 +197,7 @@ excel:
175197
# timer task db clear table
176198
Timer:
177199
start: true
178-
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
200+
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
179201
detail:
180202
- tableName: sys_operation_records
181203
compareField: created_at

server/config/captcha.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package config
22

33
type Captcha struct {
4-
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
5-
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
6-
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
4+
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"` // 验证码长度
5+
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"` // 验证码宽度
6+
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
7+
OpenCaptcha int `mapstructure:"open-captcha" json:"open-captcha" yaml:"open-captcha"` // 防爆破验证码开启此数,0代表每次登录都需要验证码,其他数字代表错误密码此数,如3代表错误三次后出现验证码
8+
OpenCaptchaTimeOut int `mapstructure:"open-captcha-timeout" json:"open-captcha-timeout" yaml:"open-captcha-timeout"` // 防爆破验证码超时时间,单位:s(秒)
79
}

server/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Server struct {
1111
AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"`
1212
// gorm
1313
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
14+
Mssql Mssql `mapstructure:"mssql" json:"mssql" yaml:"mssql"`
1415
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
1516
Oracle Oracle `mapstructure:"oracle" json:"oracle" yaml:"oracle"`
1617
DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`

server/config/gorm_mssql.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package config
2+
3+
type Mssql struct {
4+
GeneralDB `yaml:",inline" mapstructure:",squash"`
5+
}
6+
//dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
7+
func (m *Mssql) Dsn() string {
8+
return "sqlserver://" + m.Username + ":" + m.Password + "@" + m.Path + ":" + m.Port + "?database=" + m.Dbname + "&encrypt=disable"
9+
}
10+
11+
func (m *Mssql) GetLogMode() string {
12+
return m.LogMode
13+
}

server/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ require (
3939
golang.org/x/text v0.3.7
4040
gorm.io/driver/mysql v1.3.3
4141
gorm.io/driver/postgres v1.3.4
42+
gorm.io/driver/sqlserver v1.3.2
4243
gorm.io/gorm v1.23.4
4344
nhooyr.io/websocket v1.8.6
4445
)
@@ -124,7 +125,6 @@ require (
124125
gopkg.in/ini.v1 v1.55.0 // indirect
125126
gopkg.in/yaml.v2 v2.4.0 // indirect
126127
gopkg.in/yaml.v3 v3.0.0 // indirect
127-
gorm.io/driver/sqlserver v1.3.2 // indirect
128128
gorm.io/plugin/dbresolver v1.1.0 // indirect
129129
modernc.org/libc v1.15.1 // indirect
130130
modernc.org/mathutil v1.4.1 // indirect

server/initialize/db_list.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ func DBList() {
1717
switch info.Type {
1818
case "mysql":
1919
dbMap[info.AliasName] = GormMysqlByConfig(config.Mysql{GeneralDB: info.GeneralDB})
20+
case "mssql":
21+
dbMap[info.AliasName] = GormMssqlByConfig(config.Mssql{GeneralDB: info.GeneralDB})
2022
case "pgsql":
2123
dbMap[info.AliasName] = GormPgSqlByConfig(config.Pgsql{GeneralDB: info.GeneralDB})
2224
case "oracle":

server/initialize/gorm.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ func Gorm() *gorm.DB {
1919
return GormMysql()
2020
case "pgsql":
2121
return GormPgSql()
22+
case "oracle":
23+
return GormOracle()
24+
case "mssql":
25+
return GormMssql()
2226
default:
2327
return GormMysql()
2428
}

server/initialize/gorm_mssql.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* @Author: 逆光飞翔 191180776@qq.com
3+
* @Date: 2022-12-08 17:25:49
4+
* @LastEditors: 逆光飞翔 191180776@qq.com
5+
* @LastEditTime: 2022-12-08 18:00:00
6+
* @FilePath: \server\initialize\gorm_mssql.go
7+
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
8+
*/
9+
package initialize
10+
11+
import (
12+
"github.com/flipped-aurora/gin-vue-admin/server/config"
13+
"github.com/flipped-aurora/gin-vue-admin/server/global"
14+
"github.com/flipped-aurora/gin-vue-admin/server/initialize/internal"
15+
"gorm.io/driver/sqlserver"
16+
"gorm.io/gorm"
17+
)
18+
19+
// GormMssql 初始化Mssql数据库
20+
// Author [LouisZhang](191180776@qq.com)
21+
func GormMssql() *gorm.DB {
22+
m := global.GVA_CONFIG.Mssql
23+
if m.Dbname == "" {
24+
return nil
25+
}
26+
mssqlConfig := sqlserver.Config{
27+
DSN: m.Dsn(), // DSN data source name
28+
DefaultStringSize: 191, // string 类型字段的默认长度
29+
}
30+
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
31+
return nil
32+
} else {
33+
db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine)
34+
sqlDB, _ := db.DB()
35+
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
36+
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
37+
return db
38+
}
39+
}
40+
41+
// GormMssqlByConfig 初始化Mysql数据库用过传入配置
42+
func GormMssqlByConfig(m config.Mssql) *gorm.DB {
43+
if m.Dbname == "" {
44+
return nil
45+
}
46+
mssqlConfig := sqlserver.Config{
47+
DSN: m.Dsn(), // DSN data source name
48+
DefaultStringSize: 191, // string 类型字段的默认长度
49+
}
50+
if db, err := gorm.Open(sqlserver.New(mssqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
51+
panic(err)
52+
} else {
53+
db.InstanceSet("gorm:table_options", "ENGINE=InnoDB")
54+
sqlDB, _ := db.DB()
55+
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
56+
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
57+
return db
58+
}
59+
}

server/model/system/response/sys_captcha.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ type SysCaptchaResponse struct {
44
CaptchaId string `json:"captchaId"`
55
PicPath string `json:"picPath"`
66
CaptchaLength int `json:"captchaLength"`
7+
OpenCaptcha bool `json:"openCaptcha"`
78
}

server/service/system/sys_auto_code_interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ func (autoCodeService *AutoCodeService) Database(businessDB string) Database {
2929
switch info.Type {
3030
case "mysql":
3131
return AutoCodeMysql
32+
case "mssql":
33+
return AutoCodeMssql
3234
case "pgsql":
3335
return AutoCodePgsql
3436
case "oracle":

0 commit comments

Comments
 (0)