mirror of
https://github.com/tickstep/aliyunpan.git
synced 2025-01-23 22:42:15 +08:00
support QR Code login way
This commit is contained in:
parent
7e0d2155a8
commit
04e414b65d
@ -14,20 +14,18 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tickstep/aliyunpan-api/aliyunpan"
|
"github.com/tickstep/aliyunpan-api/aliyunpan"
|
||||||
"github.com/tickstep/aliyunpan-api/aliyunpan/apierror"
|
|
||||||
"github.com/tickstep/aliyunpan/cmder"
|
"github.com/tickstep/aliyunpan/cmder"
|
||||||
|
"github.com/tickstep/aliyunpan/cmder/cmdliner"
|
||||||
"github.com/tickstep/aliyunpan/internal/config"
|
"github.com/tickstep/aliyunpan/internal/config"
|
||||||
|
"github.com/tickstep/aliyunpan/internal/functions/panlogin"
|
||||||
"github.com/tickstep/library-go/logger"
|
"github.com/tickstep/library-go/logger"
|
||||||
"github.com/tickstep/library-go/requester"
|
|
||||||
_ "github.com/tickstep/library-go/requester"
|
_ "github.com/tickstep/library-go/requester"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func CmdLogin() cli.Command {
|
func CmdLogin() cli.Command {
|
||||||
return cli.Command{
|
return cli.Command{
|
||||||
Name: "login",
|
Name: "login",
|
||||||
@ -40,41 +38,24 @@ func CmdLogin() cli.Command {
|
|||||||
2.直接指定RefreshToken进行登录
|
2.直接指定RefreshToken进行登录
|
||||||
aliyunpan login -RefreshToken=8B12CBBCE89CA8DFC3445985B63B511B5E7EC7...
|
aliyunpan login -RefreshToken=8B12CBBCE89CA8DFC3445985B63B511B5E7EC7...
|
||||||
|
|
||||||
3.指定自行搭建的web服务,从指定的URL获取Token进行登录
|
3.使用二维码方式进行登录
|
||||||
aliyunpan login --RefreshTokenUrl "http://your.host.com/aliyunpan/token/refresh"
|
aliyunpan login -QrCode
|
||||||
|
|
||||||
web服务为GET请求,返回的响应体必须是JSON,格式要求如下所示,data内容即为token:
|
|
||||||
{
|
|
||||||
"code": "0",
|
|
||||||
"msg": "ok",
|
|
||||||
"data": "88771cd41a111521b4471a552bf633ba"
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
Category: "阿里云盘账号",
|
Category: "阿里云盘账号",
|
||||||
Before: cmder.ReloadConfigFunc, // 每次进行登录动作的时候需要调用刷新配置
|
Before: cmder.ReloadConfigFunc, // 每次进行登录动作的时候需要调用刷新配置
|
||||||
After: cmder.SaveConfigFunc, // 登录完成需要调用保存配置
|
After: cmder.SaveConfigFunc, // 登录完成需要调用保存配置
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
// 优先从web服务获取token
|
|
||||||
refreshTokenStr := ""
|
refreshTokenStr := ""
|
||||||
if c.String("RefreshTokenUrl") != "" {
|
|
||||||
refreshTokenUrl := c.String("RefreshTokenUrl")
|
|
||||||
ts, e := getRefreshTokenFromWebServer(refreshTokenUrl)
|
|
||||||
if e != nil {
|
|
||||||
fmt.Println("从web服务获取Token失败")
|
|
||||||
} else if ts != "" {
|
|
||||||
fmt.Println("成功从web服务获取Token:" + ts)
|
|
||||||
refreshTokenStr = ts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if refreshTokenStr == "" {
|
if refreshTokenStr == "" {
|
||||||
refreshTokenStr = c.String("RefreshToken")
|
refreshTokenStr = c.String("RefreshToken")
|
||||||
}
|
}
|
||||||
|
useQrCode := c.Bool("QrCode")
|
||||||
|
|
||||||
|
tokenId := ""
|
||||||
webToken := aliyunpan.WebLoginToken{}
|
webToken := aliyunpan.WebLoginToken{}
|
||||||
refreshToken := ""
|
refreshToken := ""
|
||||||
var err error
|
var err error
|
||||||
refreshToken, webToken, err = RunLogin(refreshTokenStr)
|
tokenId, refreshToken, webToken, err = RunLogin(useQrCode, refreshTokenStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
@ -86,20 +67,21 @@ func CmdLogin() cli.Command {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cloudUser.RefreshToken = refreshToken
|
cloudUser.RefreshToken = refreshToken
|
||||||
|
cloudUser.TokenId = tokenId
|
||||||
config.Config.SetActiveUser(cloudUser)
|
config.Config.SetActiveUser(cloudUser)
|
||||||
fmt.Println("阿里云盘登录成功: ", cloudUser.Nickname)
|
fmt.Println("阿里云盘登录成功: ", cloudUser.Nickname)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
// 命令的附加options参数说明,使用 help login 命令即可查看
|
// 命令的附加options参数说明,使用 help panlogin 命令即可查看
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
// aliyunpan login -RefreshToken=8B12CBBCE89CA8DFC3445985B63B511B5E7EC7...
|
// aliyunpan panlogin -RefreshToken=8B12CBBCE89CA8DFC3445985B63B511B5E7EC7...
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "RefreshToken",
|
Name: "RefreshToken",
|
||||||
Usage: "使用RefreshToken Cookie来登录帐号",
|
Usage: "使用RefreshToken Cookie来登录帐号",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.BoolFlag{
|
||||||
Name: "RefreshTokenUrl",
|
Name: "QrCode",
|
||||||
Usage: "使用自行搭建的web服务获取RefreshToken来进行登录",
|
Usage: "使用二维码登录",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -149,44 +131,53 @@ func CmdLogout() cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunLogin(refreshToken string) (refreshTokenStr string, webToken aliyunpan.WebLoginToken, error error) {
|
func RunLogin(useQrCodeLogin bool, refreshToken string) (tokenId, refreshTokenStr string, webToken aliyunpan.WebLoginToken, error error) {
|
||||||
return cmder.DoLoginHelper(refreshToken)
|
if useQrCodeLogin {
|
||||||
}
|
h := panlogin.NewLoginHelper("http://localhost:8977")
|
||||||
|
qrCodeUrlResult, err := h.GetQRCodeLoginUrl("")
|
||||||
|
if err != nil {
|
||||||
// getRefreshTokenFromWebServer 从自定义的web服务获取token
|
fmt.Println("二维码登录错误:", err)
|
||||||
func getRefreshTokenFromWebServer(url string) (string, error) {
|
return "", "", aliyunpan.WebLoginToken{}, err
|
||||||
type tokenResult struct {
|
}
|
||||||
Code string `json:"code"`
|
fmt.Printf("请在浏览器打开以下链接进行扫码登录,链接有效时间为5分钟\n%s\n\n", qrCodeUrlResult.TokenUrl)
|
||||||
Msg string `json:"msg"`
|
|
||||||
Data string `json:"data"`
|
// handler waiting
|
||||||
}
|
line := cmdliner.NewLiner()
|
||||||
|
var qrCodeLoginResult *panlogin.QRCodeLoginResult
|
||||||
if url == "" {
|
defer line.Close()
|
||||||
return "", fmt.Errorf("url is empty")
|
|
||||||
}
|
go func() {
|
||||||
|
for {
|
||||||
logger.Verboseln("do request url: " + url)
|
time.Sleep(3 * time.Second)
|
||||||
header := map[string]string {
|
qr, er := h.GetQRCodeLoginResult(qrCodeUrlResult.TokenId)
|
||||||
"accept": "application/json, text/plain, */*",
|
if er != nil {
|
||||||
"content-type": "application/json;charset=UTF-8",
|
continue
|
||||||
"user-agent": "aliyunpan/" + config.AppVersion,
|
}
|
||||||
}
|
logger.Verboseln(qr)
|
||||||
// request
|
if qr.QrCodeStatus == "CONFIRMED" {
|
||||||
client := requester.NewHTTPClient()
|
// login successfully
|
||||||
client.SetTimeout(10 * time.Second)
|
qrCodeLoginResult = qr
|
||||||
client.SetKeepAlive(false)
|
break
|
||||||
body, err := client.Fetch("GET", url, nil, header)
|
} else if qr.QrCodeStatus == "EXPIRED" {
|
||||||
if err != nil {
|
break
|
||||||
logger.Verboseln("get token error ", err)
|
}
|
||||||
return "", err
|
}
|
||||||
}
|
}()
|
||||||
|
|
||||||
// parse result
|
line.State.Prompt("请在浏览器里面完成扫码登录,然后再按Enter键继续...")
|
||||||
r := &tokenResult{}
|
if qrCodeLoginResult == nil {
|
||||||
if err2 := json.Unmarshal(body, r); err2 != nil {
|
return "", "", aliyunpan.WebLoginToken{}, fmt.Errorf("二维码登录失败")
|
||||||
logger.Verboseln("parse token info result json error ", err2)
|
}
|
||||||
return "", apierror.NewFailedApiError(err2.Error())
|
|
||||||
}
|
tokenStr, er := h.ParseSecureRefreshToken("", qrCodeLoginResult.SecureRefreshToken)
|
||||||
return r.Data, nil
|
if er != nil {
|
||||||
|
fmt.Println("解析Token错误:", er)
|
||||||
|
return "", "", aliyunpan.WebLoginToken{}, er
|
||||||
|
}
|
||||||
|
refreshToken = tokenStr
|
||||||
|
tokenId = qrCodeUrlResult.TokenId
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshTokenStr, webToken, error = cmder.DoLoginHelper(refreshToken)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,12 @@ import (
|
|||||||
type DriveInfo struct {
|
type DriveInfo struct {
|
||||||
DriveId string `json:"driveId"`
|
DriveId string `json:"driveId"`
|
||||||
DriveName string `json:"driveName"`
|
DriveName string `json:"driveName"`
|
||||||
DriveTag string `json:"driveTag"`
|
DriveTag string `json:"driveTag"`
|
||||||
}
|
}
|
||||||
type DriveInfoList []*DriveInfo
|
type DriveInfoList []*DriveInfo
|
||||||
|
|
||||||
func (d DriveInfoList) GetFileDriveId() string {
|
func (d DriveInfoList) GetFileDriveId() string {
|
||||||
for _,drive := range d {
|
for _, drive := range d {
|
||||||
if drive.DriveTag == "File" {
|
if drive.DriveTag == "File" {
|
||||||
return drive.DriveId
|
return drive.DriveId
|
||||||
}
|
}
|
||||||
@ -41,22 +41,23 @@ func (d DriveInfoList) GetFileDriveId() string {
|
|||||||
|
|
||||||
type PanUser struct {
|
type PanUser struct {
|
||||||
UserId string `json:"userId"`
|
UserId string `json:"userId"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
AccountName string `json:"accountName"`
|
AccountName string `json:"accountName"`
|
||||||
|
|
||||||
Workdir string `json:"workdir"`
|
Workdir string `json:"workdir"`
|
||||||
WorkdirFileEntity aliyunpan.FileEntity `json:"workdirFileEntity"`
|
WorkdirFileEntity aliyunpan.FileEntity `json:"workdirFileEntity"`
|
||||||
|
|
||||||
AlbumWorkdir string `json:"albumWorkdir"`
|
AlbumWorkdir string `json:"albumWorkdir"`
|
||||||
AlbumWorkdirFileEntity aliyunpan.FileEntity `json:"albumWorkdirFileEntity"`
|
AlbumWorkdirFileEntity aliyunpan.FileEntity `json:"albumWorkdirFileEntity"`
|
||||||
|
|
||||||
ActiveDriveId string `json:"activeDriveId"`
|
ActiveDriveId string `json:"activeDriveId"`
|
||||||
DriveList DriveInfoList `json:"driveList"`
|
DriveList DriveInfoList `json:"driveList"`
|
||||||
|
|
||||||
RefreshToken string `json:"refreshToken"`
|
RefreshToken string `json:"refreshToken"`
|
||||||
WebToken aliyunpan.WebLoginToken `json:"webToken"`
|
WebToken aliyunpan.WebLoginToken `json:"webToken"`
|
||||||
|
TokenId string `json:"tokenId"`
|
||||||
|
|
||||||
panClient *aliyunpan.PanClient
|
panClient *aliyunpan.PanClient
|
||||||
cacheOpMap cachemap.CacheOpMap
|
cacheOpMap cachemap.CacheOpMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,9 +73,9 @@ func SetupUserByCookie(webToken *aliyunpan.WebLoginToken) (user *PanUser, err *a
|
|||||||
doLoginAct:
|
doLoginAct:
|
||||||
panClient := aliyunpan.NewPanClient(*webToken, aliyunpan.AppLoginToken{})
|
panClient := aliyunpan.NewPanClient(*webToken, aliyunpan.AppLoginToken{})
|
||||||
u := &PanUser{
|
u := &PanUser{
|
||||||
WebToken: *webToken,
|
WebToken: *webToken,
|
||||||
panClient: panClient,
|
panClient: panClient,
|
||||||
Workdir: "/",
|
Workdir: "/",
|
||||||
WorkdirFileEntity: *aliyunpan.NewFileEntityForRootDir(),
|
WorkdirFileEntity: *aliyunpan.NewFileEntityForRootDir(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ doLoginAct:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Code == apierror.ApiCodeTokenExpiredCode && tryRefreshWebToken {
|
if err.Code == apierror.ApiCodeTokenExpiredCode && tryRefreshWebToken {
|
||||||
tryRefreshWebToken = false
|
tryRefreshWebToken = false
|
||||||
webCookie,_ := aliyunpan.GetAccessTokenFromRefreshToken(webToken.RefreshToken)
|
webCookie, _ := aliyunpan.GetAccessTokenFromRefreshToken(webToken.RefreshToken)
|
||||||
if webCookie != nil {
|
if webCookie != nil {
|
||||||
webToken = webCookie
|
webToken = webCookie
|
||||||
goto doLoginAct
|
goto doLoginAct
|
||||||
@ -171,7 +172,7 @@ func (pu *PanUser) GetSavePath(filePanPath string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pu *PanUser) GetDriveByTag(tag string) *DriveInfo {
|
func (pu *PanUser) GetDriveByTag(tag string) *DriveInfo {
|
||||||
for _,item := range pu.DriveList {
|
for _, item := range pu.DriveList {
|
||||||
if item.DriveTag == tag {
|
if item.DriveTag == tag {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
@ -180,7 +181,7 @@ func (pu *PanUser) GetDriveByTag(tag string) *DriveInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pu *PanUser) GetDriveById(id string) *DriveInfo {
|
func (pu *PanUser) GetDriveById(id string) *DriveInfo {
|
||||||
for _,item := range pu.DriveList {
|
for _, item := range pu.DriveList {
|
||||||
if item.DriveId == id {
|
if item.DriveId == id {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
@ -189,7 +190,7 @@ func (pu *PanUser) GetDriveById(id string) *DriveInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pu *PanUser) GetActiveDriveInfo() *DriveInfo {
|
func (pu *PanUser) GetActiveDriveInfo() *DriveInfo {
|
||||||
for _,item := range pu.DriveList {
|
for _, item := range pu.DriveList {
|
||||||
if item.DriveId == pu.ActiveDriveId {
|
if item.DriveId == pu.ActiveDriveId {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
@ -213,4 +214,4 @@ func (di *DriveInfo) IsFileDrive() bool {
|
|||||||
|
|
||||||
func (di *DriveInfo) IsAlbumDrive() bool {
|
func (di *DriveInfo) IsAlbumDrive() bool {
|
||||||
return di.DriveTag == "Album"
|
return di.DriveTag == "Album"
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,9 @@ func NewLoginHelper(webHost string) *LoginHelper {
|
|||||||
|
|
||||||
// GetQRCodeLoginUrl 获取登录二维码链接
|
// GetQRCodeLoginUrl 获取登录二维码链接
|
||||||
func (h *LoginHelper) GetQRCodeLoginUrl(keyStr string) (*QRCodeUrlResult, error) {
|
func (h *LoginHelper) GetQRCodeLoginUrl(keyStr string) (*QRCodeUrlResult, error) {
|
||||||
|
if keyStr == "" {
|
||||||
|
keyStr = ids.GetUniqueId("", 32)
|
||||||
|
}
|
||||||
fullUrl := strings.Builder{}
|
fullUrl := strings.Builder{}
|
||||||
ipAddr, err := getip.IPInfoFromTechainBaidu()
|
ipAddr, err := getip.IPInfoFromTechainBaidu()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -104,11 +104,11 @@ func ParseVersionNum(versionStr string) int {
|
|||||||
versionStr = strings.ReplaceAll(versionStr, "-dev", "")
|
versionStr = strings.ReplaceAll(versionStr, "-dev", "")
|
||||||
versionStr = strings.ReplaceAll(versionStr, "v", "")
|
versionStr = strings.ReplaceAll(versionStr, "v", "")
|
||||||
versionParts := strings.Split(versionStr, ".")
|
versionParts := strings.Split(versionStr, ".")
|
||||||
verNum := parseInt(versionParts[0]) * 1e4 + parseInt(versionParts[1]) * 1e2 + parseInt(versionParts[2])
|
verNum := parseInt(versionParts[0])*1e4 + parseInt(versionParts[1])*1e2 + parseInt(versionParts[2])
|
||||||
return verNum
|
return verNum
|
||||||
}
|
}
|
||||||
func parseInt(numStr string) int {
|
func parseInt(numStr string) int {
|
||||||
num,e := strconv.Atoi(numStr)
|
num, e := strconv.Atoi(numStr)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -117,7 +117,10 @@ func parseInt(numStr string) int {
|
|||||||
|
|
||||||
func ConvertTime(t time.Duration) string {
|
func ConvertTime(t time.Duration) string {
|
||||||
seconds := int64(t.Seconds())
|
seconds := int64(t.Seconds())
|
||||||
|
return ConvertTimeSecond(seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertTimeSecond(seconds int64) string {
|
||||||
MT := int64(1 * 60)
|
MT := int64(1 * 60)
|
||||||
HT := int64(1 * 60 * 60)
|
HT := int64(1 * 60 * 60)
|
||||||
|
|
||||||
@ -128,22 +131,22 @@ func ConvertTime(t time.Duration) string {
|
|||||||
return fmt.Sprintf("%d秒", seconds)
|
return fmt.Sprintf("%d秒", seconds)
|
||||||
}
|
}
|
||||||
if seconds >= MT && seconds < HT {
|
if seconds >= MT && seconds < HT {
|
||||||
return fmt.Sprintf("%d分%d秒", seconds / MT, seconds % MT)
|
return fmt.Sprintf("%d分%d秒", seconds/MT, seconds%MT)
|
||||||
}
|
}
|
||||||
if seconds >= HT {
|
if seconds >= HT {
|
||||||
h := seconds / HT
|
h := seconds / HT
|
||||||
tmp := seconds % HT
|
tmp := seconds % HT
|
||||||
return fmt.Sprintf("%d小时%d分%d秒", h, tmp / MT, tmp % MT)
|
return fmt.Sprintf("%d小时%d分%d秒", h, tmp/MT, tmp%MT)
|
||||||
}
|
}
|
||||||
return "0秒"
|
return "0秒"
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSuffix 判断是否以某字符串作为结尾
|
// HasSuffix 判断是否以某字符串作为结尾
|
||||||
func HasSuffix(s ,suffix string) bool {
|
func HasSuffix(s, suffix string) bool {
|
||||||
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPrefix 判断是否以某字符串作为开始
|
// HasPrefix 判断是否以某字符串作为开始
|
||||||
func HasPrefix(s, prefix string) bool {
|
func HasPrefix(s, prefix string) bool {
|
||||||
return len(s) >= len(prefix) && s[0:len(prefix)]== prefix
|
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user