Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cloudpan/api_constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package cloudpan
const (
WEB_URL string = "https://cloud.189.cn"
AUTH_URL string = "https://open.e.189.cn/api/logbox/oauth2"
API_URL string = "https://api.cloud.189.cn"

ACCOUNT_TYPE = "02"
APP_ID = "8025431004"
Expand All @@ -32,3 +31,8 @@ const (

CHANNEL_ID = "web_cloud.189.cn"
)

// API_URL is declared as a var (rather than const) so that tests can inject a
// httptest mock-server URL by reassigning cloudpan.API_URL. Its runtime value is
// unchanged.
var API_URL string = "https://api.cloud.189.cn"
213 changes: 136 additions & 77 deletions cloudpan/app_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,59 +33,59 @@ import (
type (
appLoginParams struct {
CaptchaToken string
Lt string
ReturnUrl string
ParamId string
ReqId string
jRsaKey string
Lt string
ReturnUrl string
ParamId string
ReqId string
jRsaKey string
}

AppLoginToken struct {
SessionKey string `json:"sessionKey"`
SessionSecret string `json:"sessionSecret"`
FamilySessionKey string `json:"familySessionKey"`
SessionKey string `json:"sessionKey"`
SessionSecret string `json:"sessionSecret"`
FamilySessionKey string `json:"familySessionKey"`
FamilySessionSecret string `json:"familySessionSecret"`
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
// 有效期的token
SskAccessToken string `json:"sskAccessToken"`
// token 过期时间点,时间戳ms
SskAccessTokenExpiresIn int64 `json:"sskAccessTokenExpiresIn"`
RsaPublicKey string `json:"rsaPublicKey"`
SskAccessTokenExpiresIn int64 `json:"sskAccessTokenExpiresIn"`
RsaPublicKey string `json:"rsaPublicKey"`
}

appSessionResp struct {
ResCode int `json:"res_code"`
ResMessage string `json:"res_message"`
AccessToken string `json:"accessToken"`
FamilySessionKey string `json:"familySessionKey"`
ResCode int `json:"res_code"`
ResMessage string `json:"res_message"`
AccessToken string `json:"accessToken"`
FamilySessionKey string `json:"familySessionKey"`
FamilySessionSecret string `json:"familySessionSecret"`
GetFileDiffSpan int `json:"getFileDiffSpan"`
GetUserInfoSpan int `json:"getUserInfoSpan"`
IsSaveName string `json:"isSaveName"`
KeepAlive int `json:"keepAlive"`
LoginName string `json:"loginName"`
RefreshToken string `json:"refreshToken"`
SessionKey string `json:"sessionKey"`
SessionSecret string `json:"sessionSecret"`
GetFileDiffSpan int `json:"getFileDiffSpan"`
GetUserInfoSpan int `json:"getUserInfoSpan"`
IsSaveName string `json:"isSaveName"`
KeepAlive int `json:"keepAlive"`
LoginName string `json:"loginName"`
RefreshToken string `json:"refreshToken"`
SessionKey string `json:"sessionKey"`
SessionSecret string `json:"sessionSecret"`
}

accessTokenResp struct {
// token过期时间,默认30天
ExpiresIn int64 `json:"expiresIn"`
ExpiresIn int64 `json:"expiresIn"`
AccessToken string `json:"accessToken"`
}

appRefreshUserSessionResp struct {
XMLName xml.Name `xml:"userSession"`
LoginName string `xml:"loginName"`
SessionKey string `xml:"sessionKey"`
SessionSecret string `xml:"sessionSecret"`
KeepAlive int `xml:"keepAlive"`
GetFileDiffSpan int `xml:"getFileDiffSpan"`
GetUserInfoSpan int `xml:"getUserInfoSpan"`
FamilySessionKey string `xml:"familySessionKey"`
FamilySessionSecret string `xml:"familySessionSecret"`
XMLName xml.Name `xml:"userSession"`
LoginName string `xml:"loginName"`
SessionKey string `xml:"sessionKey"`
SessionSecret string `xml:"sessionSecret"`
KeepAlive int `xml:"keepAlive"`
GetFileDiffSpan int `xml:"getFileDiffSpan"`
GetUserInfoSpan int `xml:"getUserInfoSpan"`
FamilySessionKey string `xml:"familySessionKey"`
FamilySessionSecret string `xml:"familySessionSecret"`
}
)

Expand All @@ -109,29 +109,29 @@ func AppLogin(username, password string) (result *AppLoginToken, error *apierror
rsaPassword, _ := crypto.RsaEncrypt([]byte(rsaKey.String()), []byte(password))

urlStr := "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
headers := map[string]string {
"Content-Type": "application/x-www-form-urlencoded",
"Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
"Cookie": "LT=" + loginParams.Lt,
headers := map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
"Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
"Cookie": "LT=" + loginParams.Lt,
"X-Requested-With": "XMLHttpRequest",
"REQID": loginParams.ReqId,
"lt": loginParams.Lt,
}
formData := map[string]string {
"appKey": "8025431004",
"accountType": "02",
"userName": "{RSA}" + apiutil.B64toHex(string(crypto.Base64Encode(rsaUserName))),
"password": "{RSA}" + apiutil.B64toHex(string(crypto.Base64Encode(rsaPassword))),
"REQID": loginParams.ReqId,
"lt": loginParams.Lt,
}
formData := map[string]string{
"appKey": "8025431004",
"accountType": "02",
"userName": "{RSA}" + apiutil.B64toHex(string(crypto.Base64Encode(rsaUserName))),
"password": "{RSA}" + apiutil.B64toHex(string(crypto.Base64Encode(rsaPassword))),
"validateCode": "",
"captchaToken": loginParams.CaptchaToken,
"returnUrl": loginParams.ReturnUrl,
"mailSuffix": "@189.cn",
"returnUrl": loginParams.ReturnUrl,
"mailSuffix": "@189.cn",
"dynamicCheck": "FALSE",
"clientType": "10020",
"cb_SaveName": "1",
"isOauth2": "false",
"state": "",
"paramId": loginParams.ParamId,
"clientType": "10020",
"cb_SaveName": "1",
"isOauth2": "false",
"state": "",
"paramId": loginParams.ParamId,
}

logger.Verboseln("do request url: " + urlStr)
Expand All @@ -147,13 +147,43 @@ func AppLogin(username, password string) (result *AppLoginToken, error *apierror
return nil, apierror.NewFailedApiError(err.Error())
}
if r.Result != 0 || r.ToUrl == "" {
return nil, apierror.NewFailedApiError("登录失败")
errMsg := r.Msg
if errMsg == "" {
errMsg = "登录失败"
}
return nil, apierror.NewFailedApiError(errMsg)
}

// 访问ToUrl获取COOKIE_LOGIN_USER cookie
toUrlHeaders := map[string]string{
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9",
}
logger.Verboseln("访问ToUrl获取cookie: " + r.ToUrl)
_, err1 = appClient.Fetch("GET", r.ToUrl, nil, toUrlHeaders)
if err1 != nil {
logger.Verboseln("访问ToUrl error: ", err1.Error())
}

// 从cookie jar中获取COOKIE_LOGIN_USER
cloudpanUrl := &url.URL{
Scheme: "http",
Host: "cloud.189.cn",
Path: "/",
}
cks := appClient.Jar.Cookies(cloudpanUrl)
for _, cookie := range cks {
if cookie.Name == "COOKIE_LOGIN_USER" {
logger.Verboseln("获取到COOKIE_LOGIN_USER: ", cookie.Value)
// 这里不返回,继续后续流程
break
}
}

fullUrl := &strings.Builder{}
fmt.Fprintf(fullUrl, "%s/getSessionForPC.action?clientType=%s&version=%s&channelId=%s&redirectURL=%s",
API_URL, "TELEMAC", "1.0.0", "web_cloud.189.cn", url.QueryEscape(r.ToUrl))
headers = map[string]string {
headers = map[string]string{
"Accept": "application/json;charset=UTF-8",
}
logger.Verboseln("do request url: " + fullUrl.String())
Expand Down Expand Up @@ -183,16 +213,16 @@ func AppLogin(username, password string) (result *AppLoginToken, error *apierror
fmt.Fprintf(fullUrl, "%s/open/oauth2/getAccessTokenBySsKey.action?sessionKey=%s",
API_URL, rs.SessionKey)
timestamp := apiutil.Timestamp()
signParams := map[string]string {
"Timestamp": strconv.Itoa(timestamp),
signParams := map[string]string{
"Timestamp": strconv.Itoa(timestamp),
"sessionKey": rs.SessionKey,
"AppKey": "601102120",
"AppKey": "601102120",
}
headers = map[string]string {
"AppKey": "601102120",
headers = map[string]string{
"AppKey": "601102120",
"Signature": apiutil.SignatureOfMd5(signParams),
"Sign-Type": "1",
"Accept": "application/json",
"Accept": "application/json",
"Timestamp": strconv.Itoa(timestamp),
}
body, err1 = appClient.Fetch("GET", fullUrl.String(), nil, headers)
Expand All @@ -212,7 +242,7 @@ func AppLogin(username, password string) (result *AppLoginToken, error *apierror
}

func appGetLoginParams() (params appLoginParams, error *apierror.ApiError) {
header := map[string]string {
header := map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
}
fullUrl := &strings.Builder{}
Expand All @@ -227,23 +257,35 @@ func appGetLoginParams() (params appLoginParams, error *apierror.ApiError) {
}
content := string(data)

re, _ := regexp.Compile("captchaToken' value='(.+?)'")
params.CaptchaToken = re.FindStringSubmatch(content)[1]
match := regexp.MustCompile("name='captchaToken' value='(.+?)'").FindStringSubmatch(content)
if len(match) > 1 {
params.CaptchaToken = match[1]
}

re, _ = regexp.Compile("lt = \"(.+?)\"")
params.Lt = re.FindStringSubmatch(content)[1]
match = regexp.MustCompile("var lt = \"(.+?)\"").FindStringSubmatch(content)
if len(match) > 1 {
params.Lt = match[1]
}

re, _ = regexp.Compile("returnUrl = '(.+?)'")
params.ReturnUrl = re.FindStringSubmatch(content)[1]
match = regexp.MustCompile("returnUrl = '(.+?)'").FindStringSubmatch(content)
if len(match) > 1 {
params.ReturnUrl = match[1]
}

re, _ = regexp.Compile("paramId = \"(.+?)\"")
params.ParamId = re.FindStringSubmatch(content)[1]
match = regexp.MustCompile("var paramId = \"(.+?)\"").FindStringSubmatch(content)
if len(match) > 1 {
params.ParamId = match[1]
}

re, _ = regexp.Compile("reqId = \"(.+?)\"")
params.ReqId = re.FindStringSubmatch(content)[1]
match = regexp.MustCompile("var reqId = \"(.+?)\"").FindStringSubmatch(content)
if len(match) > 1 {
params.ReqId = match[1]
}

re, _ = regexp.Compile("j_rsaKey\" value=\"(.+?)\"")
params.jRsaKey = re.FindStringSubmatch(content)[1]
match = regexp.MustCompile("id=\"j_rsaKey\" value=\"(.+?)\"").FindStringSubmatch(content)
if len(match) > 1 {
params.jRsaKey = match[1]
}
return
}

Expand All @@ -252,7 +294,7 @@ func getSessionByAccessToken(accessToken string) (*appRefreshUserSessionResp, *a
fullUrl := &strings.Builder{}
fmt.Fprintf(fullUrl, "%s/getSessionForPC.action?appId=%s&accessToken=%s&clientSn=%s&%s",
API_URL, "8025431004", accessToken, apiutil.Uuid(), apiutil.PcClientInfoSuffixParam())
headers := map[string]string {
headers := map[string]string{
"X-Request-ID": apiutil.XRequestId(),
}
logger.Verboseln("do request url: " + fullUrl.String())
Expand All @@ -268,4 +310,21 @@ func getSessionByAccessToken(accessToken string) (*appRefreshUserSessionResp, *a
return nil, apierror.NewApiErrorWithError(err)
}
return item, nil
}
}

// RefreshUserSessionByAccessToken 用 accessToken 刷新 PC session(非密码登录)。
// 接通原死代码 getSessionByAccessToken,返回含新 sessionKey/sessionSecret/family* 的 AppLoginToken;
// accessToken 本身不变(在其有效期内可重复刷新),供 PanUser.RefreshAppSession 主动续期。
func RefreshUserSessionByAccessToken(accessToken string) (*AppLoginToken, *apierror.ApiError) {
item, err := getSessionByAccessToken(accessToken)
if err != nil {
return nil, err
}
return &AppLoginToken{
SessionKey: item.SessionKey,
SessionSecret: item.SessionSecret,
FamilySessionKey: item.FamilySessionKey,
FamilySessionSecret: item.FamilySessionSecret,
AccessToken: accessToken,
}, nil
}
Loading