From 35ca753ad61d94584e76c6ebdee1b53de5ef0610 Mon Sep 17 00:00:00 2001 From: tickstep Date: Sun, 30 Jan 2022 10:36:01 +0800 Subject: [PATCH] add qrcode login api --- internal/functions/panlogin/login_helper.go | 193 ++++++++++++++++++ .../functions/panlogin/login_helper_test.go | 60 ++++++ 2 files changed, 253 insertions(+) create mode 100644 internal/functions/panlogin/login_helper.go create mode 100644 internal/functions/panlogin/login_helper_test.go diff --git a/internal/functions/panlogin/login_helper.go b/internal/functions/panlogin/login_helper.go new file mode 100644 index 0000000..dbc929c --- /dev/null +++ b/internal/functions/panlogin/login_helper.go @@ -0,0 +1,193 @@ +package panlogin + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/library-go/crypto" + "github.com/tickstep/library-go/getip" + "github.com/tickstep/library-go/ids" + "github.com/tickstep/library-go/logger" + "github.com/tickstep/library-go/requester" + "net/url" + "runtime" + "strings" + "time" +) + +type LoginHelper struct { + webHost string +} + +type LoginHttpResult struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +// QRCodeUrlResult 二维码登录页面 +type QRCodeUrlResult struct { + TokenId string `json:"tokenId"` + TokenUrl string `json:"tokenUrl"` + ExpiredTime int `json:"expiredTime"` +} + +// QRCodeLoginResult 二维码登录结果 +type QRCodeLoginResult struct { + QrCodeStatus string `json:"qrCodeStatus"` + SecureRefreshToken string `json:"secureRefreshToken"` +} + +func NewLoginHelper(webHost string) *LoginHelper { + return &LoginHelper{ + webHost: webHost, + } +} + +// GetQRCodeLoginUrl 获取登录二维码链接 +func (h *LoginHelper) GetQRCodeLoginUrl(keyStr string) (*QRCodeUrlResult, error) { + fullUrl := strings.Builder{} + ipAddr, err := getip.IPInfoFromTechainBaidu() + if err != nil { + ipAddr = "127.0.0.1" + } + fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/qrcode/create?ip=%s&os=%s&arch=%s&version=%s&key=%s", + h.webHost, ipAddr, runtime.GOOS, runtime.GOARCH, config.AppVersion, keyStr) + + logger.Verboseln("do request url: " + fullUrl.String()) + header := map[string]string{ + "accept": "application/json, text/plain, */*", + "content-type": "application/json;charset=UTF-8", + "user-agent": "aliyunpan/" + config.AppVersion, + } + // request + client := requester.NewHTTPClient() + client.SetTimeout(20 * time.Second) + client.SetKeepAlive(false) + body, err := client.Fetch("GET", fullUrl.String(), nil, header) + if err != nil { + logger.Verboseln("get qr code error ", err) + return nil, err + } + + // parse result + r := &LoginHttpResult{} + r.Data = &QRCodeUrlResult{} + if err2 := json.Unmarshal(body, r); err2 != nil { + logger.Verboseln("parse qrcode result json error ", err2) + return nil, err2 + } + if r.Code != 0 { + return nil, fmt.Errorf(r.Msg) + } + token := r.Data.(*QRCodeUrlResult) + if len(token.TokenUrl) > 0 { + u, err := url.Parse(token.TokenUrl) + if err != nil { + return nil, err + } + m, _ := url.ParseQuery(u.RawQuery) + token.TokenId = m["tokenId"][0] + } + return token, nil +} + +// GetQRCodeLoginResult 获取二维码登录结果 +func (h *LoginHelper) GetQRCodeLoginResult(tokenId string) (*QRCodeLoginResult, error) { + fullUrl := strings.Builder{} + fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/qrcode/result?tokenId=%s", + h.webHost, tokenId) + + logger.Verboseln("do request url: " + fullUrl.String()) + header := map[string]string{ + "accept": "application/json, text/plain, */*", + "content-type": "application/json;charset=UTF-8", + "user-agent": "aliyunpan/" + config.AppVersion, + } + // request + client := requester.NewHTTPClient() + client.SetTimeout(20 * time.Second) + client.SetKeepAlive(false) + body, err := client.Fetch("GET", fullUrl.String(), nil, header) + if err != nil { + logger.Verboseln("get qr code result error ", err) + return nil, err + } + + // parse result + r := &LoginHttpResult{} + r.Data = &QRCodeLoginResult{} + if err2 := json.Unmarshal(body, r); err2 != nil { + logger.Verboseln("parse qrcode result json error ", err2) + return nil, err2 + } + if r.Code != 0 { + return nil, fmt.Errorf(r.Msg) + } + return r.Data.(*QRCodeLoginResult), nil +} + +// GetRefreshToken 获取Token +func (h *LoginHelper) GetRefreshToken(tokenId string) (*QRCodeLoginResult, error) { + fullUrl := strings.Builder{} + fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/qrcode/retrieve?tokenId=%s", + h.webHost, tokenId) + logger.Verboseln("do request url: " + fullUrl.String()) + header := map[string]string{ + "accept": "application/json, text/plain, */*", + "content-type": "application/json;charset=UTF-8", + "user-agent": "aliyunpan/" + config.AppVersion, + } + // request + client := requester.NewHTTPClient() + client.SetTimeout(20 * time.Second) + client.SetKeepAlive(false) + body, err := client.Fetch("GET", fullUrl.String(), nil, header) + if err != nil { + logger.Verboseln("get refresh token result error ", err) + return nil, err + } + + // parse result + r := &LoginHttpResult{} + r.Data = &QRCodeLoginResult{} + if err2 := json.Unmarshal(body, r); err2 != nil { + logger.Verboseln("parse refresh token result json error ", err2) + return nil, err2 + } + if r.Code != 0 { + return nil, fmt.Errorf(r.Msg) + } + return r.Data.(*QRCodeLoginResult), nil +} + +// ParseSecureRefreshToken 解密Token +func (h *LoginHelper) ParseSecureRefreshToken(keyStr, secureRefreshToken string) (string, error) { + defer func() { + if err := recover(); err != nil { + logger.Verboseln("decrypt string failed, maybe the key has been changed") + } + }() + + if len(keyStr) == 0 { + keyStr = ids.GetUniqueId("", 32) + } + + if secureRefreshToken == "" { + return "", nil + } + d, _ := hex.DecodeString(secureRefreshToken) + + // use the machine unique id as the key + // but in some OS, this key will be changed if you reinstall the OS + key := []byte(keyStr) + if len(key) > 16 { + key = key[:16] + } + r, e := crypto.DecryptAES(d, key) + if e != nil { + return "", nil + } + return string(r), nil +} diff --git a/internal/functions/panlogin/login_helper_test.go b/internal/functions/panlogin/login_helper_test.go new file mode 100644 index 0000000..04ab829 --- /dev/null +++ b/internal/functions/panlogin/login_helper_test.go @@ -0,0 +1,60 @@ +// Copyright (c) 2020 tickstep. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package panlogin + +import ( + "fmt" + "github.com/tickstep/library-go/ids" + "testing" + "time" +) + +func TestGetQRCodeLoginUrl(t *testing.T) { + h := NewLoginHelper("http://localhost:8977") + keyStr := ids.GetUniqueId("", 32) + fmt.Println(keyStr) + r, e := h.GetQRCodeLoginUrl(keyStr) + fmt.Println(e) + fmt.Println(r) +} + +func TestGetQRCodeLoginResult(t *testing.T) { + h := NewLoginHelper("http://localhost:8977") + tokenId := "26e69f9978ba4574a1d66e58399fed4e" + for { + r, e := h.GetQRCodeLoginResult(tokenId) + fmt.Println(e) + fmt.Println(r) + time.Sleep(1 * time.Second) + if r.QrCodeStatus == "CONFIRMED" { + break + } + } +} + +func TestGetRefreshToken(t *testing.T) { + h := NewLoginHelper("http://localhost:8977") + tokenId := "26e69f9978ba4574a1d66e58399fed4e" + r, e := h.GetRefreshToken(tokenId) + fmt.Println(e) + fmt.Println(r) +} + +func TestParseRefreshToken(t *testing.T) { + h := NewLoginHelper("http://localhost:8977") + secureToken := "eff6054736d47b9c31f8839465555ebdff38c878ea0abbcd4b2336b30d33c71c7dac8824e40991654429ae521d8ef471" + r, e := h.ParseSecureRefreshToken("", secureToken) + fmt.Println(e) + fmt.Println(r) +}