fix(pikpak): modify the processing logic of CaptchaToken (#7024)

pull/6981/head
YangXu 2024-08-18 23:26:29 +08:00 committed by GitHub
parent 69e5b66b50
commit e238b90836
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 197 additions and 89 deletions

View File

@ -5,6 +5,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/alist-org/alist/v3/internal/op" "github.com/alist-org/alist/v3/internal/op"
"golang.org/x/oauth2"
"io"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -28,6 +30,7 @@ type PikPak struct {
*Common *Common
RefreshToken string RefreshToken string
AccessToken string AccessToken string
oauth2Token oauth2.TokenSource
} }
func (d *PikPak) Config() driver.Config { func (d *PikPak) Config() driver.Config {
@ -39,10 +42,6 @@ func (d *PikPak) GetAddition() driver.Additional {
} }
func (d *PikPak) Init(ctx context.Context) (err error) { func (d *PikPak) Init(ctx context.Context) (err error) {
if d.ClientID == "" || d.ClientSecret == "" {
d.ClientID = "YNxT9w7GMdWvEOKa"
d.ClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
}
if d.Common == nil { if d.Common == nil {
d.Common = &Common{ d.Common = &Common{
@ -50,7 +49,7 @@ func (d *PikPak) Init(ctx context.Context) (err error) {
CaptchaToken: "", CaptchaToken: "",
UserID: "", UserID: "",
DeviceID: utils.GetMD5EncodeStr(d.Username + d.Password), DeviceID: utils.GetMD5EncodeStr(d.Username + d.Password),
UserAgent: BuildCustomUserAgent(utils.GetMD5EncodeStr(d.Username+d.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, ""), UserAgent: "",
RefreshCTokenCk: func(token string) { RefreshCTokenCk: func(token string) {
d.Common.CaptchaToken = token d.Common.CaptchaToken = token
op.MustSaveDriverStorage(d) op.MustSaveDriverStorage(d)
@ -58,29 +57,84 @@ func (d *PikPak) Init(ctx context.Context) (err error) {
} }
} }
if d.Platform == "android" {
d.ClientID = AndroidClientID
d.ClientSecret = AndroidClientSecret
d.ClientVersion = AndroidClientVersion
d.PackageName = AndroidPackageName
d.Algorithms = AndroidAlgorithms
d.UserAgent = BuildCustomUserAgent(utils.GetMD5EncodeStr(d.Username+d.Password), AndroidClientID, AndroidPackageName, AndroidSdkVersion, AndroidClientVersion, AndroidPackageName, "")
} else if d.Platform == "web" {
d.ClientID = WebClientID
d.ClientSecret = WebClientSecret
d.ClientVersion = WebClientVersion
d.PackageName = WebPackageName
d.Algorithms = WebAlgorithms
d.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
}
if d.Addition.CaptchaToken != "" && d.Addition.RefreshToken == "" { if d.Addition.CaptchaToken != "" && d.Addition.RefreshToken == "" {
d.SetCaptchaToken(d.Addition.CaptchaToken) d.SetCaptchaToken(d.Addition.CaptchaToken)
} }
// 如果已经有RefreshToken直接刷新AccessToken if d.Addition.DeviceID != "" {
if d.Addition.RefreshToken != "" { d.SetDeviceID(d.Addition.DeviceID)
d.RefreshToken = d.Addition.RefreshToken
if err := d.refreshToken(); err != nil {
return err
}
} else { } else {
d.Addition.DeviceID = d.Common.DeviceID
op.MustSaveDriverStorage(d)
}
// 初始化 oauth2Config
oauth2Config := &oauth2.Config{
ClientID: d.ClientID,
ClientSecret: d.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: "https://user.mypikpak.com/v1/auth/signin",
TokenURL: "https://user.mypikpak.com/v1/auth/token",
AuthStyle: oauth2.AuthStyleInParams,
},
}
// 如果已经有RefreshToken直接获取AccessToken
if d.Addition.RefreshToken != "" {
// 使用 oauth2 刷新令牌
// 初始化 oauth2Token
d.oauth2Token = oauth2.ReuseTokenSource(nil, utils.TokenSource(func() (*oauth2.Token, error) {
return oauth2Config.TokenSource(ctx, &oauth2.Token{
RefreshToken: d.Addition.RefreshToken,
}).Token()
}))
} else {
// 如果没有填写RefreshToken尝试登录 获取 refreshToken
if err := d.login(); err != nil { if err := d.login(); err != nil {
return err return err
} }
d.oauth2Token = oauth2.ReuseTokenSource(nil, utils.TokenSource(func() (*oauth2.Token, error) {
return oauth2Config.TokenSource(ctx, &oauth2.Token{
RefreshToken: d.RefreshToken,
}).Token()
}))
} }
token, err := d.oauth2Token.Token()
if err != nil {
return err
}
d.RefreshToken = token.RefreshToken
d.AccessToken = token.AccessToken
// 获取CaptchaToken // 获取CaptchaToken
err = d.RefreshCaptchaTokenAtLogin(GetAction(http.MethodGet, "https://api-drive.mypikpak.com/drive/v1/files"), d.Common.UserID) err = d.RefreshCaptchaTokenAtLogin(GetAction(http.MethodGet, "https://api-drive.mypikpak.com/drive/v1/files"), d.Common.UserID)
if err != nil { if err != nil {
return err return err
} }
// 更新UserAgent // 更新UserAgent
d.Common.UserAgent = BuildCustomUserAgent(d.Common.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, d.Common.UserID) if d.Platform == "android" {
d.Common.UserAgent = BuildCustomUserAgent(utils.GetMD5EncodeStr(d.Username+d.Password), AndroidClientID, AndroidPackageName, AndroidSdkVersion, AndroidClientVersion, AndroidPackageName, d.Common.UserID)
}
// 保存 有效的 RefreshToken
d.Addition.RefreshToken = d.RefreshToken
op.MustSaveDriverStorage(d)
return nil return nil
} }
@ -100,8 +154,18 @@ func (d *PikPak) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
func (d *PikPak) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *PikPak) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp File var resp File
_, err := d.request(fmt.Sprintf("https://api-drive.mypikpak.com/drive/v1/files/%s?_magic=2021&thumbnail_size=SIZE_LARGE", file.GetID()), queryParams := map[string]string{
http.MethodGet, nil, &resp) "_magic": "2021",
"usage": "FETCH",
"thumbnail_size": "SIZE_LARGE",
}
if !d.DisableMediaLink {
queryParams["usage"] = "CACHE"
}
_, err := d.request(fmt.Sprintf("https://api-drive.mypikpak.com/drive/v1/files/%s", file.GetID()),
http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(queryParams)
}, &resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -224,7 +288,7 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
input := &s3manager.UploadInput{ input := &s3manager.UploadInput{
Bucket: &params.Bucket, Bucket: &params.Bucket,
Key: &params.Key, Key: &params.Key,
Body: stream, Body: io.TeeReader(stream, driver.NewProgress(stream.GetSize(), up)),
} }
_, err = uploader.UploadWithContext(ctx, input) _, err = uploader.UploadWithContext(ctx, input)
return err return err

View File

@ -9,11 +9,11 @@ type Addition struct {
driver.RootID driver.RootID
Username string `json:"username" required:"true"` Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"` Password string `json:"password" required:"true"`
ClientID string `json:"client_id" required:"true" default:"YNxT9w7GMdWvEOKa"` Platform string `json:"platform" required:"true" type:"select" options:"android,web"`
ClientSecret string `json:"client_secret" required:"true" default:"dbw2OtmVEeuUvIptb1Coyg"`
RefreshToken string `json:"refresh_token" required:"true" default:""` RefreshToken string `json:"refresh_token" required:"true" default:""`
CaptchaToken string `json:"captcha_token" default:""` CaptchaToken string `json:"captcha_token" default:""`
DisableMediaLink bool `json:"disable_media_link"` DeviceID string `json:"device_id" required:"false" default:""`
DisableMediaLink bool `json:"disable_media_link" default:"true"`
} }
var config = driver.Config{ var config = driver.Config{

View File

@ -20,7 +20,7 @@ import (
// do others that not defined in Driver interface // do others that not defined in Driver interface
var Algorithms = []string{ var AndroidAlgorithms = []string{
"Gez0T9ijiI9WCeTsKSg3SMlx", "Gez0T9ijiI9WCeTsKSg3SMlx",
"zQdbalsolyb1R/", "zQdbalsolyb1R/",
"ftOjr52zt51JD68C3s", "ftOjr52zt51JD68C3s",
@ -38,27 +38,54 @@ var Algorithms = []string{
"zVof5yaJkPe3VFpadPof", "zVof5yaJkPe3VFpadPof",
} }
var WebAlgorithms = []string{
"C9qPpZLN8ucRTaTiUMWYS9cQvWOE",
"+r6CQVxjzJV6LCV",
"F",
"pFJRC",
"9WXYIDGrwTCz2OiVlgZa90qpECPD6olt",
"/750aCr4lm/Sly/c",
"RB+DT/gZCrbV",
"",
"CyLsf7hdkIRxRm215hl",
"7xHvLi2tOYP0Y92b",
"ZGTXXxu8E/MIWaEDB+Sm/",
"1UI3",
"E7fP5Pfijd+7K+t6Tg/NhuLq0eEUVChpJSkrKxpO",
"ihtqpG6FMt65+Xk+tWUH2",
"NhXXU9rg4XXdzo7u5o",
}
const ( const (
ClientID = "YNxT9w7GMdWvEOKa" AndroidClientID = "YNxT9w7GMdWvEOKa"
ClientSecret = "dbw2OtmVEeuUvIptb1Coyg" AndroidClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
ClientVersion = "1.47.1" AndroidClientVersion = "1.47.1"
PackageName = "com.pikcloud.pikpak" AndroidPackageName = "com.pikcloud.pikpak"
SdkVersion = "2.0.4.204000 " AndroidSdkVersion = "2.0.4.204000"
WebClientID = "YUMx5nI8ZU8Ap8pm"
WebClientSecret = "dbw2OtmVEeuUvIptb1Coyg"
WebClientVersion = "2.0.0"
WebPackageName = "mypikpak.com"
WebSdkVersion = "8.0.3"
) )
func (d *PikPak) login() error { func (d *PikPak) login() error {
url := "https://user.mypikpak.com/v1/auth/signin" url := "https://user.mypikpak.com/v1/auth/signin"
// 使用 用户填写的 CaptchaToken —————— (验证后的captcha_token)
if d.GetCaptchaToken() == "" {
if err := d.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), d.Username); err != nil { if err := d.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), d.Username); err != nil {
return err return err
} }
}
var e ErrResp var e ErrResp
res, err := base.RestyClient.R().SetError(&e).SetBody(base.Json{ res, err := base.RestyClient.SetRetryCount(1).R().SetError(&e).SetBody(base.Json{
"captcha_token": d.GetCaptchaToken(), "captcha_token": d.GetCaptchaToken(),
"client_id": ClientID, "client_id": d.ClientID,
"client_secret": ClientSecret, "client_secret": d.ClientSecret,
"username": d.Username, "username": d.Username,
"password": d.Password, "password": d.Password,
}).SetQueryParam("client_id", ClientID).Post(url) }).SetQueryParam("client_id", d.ClientID).Post(url)
if err != nil { if err != nil {
return err return err
} }
@ -69,53 +96,60 @@ func (d *PikPak) login() error {
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
d.AccessToken = jsoniter.Get(data, "access_token").ToString() d.AccessToken = jsoniter.Get(data, "access_token").ToString()
d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) d.Common.SetUserID(jsoniter.Get(data, "sub").ToString())
d.Addition.RefreshToken = d.RefreshToken
op.MustSaveDriverStorage(d)
return nil return nil
} }
func (d *PikPak) refreshToken() error { //func (d *PikPak) refreshToken() error {
url := "https://user.mypikpak.com/v1/auth/token" // url := "https://user.mypikpak.com/v1/auth/token"
var e ErrResp // var e ErrResp
res, err := base.RestyClient.R().SetError(&e). // res, err := base.RestyClient.SetRetryCount(1).R().SetError(&e).
SetHeader("user-agent", "").SetBody(base.Json{ // SetHeader("user-agent", "").SetBody(base.Json{
"client_id": ClientID, // "client_id": ClientID,
"client_secret": ClientSecret, // "client_secret": ClientSecret,
"grant_type": "refresh_token", // "grant_type": "refresh_token",
"refresh_token": d.RefreshToken, // "refresh_token": d.RefreshToken,
}).SetQueryParam("client_id", ClientID).Post(url) // }).SetQueryParam("client_id", ClientID).Post(url)
if err != nil { // if err != nil {
d.Status = err.Error() // d.Status = err.Error()
op.MustSaveDriverStorage(d) // op.MustSaveDriverStorage(d)
return err // return err
} // }
if e.ErrorCode != 0 { // if e.ErrorCode != 0 {
if e.ErrorCode == 4126 { // if e.ErrorCode == 4126 {
// refresh_token invalid, re-login // // refresh_token invalid, re-login
return d.login() // return d.login()
} // }
d.Status = e.Error() // d.Status = e.Error()
op.MustSaveDriverStorage(d) // op.MustSaveDriverStorage(d)
return errors.New(e.Error()) // return errors.New(e.Error())
} // }
data := res.Body() // data := res.Body()
d.Status = "work" // d.Status = "work"
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() // d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
d.AccessToken = jsoniter.Get(data, "access_token").ToString() // d.AccessToken = jsoniter.Get(data, "access_token").ToString()
d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) // d.Common.SetUserID(jsoniter.Get(data, "sub").ToString())
d.Addition.RefreshToken = d.RefreshToken // d.Addition.RefreshToken = d.RefreshToken
op.MustSaveDriverStorage(d) // op.MustSaveDriverStorage(d)
return nil // return nil
} //}
func (d *PikPak) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { func (d *PikPak) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R() req := base.RestyClient.R()
req.SetHeaders(map[string]string{ req.SetHeaders(map[string]string{
"Authorization": "Bearer " + d.AccessToken, //"Authorization": "Bearer " + d.AccessToken,
"User-Agent": d.GetUserAgent(), "User-Agent": d.GetUserAgent(),
"X-Device-ID": d.GetDeviceID(), "X-Device-ID": d.GetDeviceID(),
"X-Captcha-Token": d.GetCaptchaToken(), "X-Captcha-Token": d.GetCaptchaToken(),
}) })
if d.oauth2Token != nil {
// 使用oauth2 获取 access_token
token, err := d.oauth2Token.Token()
if err != nil {
return nil, err
}
req.SetAuthScheme(token.TokenType).SetAuthToken(token.AccessToken)
}
if callback != nil { if callback != nil {
callback(req) callback(req)
} }
@ -132,18 +166,31 @@ func (d *PikPak) request(url string, method string, callback base.ReqCallback, r
switch e.ErrorCode { switch e.ErrorCode {
case 0: case 0:
return res.Body(), nil return res.Body(), nil
case 4122, 4121, 10, 16: case 4122, 4121, 16:
if err1 := d.refreshToken(); err1 != nil { // access_token 过期
return nil, err1
//if err1 := d.refreshToken(); err1 != nil {
// return nil, err1
//}
t, err := d.oauth2Token.Token()
if err != nil {
return nil, err
} }
d.AccessToken = t.AccessToken
d.RefreshToken = t.RefreshToken
d.Addition.RefreshToken = t.RefreshToken
op.MustSaveDriverStorage(d)
return d.request(url, method, callback, resp) return d.request(url, method, callback, resp)
case 9: // 验证码token过期 case 9: // 验证码token过期
if err = d.RefreshCaptchaTokenAtLogin(GetAction(method, url), d.Common.UserID); err != nil { if err = d.RefreshCaptchaTokenAtLogin(GetAction(method, url), d.Common.UserID); err != nil {
return nil, err return nil, err
} }
return d.request(url, method, callback, resp) return d.request(url, method, callback, resp)
case 10: // 操作频繁
return nil, errors.New(e.ErrorDescription)
default: default:
return nil, err return nil, errors.New(e.Error())
} }
} }
@ -185,6 +232,11 @@ type Common struct {
CaptchaToken string CaptchaToken string
UserID string UserID string
// 必要值,签名相关 // 必要值,签名相关
ClientID string
ClientSecret string
ClientVersion string
PackageName string
Algorithms []string
DeviceID string DeviceID string
UserAgent string UserAgent string
// 验证码token刷新成功回调 // 验证码token刷新成功回调
@ -275,8 +327,8 @@ func (c *Common) GetDeviceID() string {
// RefreshCaptchaTokenAtLogin 刷新验证码token(登录后) // RefreshCaptchaTokenAtLogin 刷新验证码token(登录后)
func (d *PikPak) RefreshCaptchaTokenAtLogin(action, userID string) error { func (d *PikPak) RefreshCaptchaTokenAtLogin(action, userID string) error {
metas := map[string]string{ metas := map[string]string{
"client_version": ClientVersion, "client_version": d.ClientVersion,
"package_name": PackageName, "package_name": d.PackageName,
"user_id": userID, "user_id": userID,
} }
metas["timestamp"], metas["captcha_sign"] = d.Common.GetCaptchaSign() metas["timestamp"], metas["captcha_sign"] = d.Common.GetCaptchaSign()
@ -299,8 +351,8 @@ func (d *PikPak) RefreshCaptchaTokenInLogin(action, username string) error {
// GetCaptchaSign 获取验证码签名 // GetCaptchaSign 获取验证码签名
func (c *Common) GetCaptchaSign() (timestamp, sign string) { func (c *Common) GetCaptchaSign() (timestamp, sign string) {
timestamp = fmt.Sprint(time.Now().UnixMilli()) timestamp = fmt.Sprint(time.Now().UnixMilli())
str := fmt.Sprint(ClientID, ClientVersion, PackageName, c.DeviceID, timestamp) str := fmt.Sprint(c.ClientID, c.ClientVersion, c.PackageName, c.DeviceID, timestamp)
for _, algorithm := range Algorithms { for _, algorithm := range c.Algorithms {
str = utils.GetMD5EncodeStr(str + algorithm) str = utils.GetMD5EncodeStr(str + algorithm)
} }
sign = "1." + str sign = "1." + str
@ -311,16 +363,16 @@ func (c *Common) GetCaptchaSign() (timestamp, sign string) {
func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) error { func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) error {
param := CaptchaTokenRequest{ param := CaptchaTokenRequest{
Action: action, Action: action,
CaptchaToken: d.Common.CaptchaToken, CaptchaToken: d.GetCaptchaToken(),
ClientID: ClientID, ClientID: d.ClientID,
DeviceID: d.Common.DeviceID, DeviceID: d.GetDeviceID(),
Meta: metas, Meta: metas,
RedirectUri: "xlaccsdk01://xbase.cloud/callback?state=harbor", RedirectUri: "xlaccsdk01://xbase.cloud/callback?state=harbor",
} }
var e ErrResp var e ErrResp
var resp CaptchaTokenResponse var resp CaptchaTokenResponse
_, err := d.request("https://user.mypikpak.com/v1/shield/captcha/init", http.MethodPost, func(req *resty.Request) { _, err := d.request("https://user.mypikpak.com/v1/shield/captcha/init", http.MethodPost, func(req *resty.Request) {
req.SetError(&e).SetBody(param).SetQueryParam("client_id", ClientID) req.SetError(&e).SetBody(param).SetQueryParam("client_id", d.ClientID)
}, &resp) }, &resp)
if err != nil { if err != nil {
@ -328,15 +380,7 @@ func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) err
} }
if e.IsError() { if e.IsError() {
return &e return errors.New(e.Error())
}
if resp.CaptchaToken == "" {
return fmt.Errorf("empty captchaToken")
} else {
// 对 被风控的情况 进行处理
d.Addition.CaptchaToken = resp.CaptchaToken
op.MustSaveDriverStorage(d)
} }
if resp.Url != "" { if resp.Url != "" {