From d89c2a502de24141810de91ed542ae57f0660102 Mon Sep 17 00:00:00 2001 From: wsczx Date: Wed, 20 Aug 2025 00:51:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=81=E4=B9=A6=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=B2=A1=E6=9C=89=E4=BC=A0=E5=85=A5=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=BB=84=E7=9A=84Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/admin/api_cert.go | 50 +++++++++++++++++++++++++++++- server/admin/server.go | 1 + server/dbdata/cert_client.go | 25 +++++++++------ server/handler/link_auth.go | 9 ++++++ web/src/pages/set/Other.vue | 60 ++++++++++++++++++++++++++++-------- 5 files changed, 122 insertions(+), 23 deletions(-) diff --git a/server/admin/api_cert.go b/server/admin/api_cert.go index e4c9af3..c973231 100644 --- a/server/admin/api_cert.go +++ b/server/admin/api_cert.go @@ -131,6 +131,11 @@ func GenerateClientCert(w http.ResponseWriter, r *http.Request) { RespError(w, RespInternalErr, "用户名不能为空") return } + groupname := r.FormValue("group_name") + if groupname == "" { + RespError(w, RespInternalErr, "用户组不能为空") + return + } // 检查用户是否存在 user := &dbdata.User{} @@ -141,7 +146,7 @@ func GenerateClientCert(w http.ResponseWriter, r *http.Request) { } // 生成客户端证书 - certData, err := dbdata.GenerateClientCert(username) + certData, err := dbdata.GenerateClientCert(username, groupname) if err != nil { RespError(w, RespInternalErr, fmt.Sprintf("证书生成失败: %v", err)) return @@ -305,3 +310,46 @@ func GetClientCertList(w http.ResponseWriter, r *http.Request) { RespSucess(w, data) } + +// UserCertInfo 获取用户证书生成所需信息 +func UserCertInfo(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + + // 获取所有启用的用户 + var users []dbdata.User + err := dbdata.Find(&users, 1000, 1) + if err != nil && !dbdata.CheckErrNotFound(err) { + RespError(w, RespInternalErr, err) + return + } + + // 获取所有启用的组 + var groups []dbdata.Group + err = dbdata.Find(&groups, 1000, 1) + if err != nil && !dbdata.CheckErrNotFound(err) { + RespError(w, RespInternalErr, err) + return + } + + // 过滤启用的用户和组 + activeUsers := make([]dbdata.User, 0) + for _, user := range users { + if user.Status == 1 { + activeUsers = append(activeUsers, user) + } + } + + activeGroups := make([]dbdata.Group, 0) + for _, group := range groups { + if group.Status == 1 { + activeGroups = append(activeGroups, group) + } + } + + data := map[string]any{ + "users": activeUsers, + "groups": activeGroups, + } + + RespSucess(w, data) +} diff --git a/server/admin/server.go b/server/admin/server.go index 6e2641b..a43caf1 100644 --- a/server/admin/server.go +++ b/server/admin/server.go @@ -67,6 +67,7 @@ func StartAdmin() { // r.HandleFunc("/set/client_cert/enable", EnableClientCert) // r.HandleFunc("/set/client_cert/disable", DisableClientCert) r.HandleFunc("/set/client_cert/delete", DeleteClientCert) + r.HandleFunc("/set/client_cert/user_cert_info", UserCertInfo) r.HandleFunc("/user/list", UserList) r.HandleFunc("/user/detail", UserDetail) diff --git a/server/dbdata/cert_client.go b/server/dbdata/cert_client.go index b416ccf..e807a45 100644 --- a/server/dbdata/cert_client.go +++ b/server/dbdata/cert_client.go @@ -21,9 +21,9 @@ import ( // 客户端证书数据结构 type ClientCertData struct { - Id int `json:"id" xorm:"pk autoincr not null"` - Username string `json:"username" xorm:"varchar(60) not null"` - // GroupName string `json:"group_name" xorm:"varchar(60)"` + Id int `json:"id" xorm:"pk autoincr not null"` + Username string `json:"username" xorm:"varchar(60) not null"` + GroupName string `json:"groupname" xorm:"varchar(60)"` Certificate string `json:"certificate" xorm:"text not null"` PrivateKey string `json:"private_key" xorm:"text not null"` SerialNumber string `json:"serial_number" xorm:"varchar(100) not null"` @@ -177,7 +177,7 @@ func GenerateClientCA() error { } // 生成客户端证书并保存到数据库 -func GenerateClientCert(username string) (*ClientCertData, error) { +func GenerateClientCert(username, groupname string) (*ClientCertData, error) { // 检查是否已存在证书记录 _, err := GetClientCert(username) if err != nil { @@ -204,11 +204,12 @@ func GenerateClientCert(username string) (*ClientCertData, error) { template := x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), Subject: pkix.Name{ - CommonName: username, - Organization: []string{"AnyLink VPN"}, - Country: []string{"CN"}, - Province: []string{"Beijing"}, - Locality: []string{"Beijing"}, + CommonName: username, + OrganizationalUnit: []string{groupname}, + Organization: []string{"AnyLink VPN"}, + Country: []string{"CN"}, + Province: []string{"Beijing"}, + Locality: []string{"Beijing"}, }, NotBefore: time.Now(), NotAfter: time.Now().Add(time.Hour * 24 * 365), // 1年有效期 @@ -232,6 +233,7 @@ func GenerateClientCert(username string) (*ClientCertData, error) { // 保存到数据库 clientCertData := &ClientCertData{ Username: username, + GroupName: groupname, Certificate: string(certPEM), PrivateKey: string(keyPEM), SerialNumber: template.SerialNumber.String(), @@ -319,6 +321,11 @@ func ValidateClientCert(cert *x509.Certificate, userAgent string) bool { return false } + if clientCertData.GroupName != cert.Subject.OrganizationalUnit[0] { + base.Error("证书验证失败:证书组名与用户组名不匹配") + return false + } + // 检查证书状态 if clientCertData.GetStatus() != CertStatusActive { base.Error("证书验证失败:证书状态为", clientCertData.GetStatusText()) diff --git a/server/handler/link_auth.go b/server/handler/link_auth.go index 6d5da28..fdd6a06 100644 --- a/server/handler/link_auth.go +++ b/server/handler/link_auth.go @@ -81,13 +81,22 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) { if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { clientCert := r.TLS.PeerCertificates[0] username := clientCert.Subject.CommonName + groupname := clientCert.Subject.OrganizationalUnit[0] + if username == "" || groupname == "" { + base.Warn("客户端证书缺少用户名或组名") + w.WriteHeader(http.StatusBadRequest) + return + } // 验证证书有效性和用户状态 if dbdata.ValidateClientCert(clientCert, userAgent) { // 证书认证成功,创建会话 base.Info("用户通过证书认证:", username) + sessionData.ClientRequest.GroupSelect = groupname + sessionData.ClientRequest.Auth.Username = username ua.Username = username + ua.GroupName = groupname ua.Info = "用户通过证书认证登录" ua.Status = dbdata.UserConnected dbdata.UserActLogIns.Add(*ua, userAgent) diff --git a/web/src/pages/set/Other.vue b/web/src/pages/set/Other.vue index 66d7f89..0a928fa 100644 --- a/web/src/pages/set/Other.vue +++ b/web/src/pages/set/Other.vue @@ -130,16 +130,24 @@ + + default-first-option style="width: 100%;" @change="onUserChange"> + + + + + + + 取消 确定生成 @@ -157,9 +165,10 @@ + - +