package handlers import ( "log/slog" "proxy-server/server/pkg/orm" "proxy-server/server/pkg/resp" "proxy-server/server/web/models" "strings" "time" "github.com/gin-gonic/gin" "github.com/pkg/errors" ) // region frp 接口 type FrpData struct { Reject bool RejectReason string Unchange bool } func ChanRequest(c *gin.Context) { type Body struct { Content struct { ProxyName string `json:"proxy_name"` ProxyType string `json:"proxy_type"` RemoteAddr string `json:"remote_addr"` User interface{} } } op := c.Query("op") if op != "NewUserConn" { _ = c.Error(errors.New("不支持的操作")) return } id := c.GetHeader("X-Frp-Reqid") if id == "" { _ = c.Error(errors.New("请求头中缺少 X-Frp-Reqid")) return } var body Body err := c.ShouldBindJSON(&body) if err != nil { _ = c.Error(errors.Wrap(err, "解析请求正文失败")) return } content := body.Content // 检查此 ip 是否有权限访问目标 node clientIp := strings.Split(content.RemoteAddr, ":")[0] targetNode := content.ProxyName slog.Debug(id + " 用户 " + clientIp + " 请求连接到 " + targetNode) var channels []models.Channel err = orm.DB. Joins("INNER JOIN public.nodes n ON channels.node_id = n.id AND n.name = ?", targetNode). Joins("INNER JOIN public.users u ON channels.user_id = u.id"). Joins("INNER JOIN public.user_ips ip ON u.id = ip.user_id AND ip.ip_address = ?", clientIp). Find(&channels).Error if err != nil { _ = c.Error(errors.Wrap(err, "查询用户权限失败")) return } // 返回响应 rsCount := len(channels) if rsCount > 1 { slog.Warn(clientIp + " + " + targetNode + "的组合有多个权限结果,这是不应当存在的") } if rsCount == 0 { slog.Debug(id + " 没有权限") reject(c) return } channel := channels[0] if channel.Expiration.Before(time.Now()) { slog.Debug(id + " 权限已过期") reject(c) return } slog.Debug(id + " 通过验证") confirm(c) } func ChanTest(c *gin.Context) { var body map[string]interface{} err := c.ShouldBindJSON(&body) if err != nil { slog.Error("解析请求正文失败", err) } for k, v := range body { slog.Debug("map", "key: ", k, " value: ", v) } confirm(c) } func confirm(c *gin.Context) { c.JSON(200, FrpData{ Reject: false, Unchange: true, }) } func reject(c *gin.Context) { c.JSON(401, FrpData{ Reject: true, RejectReason: "客户端没有权限访问该节点", }) } // endregion func ChanAuth(c *gin.Context) { type Body struct { Username string `json:"username"` Password string `json:"password"` } type Data struct { Timeout uint64 `json:"timeout"` } var body Body err := c.ShouldBindJSON(&body) if err != nil { _ = c.Error(err) c.JSON(400, resp.Fail("请求参数错误")) return } // 查找通道 var result *models.Channel orm.DB. Model(&models.Channel{}). Where(&models.Channel{ Username: body.Username, Password: body.Password, }). First(&result) if result == nil { _ = c.Error(errors.New("用户信息不存在")) c.JSON(401, resp.Fail("账号密码错误")) return } // 验证账号密码 todo 哈希密码验证 if result.Username != body.Username || result.Password != body.Password { _ = c.Error(errors.New("账号密码错误")) c.JSON(401, resp.Fail("账号密码错误")) return } // 计算到期时间 timeout := result.Expiration.Sub(time.Now()) // todo 保存会话 对于大量短连接的情况,考虑如何保存连接会话信息 // 返回结果 c.JSON(200, resp.Done(Data{ Timeout: uint64(timeout.Seconds()), })) }