package main import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) // lzuapi.go - FasterLZU (appservice.lzu.edu.cn) 登录客户端 // 对应 Python lzuapi.py 中的 login() 与 get_st() const ( appServiceBase = "https://appservice.lzu.edu.cn" lzuUserAgent = "Mozilla/5.0 (Linux; Android 12; SM-S7110 Build/V417IR; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 lzdx_ua JHZF_LZDXAPP" ) // lzuClient FasterLZU API 客户端 type lzuClient struct { http *http.Client username string password string loginToken string gatewayToken string } func newLzuClient(username, password string) *lzuClient { return &lzuClient{ http: &http.Client{}, username: username, password: password, } } // encryptedHeaders 加密请求头 func encryptedHeaders() http.Header { h := http.Header{} h.Set("Content-Type", "application/json;charset=UTF-8") h.Set("Transfer-Encrypt", "true") h.Set("User-Agent", lzuUserAgent) return h } // Login 登录,返回 (loginToken, gatewayToken) func (c *lzuClient) Login() (string, string, error) { url := appServiceBase + "/api/eusp-unify-terminal/app-user/login" data := map[string]any{ "app_os": 2, "name": c.username, "pwd": c.password, } plaintext, _ := json.Marshal(data) // 无空格,与 Python separators=(",",":") 一致 ciphertext, err := aesEncrypt(string(plaintext)) if err != nil { return "", "", err } ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBufferString(ciphertext)) if err != nil { return "", "", err } req.Header = encryptedHeaders() resp, err := c.http.Do(req) if err != nil { return "", "", err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) decrypted, err := aesDecrypt(strings.TrimSpace(string(body))) if err != nil { return "", "", fmt.Errorf("登录响应解密失败: %w", err) } var res struct { Code int `json:"code"` Data struct { LoginToken string `json:"login_token"` GatewayToken string `json:"gateway_token"` } `json:"data"` } if err := json.Unmarshal([]byte(decrypted), &res); err != nil { return "", "", fmt.Errorf("登录响应解析失败: %w", err) } if res.Code != 1 { return "", "", fmt.Errorf("登录失败 code=%d", res.Code) } c.loginToken = res.Data.LoginToken c.gatewayToken = res.Data.GatewayToken return c.loginToken, c.gatewayToken, nil } // GetSt 获取服务令牌 ST func (c *lzuClient) GetSt(serviceID string) (string, error) { url := fmt.Sprintf("%s/api/eusp-unify-terminal/app-user/getSt?loginToken=%s&serviceId=%s&service=", appServiceBase, c.loginToken, serviceID) ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return "", err } req.Header = encryptedHeaders() resp, err := c.http.Do(req) if err != nil { return "", err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) decrypted, err := aesDecrypt(strings.TrimSpace(string(body))) if err != nil { return "", fmt.Errorf("getSt 响应解密失败: %w", err) } var res struct { Code int `json:"code"` Data string `json:"data"` } if err := json.Unmarshal([]byte(decrypted), &res); err != nil { return "", fmt.Errorf("getSt 响应解析失败: %w", err) } if res.Code != 1 { return "", fmt.Errorf("getSt 失败 code=%d", res.Code) } return res.Data, nil }