awesome-python-login-model icon indicating copy to clipboard operation
awesome-python-login-model copied to clipboard

gitee的模拟登录考虑加吗?

Open zeno-io opened this issue 3 years ago • 3 comments

zeno-io avatar Aug 14 '20 08:08 zeno-io

import requests
import re
import base64

# pip uninstall pycrypto
# pip install pycryptodome
# pip install Crypto
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA

# python2 和 python3的兼容代码
try:
    # python2 中
    import cookielib
except:
    # python3 中
    import http.cookiejar as cookielib


class GiteeLogin(object):
    def __init__(self, username, password):
        # 初始化信息
        self.headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
            "Connection": "keep-alive",
            "Content-Type": "application/x-www-form-urlencoded",
            "Host": "gitee.com",
            "Origin": "https://gitee.com",
            "Referer": "https://gitee.com/login",
            'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
        }
        # session代表某一次连接
        self.session = requests.Session()
        self.profile_url = 'https://gitee.com/profile'
        self.post_url = 'https://gitee.com/login'
        self.token_url = 'https://gitee.com/login'
        self.encryptCsrfToken = None
        self.encryptSeparator = None
        self.publicKey = None
        self.username = username
        self.password = password
        self.token = None
        # 因为原始的session.cookies 没有save()方法,所以需要用到cookielib中的方法LWPCookieJar,这个类实例化的cookie对象,就可以直接调用save方法。
        self.session.cookies = cookielib.LWPCookieJar(filename="giteeCookie.txt")

    # 登录入口
    def tryLogin(self):
        # 第一步:尝试使用已有的cookie登录
        try:
            self.session.cookies.load()
        except:
            print(f"Failed to load cookie.")

        isLogin = self.isLoginStatus()
        print(f"isLogin = {isLogin}")
        if isLogin == False:
            return self.login()
        return True

    # 通过访问个人中心页面的返回状态码来判断是否为登录状态
    def isLoginStatus(self):
        # 下面有两个关键点
        # 第一个是header,如果不设置,会返回500的错误
        # 第二个是allow_redirects,如果不设置,session访问时,服务器返回302,
        # 然后session会自动重定向到登录页面,获取到登录页面之后,变成200的状态码
        # allow_redirects = False  就是不允许重定向
        resp = self.session.get(self.profile_url, headers=self.headers, allow_redirects=False)
        if resp.status_code != 200:
            # print(f"Login Status, StatusCode = {resp.status_code}")
            return False
        else:
            return True

    # 登录入口
    def login(self):
        ok = self.prepare()
        if ok == False:
            return False

        plainStringText = '' + self.encryptCsrfToken + self.encryptSeparator + self.password
        formatPublicKey = self.publicKey.replace('\\n', '\n')
        encryptPassword = self.encryptStringByRSA(formatPublicKey, plainStringText)
        # print( self.encryptCsrfToken )
        # print( self.encryptSeparator )
        # print( self.password )

        postData = {
            "encrypt_key": "password",
            "utf8": "✓",
            "authenticity_token": self.token,
            "redirect_to_url": "",
            "user[login]": self.username,
            "encrypt_data[user[password]]": encryptPassword,
            "user[remember_me]": "0"
        }
        # print(postData)
        resp = self.session.post(self.post_url, data=postData, headers=self.headers)
        if resp.status_code != 200:
            print(f"Login Fail, StatusCode = {resp.status_code}")
            return False
        # print(f"Text = {resp.text}")

        index = resp.text.index('action="/login"')
        if index >= 0:
            return False

        # 登录成功之后,将cookie保存在本地文件中
        self.session.cookies.save()
        return True

    def prepare(self):
        resp = self.session.get(self.token_url, headers=self.headers)
        if resp.status_code != 200:
            print(f"Prepare Fail, StatusCode = {resp.status_code}")
            return None
        ok = self.parseKey(resp.text)
        if ok == False:
            return False
        ok = self.parseToken(resp.text)
        if ok == False:
            return False
        return True

    # Get login token
    def getToken(self):
        if self.token == None:
            resp = self.session.get(self.token_url, headers=self.headers)
            if resp.status_code != 200:
                print(f"Get Token Fail, StatusCode = {resp.status_code}")
                return None
            self.parseToken(resp.text)
        return self.token

    # parse key
    def parseKey(self, text):
        match = re.search(r'meta content="(.*?)" name="csrf-token"', text)
        if not match:
            print('Get Encrypt Csrf Token Fail')
            return False
        else:
            self.encryptCsrfToken = match.group(1)
        match = re.search(r'"separator":"(.*?)"', text)
        if not match:
            print('Get Encrypt separator Fail')
            return False
        else:
            self.encryptSeparator = match.group(1)
        match = re.search(r'"password_key":"(.*?)"', text)
        if not match:
            print('Get Public Key Fail')
            return False
        else:
            self.publicKey = match.group(1)
        return True

    # parse token
    def parseToken(self, text):
        match = re.search(r'name="authenticity_token" type="hidden" value="(.*?)"', text)
        if not match:
            print('Get Csrf Token Fail')
            return False
        self.token = match.group(1)
        return True

    # RSA 加密
    def encryptStringByRSA(self, publicKey, plainStringText):
        rsa_key = RSA.import_key(publicKey)
        cipher = PKCS1_OAEP.new(rsa_key)
        x = cipher.encrypt(plainStringText.encode())
        return base64.b64encode(x).decode()
    
    
    def force_sync_project(self, postUrl):
        postData = {
            "user_sync_code": "",
            "password_sync_code": "",
            "sync_wiki": "false",
            "authenticity_token": self.getToken()
        }
        resp = self.session.post(postUrl, data=postData, headers=self.headers, allow_redirects=False)
        if resp.status_code != 200:
            print(f"Failed to Force Sync Project, StatusCode = {resp.status_code}")
            print(f"Text = {resp.text}")
        else:
            print(f"Successful to Force Sync Project")


if __name__ == "__main__":
    # 用户名,密码请修改
    username = "[email protected]"
    password = "XXX" 

    login = GiteeLogin(username, password)
    status = login.tryLogin()
    if status == True:
        login.force_sync_project("https://gitee.com/svenaugustus/dubbo/force_sync_project")
    else:
        print(f"Login Fail")

我自己写的python脚本,登录没成功 o(╯□╰)o

zeno-io avatar Aug 14 '20 13:08 zeno-io

@SvenAugustus 可以尝试,不太确定哦。

Kr1s77 avatar Aug 17 '20 01:08 Kr1s77

@SvenAugustus 测试了一下

在你 post 登录前你可以尝试发送请求到 https://gitee.com/check_user_login 验证一下 如果再响应体中出现 {"result":1,"failed_count":2} 这种信息,那就是说要需要输入验证码了,这时候需要去请求验证码,然后在 login post data 中添加 captcha 参数,其中填写请求下来的验证码。

Kr1s77 avatar Aug 17 '20 08:08 Kr1s77