From e9c55a08530835ff06d7b1be3e8cd19aee7cae2a Mon Sep 17 00:00:00 2001 From: lanrenwo Date: Thu, 2 Jun 2022 17:58:22 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=96=B0=E5=A2=9Eradius=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/admin/api_group.go | 4 ++- server/dbdata/group.go | 70 +++++++++++++++++++++++++++++------- server/dbdata/tables.go | 29 +++++++-------- server/dbdata/user.go | 53 +++++++++++++++++++++++++++ server/go.mod | 1 + web/src/pages/group/List.vue | 52 +++++++++++++++++++-------- 6 files changed, 168 insertions(+), 41 deletions(-) diff --git a/server/admin/api_group.go b/server/admin/api_group.go index 17fbdb7..dd440e1 100644 --- a/server/admin/api_group.go +++ b/server/admin/api_group.go @@ -62,7 +62,9 @@ func GroupDetail(w http.ResponseWriter, r *http.Request) { RespError(w, RespInternalErr, err) return } - + if len(data.Auth) == 0 { + data.Auth["type"] = "local" + } RespSucess(w, data) } diff --git a/server/dbdata/group.go b/server/dbdata/group.go index 872a2a0..d39d476 100644 --- a/server/dbdata/group.go +++ b/server/dbdata/group.go @@ -1,6 +1,7 @@ package dbdata import ( + "encoding/json" "errors" "fmt" "net" @@ -32,19 +33,27 @@ type ValData struct { Note string `json:"note"` } +type AuthRadius struct { + Addr string `json:"addr"` + Secret string `json:"secret"` +} + // type Group struct { -// Id int `json:"id" xorm:"pk autoincr not null"` -// Name string `json:"name" xorm:"not null unique"` -// Note string `json:"note"` -// AllowLan bool `json:"allow_lan"` -// ClientDns []ValData `json:"client_dns"` -// RouteInclude []ValData `json:"route_include"` -// RouteExclude []ValData `json:"route_exclude"` -// LinkAcl []GroupLinkAcl `json:"link_acl"` -// Bandwidth int `json:"bandwidth"` // 带宽限制 -// Status int8 `json:"status"` // 1正常 -// CreatedAt time.Time `json:"created_at"` -// UpdatedAt time.Time `json:"updated_at"` +// Id int `json:"id" xorm:"pk autoincr not null"` +// Name string `json:"name" xorm:"varchar(60) not null unique"` +// Note string `json:"note" xorm:"varchar(255)"` +// AllowLan bool `json:"allow_lan" xorm:"Bool"` +// ClientDns []ValData `json:"client_dns" xorm:"Text"` +// RouteInclude []ValData `json:"route_include" xorm:"Text"` +// RouteExclude []ValData `json:"route_exclude" xorm:"Text"` +// DsExcludeDomains string `json:"ds_exclude_domains" xorm:"Text"` +// DsIncludeDomains string `json:"ds_include_domains" xorm:"Text"` +// LinkAcl []GroupLinkAcl `json:"link_acl" xorm:"Text"` +// Bandwidth int `json:"bandwidth" xorm:"Int"` // 带宽限制 +// Auth map[string]interface{} `json:"auth" xorm:"not null default '{}' varchar(255)"` // 认证方式 +// Status int8 `json:"status" xorm:"Int"` // 1正常 +// CreatedAt time.Time `json:"created_at" xorm:"DateTime created"` +// UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"` // } func GetGroupNames() []string { @@ -145,6 +154,20 @@ func SetGroup(g *Group) error { if err != nil { return errors.New("排除域名有误:" + err.Error()) } + // 处理认证类型 + switch g.Auth["type"] { + case "local": + g.Auth = map[string]interface{}{ + "type": g.Auth["type"], + } + case "radius": + err = checkRadiusData(g.Auth) + if err != nil { + return err + } + default: + return errors.New("未知的认证类型") + } g.UpdatedAt = time.Now() if g.Id > 0 { @@ -167,6 +190,24 @@ func parseIpNet(s string) (string, *net.IPNet, error) { return ipMask, ipNet, nil } + +func checkRadiusData(auth map[string]interface{}) error { + radisConf := AuthRadius{} + bodyBytes, err := json.Marshal(auth["radius"]) + if err != nil { + return errors.New("Radius的密钥/服务器地址填写有误") + } + json.Unmarshal(bodyBytes, &radisConf) + if !ValidateIpPort(radisConf.Addr) { + return errors.New("Radius的服务器地址填写有误") + } + // freeradius官网最大8000字符, 这里限制800 + if len(radisConf.Secret) < 8 || len(radisConf.Secret) > 800 { + return errors.New("Radius的密钥长度需在8~800个字符之间") + } + return nil +} + func CheckDomainNames(domains string) error { if domains == "" { return nil @@ -187,3 +228,8 @@ func ValidateDomainName(domain string) bool { RegExp := regexp.MustCompile(`^([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+[A-Za-z]{2,18}$`) return RegExp.MatchString(domain) } + +func ValidateIpPort(addr string) bool { + RegExp := regexp.MustCompile(`^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$$`) + return RegExp.MatchString(addr) +} diff --git a/server/dbdata/tables.go b/server/dbdata/tables.go index a0ffea2..9dd2b5d 100644 --- a/server/dbdata/tables.go +++ b/server/dbdata/tables.go @@ -6,20 +6,21 @@ import ( ) type Group struct { - Id int `json:"id" xorm:"pk autoincr not null"` - Name string `json:"name" xorm:"varchar(60) not null unique"` - Note string `json:"note" xorm:"varchar(255)"` - AllowLan bool `json:"allow_lan" xorm:"Bool"` - ClientDns []ValData `json:"client_dns" xorm:"Text"` - RouteInclude []ValData `json:"route_include" xorm:"Text"` - RouteExclude []ValData `json:"route_exclude" xorm:"Text"` - DsExcludeDomains string `json:"ds_exclude_domains" xorm:"Text"` - DsIncludeDomains string `json:"ds_include_domains" xorm:"Text"` - LinkAcl []GroupLinkAcl `json:"link_acl" xorm:"Text"` - Bandwidth int `json:"bandwidth" xorm:"Int"` // 带宽限制 - Status int8 `json:"status" xorm:"Int"` // 1正常 - CreatedAt time.Time `json:"created_at" xorm:"DateTime created"` - UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"` + Id int `json:"id" xorm:"pk autoincr not null"` + Name string `json:"name" xorm:"varchar(60) not null unique"` + Note string `json:"note" xorm:"varchar(255)"` + AllowLan bool `json:"allow_lan" xorm:"Bool"` + ClientDns []ValData `json:"client_dns" xorm:"Text"` + RouteInclude []ValData `json:"route_include" xorm:"Text"` + RouteExclude []ValData `json:"route_exclude" xorm:"Text"` + DsExcludeDomains string `json:"ds_exclude_domains" xorm:"Text"` + DsIncludeDomains string `json:"ds_include_domains" xorm:"Text"` + LinkAcl []GroupLinkAcl `json:"link_acl" xorm:"Text"` + Bandwidth int `json:"bandwidth" xorm:"Int"` // 带宽限制 + Auth map[string]interface{} `json:"auth" xorm:"not null default '{}' varchar(255)"` // 认证方式 + Status int8 `json:"status" xorm:"Int"` // 1正常 + CreatedAt time.Time `json:"created_at" xorm:"DateTime created"` + UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"` } type User struct { diff --git a/server/dbdata/user.go b/server/dbdata/user.go index 5fbb2d7..c513e63 100644 --- a/server/dbdata/user.go +++ b/server/dbdata/user.go @@ -1,13 +1,18 @@ package dbdata import ( + "context" + "encoding/json" "errors" "fmt" "sync" "time" + "github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/pkg/utils" "github.com/xlzd/gotp" + "layeh.com/radius" + "layeh.com/radius/rfc2865" ) // type User struct { @@ -68,6 +73,38 @@ func SetUser(v *User) error { // 验证用户登陆信息 func CheckUser(name, pwd, group string) error { + // 获取登入的group数据 + groupData := &Group{} + err := One("Name", group, groupData) + if err != nil { + return fmt.Errorf("%s %s", name, "No用户组") + } + if len(groupData.Auth) == 0 { + groupData.Auth["type"] = "local" + } + base.Debug(name + " auth type: " + fmt.Sprintf("%s", groupData.Auth["type"])) + switch groupData.Auth["type"] { + case "local": + return checkLocalUser(name, pwd, group) + case "radius": + radisConf := AuthRadius{} + bodyBytes, err := json.Marshal(groupData.Auth["radius"]) + if err != nil { + fmt.Errorf("%s %s", name, "Radius出现Marshal错误") + } + err = json.Unmarshal(bodyBytes, &radisConf) + if err != nil { + fmt.Errorf("%s %s", name, "Radius出现Unmarshal错误") + } + return checkRadiusUser(name, pwd, radisConf) + default: + return fmt.Errorf("%s %s", name, "无效的认证类型") + } + return nil +} + +// 验证本地用户登陆信息 +func checkLocalUser(name, pwd, group string) error { // TODO 严重问题 // return nil @@ -108,6 +145,22 @@ func CheckUser(name, pwd, group string) error { return nil } +func checkRadiusUser(name string, pwd string, raduisConf AuthRadius) error { + packet := radius.New(radius.CodeAccessRequest, []byte(raduisConf.Secret)) + rfc2865.UserName_SetString(packet, name) + rfc2865.UserPassword_SetString(packet, pwd) + ctx, done := context.WithTimeout(context.Background(), 3*time.Second) + defer done() + response, err := radius.Exchange(ctx, packet, raduisConf.Addr) + if err != nil { + return fmt.Errorf("%s %s", name, "Radius服务器连接异常, 请检测服务器和端口") + } + if response.Code != radius.CodeAccessAccept { + return fmt.Errorf("%s %s", name, "Radius:用户名或密码错误") + } + return nil +} + var ( userOtpMux = sync.Mutex{} userOtp = map[string]time.Time{} diff --git a/server/go.mod b/server/go.mod index f46c1b3..4f3d552 100644 --- a/server/go.mod +++ b/server/go.mod @@ -25,6 +25,7 @@ require ( golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac + layeh.com/radius v0.0.0-20210819152912-ad72663a72ab xorm.io/xorm v1.2.2 ) diff --git a/web/src/pages/group/List.vue b/web/src/pages/group/List.vue index fa19975..8524c53 100644 --- a/web/src/pages/group/List.vue +++ b/web/src/pages/group/List.vue @@ -207,6 +207,25 @@ + + + + 本地 + Radius + + + + + + + + + + + + + + @@ -300,7 +319,7 @@ 保存 取消 - + @@ -318,7 +337,8 @@ export default { this.$emit('update:route_name', ['用户组信息', '用户组列表']) }, mounted() { - this.getData(1) + this.getData(1); + this.setAuthData(); }, data() { return { @@ -335,21 +355,17 @@ export default { route_include: [{val: 'all', note: '默认全局代理'}], route_exclude: [], link_acl: [], + auth : {"type":'local'} }, rules: { name: [ - {required: true, message: '请输入用户名', trigger: 'blur'}, + {required: true, message: '请输入组名', trigger: 'blur'}, {max: 30, message: '长度小于 30 个字符', trigger: 'blur'} ], bandwidth: [ - {required: true, message: '请输入用户姓名', trigger: 'blur'}, - {type: 'number', message: '年龄必须为数字值'} + {required: true, message: '请输入带宽限制', trigger: 'blur'}, + {type: 'number', message: '带宽限制必须为数字值'} ], - email: [ - {required: true, message: '请输入用户邮箱', trigger: 'blur'}, - {type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']} - ], - status: [ {required: true} ], @@ -357,6 +373,14 @@ export default { } }, methods: { + setAuthData(row) { + var defAuthData = {"type":'local', + "radius":{"addr":"", "secret":""}, + } + if (this.ruleForm.auth.type == "local" || !row) { + this.ruleForm.auth = defAuthData; + } + }, handleDel(row) { axios.post('/group/del?id=' + row.id).then(resp => { const rdata = resp.data; @@ -378,15 +402,16 @@ export default { this.activeTab = "general" this.user_edit_dialog = true if (!row) { + this.setAuthData(row) return; } - axios.get('/group/detail', { params: { id: row.id, } }).then(resp => { - this.ruleForm = resp.data.data + this.ruleForm = resp.data.data; + this.setAuthData(resp.data.data); }).catch(error => { this.$message.error('哦,请求出错'); console.log(error); @@ -426,12 +451,11 @@ export default { arr.push({val: "", action: "allow", port: 0}); }, submitForm(formName) { - this.$refs[formName].validate((valid) => { + this.$refs[formName].validate((valid, obj) => { if (!valid) { console.log('error submit!!'); return false; } - axios.post('/group/set', this.ruleForm).then(resp => { const rdata = resp.data; if (rdata.code === 0) { From a450fe3eef21eae62c1e32de637e2696ba03e459 Mon Sep 17 00:00:00 2001 From: lanrenwo Date: Thu, 2 Jun 2022 20:50:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dtest=E6=9C=AA=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/dbdata/group.go | 14 +++++++++----- server/dbdata/user.go | 5 ++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/server/dbdata/group.go b/server/dbdata/group.go index d39d476..831c194 100644 --- a/server/dbdata/group.go +++ b/server/dbdata/group.go @@ -154,19 +154,23 @@ func SetGroup(g *Group) error { if err != nil { return errors.New("排除域名有误:" + err.Error()) } - // 处理认证类型 + // 处理认证方式的逻辑 + defAuth := map[string]interface{}{ + "type": "local", + } + if len(g.Auth) == 0 { + g.Auth = defAuth + } switch g.Auth["type"] { case "local": - g.Auth = map[string]interface{}{ - "type": g.Auth["type"], - } + g.Auth = defAuth case "radius": err = checkRadiusData(g.Auth) if err != nil { return err } default: - return errors.New("未知的认证类型") + return errors.New("#" + fmt.Sprintf("%s", g.Auth["type"]) + "#未知的认证类型") } g.UpdatedAt = time.Now() diff --git a/server/dbdata/user.go b/server/dbdata/user.go index c513e63..5f634f7 100644 --- a/server/dbdata/user.go +++ b/server/dbdata/user.go @@ -8,7 +8,6 @@ import ( "sync" "time" - "github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/pkg/utils" "github.com/xlzd/gotp" "layeh.com/radius" @@ -79,12 +78,12 @@ func CheckUser(name, pwd, group string) error { if err != nil { return fmt.Errorf("%s %s", name, "No用户组") } + // 初始化Auth if len(groupData.Auth) == 0 { groupData.Auth["type"] = "local" } - base.Debug(name + " auth type: " + fmt.Sprintf("%s", groupData.Auth["type"])) switch groupData.Auth["type"] { - case "local": + case "", "local": return checkLocalUser(name, pwd, group) case "radius": radisConf := AuthRadius{} From c38f1e9b8c811384f25482e663b99998a3c1b60d Mon Sep 17 00:00:00 2001 From: lanrenwo Date: Fri, 3 Jun 2022 07:26:41 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/dbdata/group.go | 6 +++--- server/dbdata/user.go | 30 +++++++++++++++++------------- web/src/pages/group/List.vue | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/server/dbdata/group.go b/server/dbdata/group.go index 831c194..4effbd2 100644 --- a/server/dbdata/group.go +++ b/server/dbdata/group.go @@ -205,9 +205,9 @@ func checkRadiusData(auth map[string]interface{}) error { if !ValidateIpPort(radisConf.Addr) { return errors.New("Radius的服务器地址填写有误") } - // freeradius官网最大8000字符, 这里限制800 - if len(radisConf.Secret) < 8 || len(radisConf.Secret) > 800 { - return errors.New("Radius的密钥长度需在8~800个字符之间") + // freeradius官网最大8000字符, 这里限制200 + if len(radisConf.Secret) < 8 || len(radisConf.Secret) > 200 { + return errors.New("Radius的密钥长度需在8~200个字符之间") } return nil } diff --git a/server/dbdata/user.go b/server/dbdata/user.go index 5f634f7..3db7461 100644 --- a/server/dbdata/user.go +++ b/server/dbdata/user.go @@ -86,16 +86,7 @@ func CheckUser(name, pwd, group string) error { case "", "local": return checkLocalUser(name, pwd, group) case "radius": - radisConf := AuthRadius{} - bodyBytes, err := json.Marshal(groupData.Auth["radius"]) - if err != nil { - fmt.Errorf("%s %s", name, "Radius出现Marshal错误") - } - err = json.Unmarshal(bodyBytes, &radisConf) - if err != nil { - fmt.Errorf("%s %s", name, "Radius出现Unmarshal错误") - } - return checkRadiusUser(name, pwd, radisConf) + return checkRadiusUser(name, pwd, groupData.Auth) default: return fmt.Errorf("%s %s", name, "无效的认证类型") } @@ -144,13 +135,26 @@ func checkLocalUser(name, pwd, group string) error { return nil } -func checkRadiusUser(name string, pwd string, raduisConf AuthRadius) error { - packet := radius.New(radius.CodeAccessRequest, []byte(raduisConf.Secret)) +func checkRadiusUser(name string, pwd string, auth map[string]interface{}) error { + if _, ok := auth["radius"]; !ok { + fmt.Errorf("%s %s", name, "Radius的radius值不存在") + } + radiusConf := AuthRadius{} + bodyBytes, err := json.Marshal(auth["radius"]) + if err != nil { + fmt.Errorf("%s %s", name, "Radius Marshal出现错误") + } + err = json.Unmarshal(bodyBytes, &radiusConf) + if err != nil { + fmt.Errorf("%s %s", name, "Radius Unmarshal出现错误") + } + // radius认证时,设置超时3秒 + packet := radius.New(radius.CodeAccessRequest, []byte(radiusConf.Secret)) rfc2865.UserName_SetString(packet, name) rfc2865.UserPassword_SetString(packet, pwd) ctx, done := context.WithTimeout(context.Background(), 3*time.Second) defer done() - response, err := radius.Exchange(ctx, packet, raduisConf.Addr) + response, err := radius.Exchange(ctx, packet, radiusConf.Addr) if err != nil { return fmt.Errorf("%s %s", name, "Radius服务器连接异常, 请检测服务器和端口") } diff --git a/web/src/pages/group/List.vue b/web/src/pages/group/List.vue index 8524c53..446584d 100644 --- a/web/src/pages/group/List.vue +++ b/web/src/pages/group/List.vue @@ -451,7 +451,7 @@ export default { arr.push({val: "", action: "allow", port: 0}); }, submitForm(formName) { - this.$refs[formName].validate((valid, obj) => { + this.$refs[formName].validate((valid) => { if (!valid) { console.log('error submit!!'); return false;