From 8e5a768a10b6b05ab8354e6439703e37cc986c91 Mon Sep 17 00:00:00 2001 From: chenzhenyu <402731062@qq.com> Date: Fri, 12 Jul 2019 01:02:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=9B=B4=E6=8E=A5=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=AF=86=E7=A0=81=E7=99=BB=E9=99=86=F0=9F=90=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.json | 2 +- components/input.js | 2 +- navigations.js | 2 +- navigations/index.js | 4 +- screens/auth/index.js | 4 +- screens/home/index.js | 5 +- screens/login-v2/index.js | 616 ++++++++++++++++++++++++-------------- screens/login/index.js | 14 +- utils/fetch.js | 10 +- 9 files changed, 415 insertions(+), 244 deletions(-) diff --git a/app.json b/app.json index 7208561dc..af8ed0450 100644 --- a/app.json +++ b/app.json @@ -31,4 +31,4 @@ "description": "A React Native App for https://bangumi.tv ", "githubUrl": "https://github.com/czy0729/Bangumi" } -} +} \ No newline at end of file diff --git a/components/input.js b/components/input.js index 5786fa7f5..aa768fd97 100644 --- a/components/input.js +++ b/components/input.js @@ -3,7 +3,7 @@ * @Author: czy0729 * @Date: 2019-03-19 01:43:43 * @Last Modified by: czy0729 - * @Last Modified time: 2019-06-22 14:17:41 + * @Last Modified time: 2019-07-11 22:36:08 */ import React from 'react' import { diff --git a/navigations.js b/navigations.js index fae974c15..e855c4eae 100644 --- a/navigations.js +++ b/navigations.js @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2019-06-02 14:42:28 * @Last Modified by: czy0729 - * @Last Modified time: 2019-07-08 23:52:21 + * @Last Modified time: 2019-07-12 00:37:34 */ export default { initialRouteName: 'HomeTab', // HomeTab diff --git a/navigations/index.js b/navigations/index.js index 521d4329b..623abac07 100644 --- a/navigations/index.js +++ b/navigations/index.js @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2019-03-29 10:38:12 * @Last Modified by: czy0729 - * @Last Modified time: 2019-07-08 23:52:46 + * @Last Modified time: 2019-07-11 22:15:31 */ import React from 'react' import { StyleSheet, View } from 'react-native' @@ -81,7 +81,7 @@ const HomeStack = createStackNavigator( Calendar, Discovery, HomeTab, - Login: LoginV2, + Login, LoginV2, Mono, Notify, diff --git a/screens/auth/index.js b/screens/auth/index.js index 272a5db8a..ecb81cd9f 100644 --- a/screens/auth/index.js +++ b/screens/auth/index.js @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2019-03-31 10:25:46 * @Last Modified by: czy0729 - * @Last Modified time: 2019-06-23 22:12:47 + * @Last Modified time: 2019-07-12 00:37:52 */ import React from 'react' import { View } from 'react-native' @@ -75,7 +75,7 @@ class Auth extends React.Component { _.mt.md ]} shadow - onPress={() => navigation.push('Login')} + onPress={() => navigation.push('LoginV2')} > 现在登录 diff --git a/screens/home/index.js b/screens/home/index.js index 3acb15eb4..bbdb7be48 100644 --- a/screens/home/index.js +++ b/screens/home/index.js @@ -2,7 +2,7 @@ * @Author: czy0729 * @Date: 2019-03-13 08:34:37 * @Last Modified by: czy0729 - * @Last Modified time: 2019-06-25 19:50:56 + * @Last Modified time: 2019-07-12 00:42:09 */ import React from 'react' import { StyleSheet, View } from 'react-native' @@ -11,6 +11,7 @@ import PropTypes from 'prop-types' import { observer } from 'mobx-react' import { Image } from '@components' import { IconTabBar, IconTabsHeader, ManageModal } from '@screens/_' +import { userStore } from '@stores' import { inject, withTabsHeader } from '@utils/decorators' import { hm } from '@utils/fetch' import { IOS } from '@constants' @@ -74,7 +75,7 @@ class Home extends React.Component { } setTimeout(() => { - hm(`?id=${$.userInfo.userId}`, title) + hm(`?id=${userStore.myUserId}`, title) }, 4000) } diff --git a/screens/login-v2/index.js b/screens/login-v2/index.js index ed5c5b86b..b1dd6a180 100644 --- a/screens/login-v2/index.js +++ b/screens/login-v2/index.js @@ -5,24 +5,59 @@ * @Author: czy0729 * @Date: 2019-06-30 15:48:46 * @Last Modified by: czy0729 - * @Last Modified time: 2019-07-11 00:41:38 + * @Last Modified time: 2019-07-12 01:00:36 */ import React from 'react' -import { StyleSheet, View, Image } from 'react-native' +import { StyleSheet, View, Image as RNImage } from 'react-native' import { Constants } from 'expo' import cheerio from 'cheerio-without-node-native' -import { Input, Button } from '@components' +import { + Flex, + Text, + Touchable, + Image, + Input, + Button, + KeyboardSpacer +} from '@components' import { StatusBar, StatusBarPlaceholder } from '@screens/_' -// import { userStore } from '@stores' +import { userStore } from '@stores' import { urlStringify, getTimestamp } from '@utils' -import { HOST } from '@constants' +import { info } from '@utils/ui' +import { HOST, APP_ID, OAUTH_REDIRECT_URL } from '@constants' import _ from '@styles' -// config -const email = '402731062@qq.com' -const password = '84783019' -const clientId = 'bgm8885c4d524cd61fc' -const clientSecret = '1da52e7834bbb73cca90302f9ddbc8dd' +function xhr({ method = 'GET', url, data, headers = {}, responseType } = {}) { + return new Promise((resolve, reject) => { + const request = new XMLHttpRequest() + request.onreadystatechange = function() { + if (this.readyState === 4 && this.status === 200) { + resolve(this) + } + } + xhr.onerror = function() { + reject(new TypeError('Network request failed')) + } + xhr.ontimeout = function() { + reject(new TypeError('Network request failed')) + } + xhr.onabort = function() { + reject(new TypeError('AbortError')) + } + + request.open(method, url, true) + request.withCredentials = false + if (responseType) { + request.responseType = responseType + } + Object.keys(headers).forEach(key => { + request.setRequestHeader(key, headers[key]) + }) + + const body = data ? urlStringify(data) : null + request.send(body) + }) +} export default class LoginV2 extends React.Component { static navigationOptions = { @@ -30,19 +65,23 @@ export default class LoginV2 extends React.Component { } state = { - state: getTimestamp(), + clicked: false, + email: '', + password: '', captcha: '', - base64: '' + base64: '', + loading: false, + info: '' } userAgent = '' formhash = '' - code = '' - accessToken = '' cookie = { chiiSid: '', chiiAuth: '' } + code = '' + accessToken = '' async componentDidMount() { this.userAgent = await Constants.getWebViewUserAgentAsync() @@ -52,112 +91,95 @@ export default class LoginV2 extends React.Component { await this.getCaptcha() } - logout = () => - new Promise(resolve => { - console.log('logout') - const request = new XMLHttpRequest() + onTour = () => { + const { navigation } = this.props + navigation.goBack() + } - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - resolve() - } - } - request.withCredentials = false - request.open('GET', `${HOST}/logout/7dd16c5e`, true) - request.setRequestHeader('User-Agent', this.userAgent) - request.send() + onLogin = () => { + this.setState({ + clicked: true }) + } - getFormHash = () => - new Promise(resolve => { - console.log('getFormHash') - const request = new XMLHttpRequest() - const that = this - - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - // set-cookie - if (this.responseHeaders['Set-Cookie']) { - const match = this.responseHeaders['Set-Cookie'].match( - /chii_sid=(.+?);/ - ) - if (match) { - that.cookie.chiiSid = match[1] - } - } - - // formhash - const match = this._response.match( - // - ) - if (match) { - that.formhash = match[1] - } - - resolve() - } + logout = () => + xhr({ + url: `${HOST}/logout/7dd16c5e`, + headers: { + 'User-Agent': this.userAgent } - request.withCredentials = false - request.open('GET', `${HOST}/login`, true) - request.setRequestHeader('User-Agent', this.userAgent) - request.send() }) - getCaptcha = () => - new Promise(resolve => { - console.log('getCaptcha') - const { state } = this.state - const request = new XMLHttpRequest() - const that = this - - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - that.setState({ - base64: `data:image/gif;base64,${this._response}` - }) - resolve() - } + getFormHash = async () => { + const res = xhr({ + url: `${HOST}/login`, + headers: { + 'User-Agent': this.userAgent } - request.withCredentials = false - request.responseType = 'arraybuffer' - request.open('GET', `${HOST}/signup/captcha?state=${state}`, true) - request.setRequestHeader('Cookie', `; chii_sid=${this.cookie.chiiSid};`) - request.setRequestHeader('User-Agent', this.userAgent) - request.send() }) - login = () => - new Promise(resolve => { - console.log('login') - const { captcha } = this.state - const request = new XMLHttpRequest() - const that = this - - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - if (this.responseHeaders['Set-Cookie']) { - const match = this.responseHeaders['Set-Cookie'].match( - /chii_auth=(.+?);/ - ) - if (match) { - that.cookie.chiiAuth = match[1] - } - } - - that.oauth() - resolve() - } + const { responseHeaders, _response } = await res + if (responseHeaders['Set-Cookie']) { + const match = responseHeaders['Set-Cookie'].match(/chii_sid=(.+?);/) + if (match) { + this.cookie.chiiSid = match[1] } - request.withCredentials = false - request.open('POST', `${HOST}/FollowTheRabbit`, true) - request.setRequestHeader( - 'Content-Type', - 'application/x-www-form-urlencoded' - ) - request.setRequestHeader('Cookie', `; chii_sid=${this.cookie.chiiSid};`) - request.setRequestHeader('User-Agent', this.userAgent) - request.send( - urlStringify({ + } + + const match = _response.match( + // + ) + if (match) { + this.formhash = match[1] + } + + return res + } + + getCaptcha = async () => { + const res = xhr({ + url: `${HOST}/signup/captcha?state=${getTimestamp()}`, + headers: { + Cookie: `; chii_sid=${this.cookie.chiiSid};`, + 'User-Agent': this.userAgent + }, + responseType: 'arraybuffer' + }) + + const { _response } = await res + this.setState({ + base64: `data:image/gif;base64,${_response}` + }) + + return res + } + + login = async () => { + const { loading, email, password, captcha } = this.state + if (loading) { + return + } + + if (!email || !password || !captcha) { + info('请填写以上字段') + return + } + + this.setState({ + loading: true, + info: '登陆请求中...' + }) + + try { + const { responseHeaders } = await xhr({ + method: 'POST', + url: `${HOST}/FollowTheRabbit`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Cookie: `; chii_sid=${this.cookie.chiiSid};`, + 'User-Agent': this.userAgent + }, + data: { formhash: this.formhash, referer: '', dreferer: '', @@ -165,157 +187,305 @@ export default class LoginV2 extends React.Component { password, captcha_challenge_field: captcha, loginsubmit: '登录' + } + }) + + if (responseHeaders['Set-Cookie']) { + const match = responseHeaders['Set-Cookie'].match(/chii_auth=(.+?);/) + if (match) { + this.cookie.chiiAuth = match[1] + } + } + + if (!this.cookie.chiiAuth) { + this.setState({ + loading: false, + info: '登陆失败, 请重试或点击这里前往旧版登陆 >' }) - ) - }) + return + } - oauth = () => - new Promise(resolve => { - console.log('oauth') - const request = new XMLHttpRequest() - const that = this + this.setState({ + info: '获取授权表单码...' + }) + await this.oauth() - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - that.updateFormhash(this._response) - resolve() - } + this.setState({ + info: '授权中...' + }) + await this.authorize() + + this.setState({ + info: '授权成功, 获取token中...' + }) + await userStore.fetchAccessToken(this.code) + + this.setState({ + loading: false, + info: '登陆成功, 正在请求个人信息...' + }) + this.inStore() + } catch (ex) { + this.setState({ + loading: false, + info: '登陆失败, 请重试或点击这里前往旧版登陆 >' + }) + } + } + + oauth = async () => { + const res = xhr({ + url: `${HOST}/oauth/authorize?client_id=${APP_ID}&response_type=code&redirect_uri=${OAUTH_REDIRECT_URL}`, + headers: { + Cookie: `; chii_sid=${this.cookie.chiiSid}; chii_auth=${ + this.cookie.chiiAuth + };`, + 'User-Agent': this.userAgent } - request.withCredentials = false - request.open( - 'GET', - `${HOST}/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=code`, - true - ) - request.setRequestHeader( - 'Cookie', - `; chii_sid=${this.cookie.chiiSid}; chii_auth=${this.cookie.chiiAuth};` - ) - request.setRequestHeader('User-Agent', this.userAgent) - request.send(null) }) - updateFormhash = html => { - console.log('updateFormhash') + const { _response } = await res this.formhash = cheerio - .load(html)('input[name=formhash]') + .load(_response)('input[name=formhash]') .attr('value') - this.authorize() + return res } - authorize = () => - new Promise(resolve => { - console.log('authorize') - const request = new XMLHttpRequest() - const that = this - - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - that.code = this.responseURL - .split('=') - .slice(1) - .join('=') - that.getAccessToken() - resolve() - } + authorize = async () => { + const res = xhr({ + method: 'POST', + url: `${HOST}/oauth/authorize?client_id=${APP_ID}&response_type=code&redirect_uri=${OAUTH_REDIRECT_URL}`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Cookie: `; chii_sid=${this.cookie.chiiSid}; chii_auth=${ + this.cookie.chiiAuth + };`, + 'User-Agent': this.userAgent + }, + data: { + formhash: this.formhash, + redirect_uri: '', + client_id: APP_ID, + submit: '授权' } - request.withCredentials = false - request.open( - 'POST', - `${HOST}/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=code`, - true - ) - request.setRequestHeader( - 'Content-Type', - 'application/x-www-form-urlencoded' - ) - request.setRequestHeader( - 'Cookie', - `; chii_sid=${this.cookie.chiiSid}; chii_auth=${this.cookie.chiiAuth};` - ) - request.setRequestHeader('User-Agent', this.userAgent) - request.send( - urlStringify({ - formhash: this.formhash, - redirect_uri: '', - client_id: clientId, - submit: '授权' - }) - ) }) - getAccessToken = () => - new Promise(resolve => { - console.log('getAccessToken') - const request = new XMLHttpRequest() + const { responseURL } = await res + this.code = responseURL + .split('=') + .slice(1) + .join('=') + return res + } - request.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - log(this) - resolve() - } - } - request.withCredentials = false - request.open('POST', `${HOST}/oauth/access_token`, true) - request.setRequestHeader( - 'Content-Type', - 'application/x-www-form-urlencoded' - ) - request.setRequestHeader('User-Agent', this.userAgent) - request.send( - urlStringify({ - grant_type: 'authorization_code', - client_id: clientId, - client_secret: clientSecret, - code: this.code, - redirect_uri: 'code', - state: '' - }) - ) + // getAccessToken = () => + // xhr({ + // method: 'POST', + // url: `${HOST}/oauth/access_token`, + // headers: { + // 'Content-Type': 'application/x-www-form-urlencoded', + // 'User-Agent': this.userAgent + // }, + // data: { + // grant_type: 'authorization_code', + // client_id: APP_ID, + // client_secret: clientSecret, + // code: this.code, + // redirect_uri: 'code', + // state: '' + // } + // }) + + inStore = async () => { + const { navigation } = this.props + userStore.updateUserCookie({ + cookie: `; chii_sid=${this.cookie.chiiSid}; chii_auth=${ + this.cookie.chiiAuth + };`, + userAgent: this.userAgent }) + await userStore.fetchUserInfo() + navigation.popToTop() + } - onChange = evt => { + onChange = (evt, type) => { const { nativeEvent } = evt const { text } = nativeEvent this.setState({ - captcha: text + [type]: text, + info: '' }) } + renderPreview() { + return ( + + + + + + + + ) + } + + renderForm() { + const { email, password, captcha, base64, loading, info } = this.state + return ( + + + + + + + + this.onChange(evt, 'email')} + /> + + + + + this.onChange(evt, 'password')} + /> + + + + + this.onChange(evt, 'captcha')} + /> + + + {!!base64 && ( + + )} + + + + { + if (info.includes('登陆失败')) { + const { navigation } = this.props + navigation.push('Login') + } + }} + > + {info} + + + + ) + } + render() { - const { captcha, base64 } = this.state - console.log(base64) + const { clicked } = this.state return ( - {!!base64 && ( - + {clicked ? this.renderForm() : this.renderPreview()} + + {clicked ? ( + + 隐私策略: 我们十分尊重您的个人隐私, 这些信息仅存储于您的设备中, + 我们不会收集上述信息. + + ) : ( + { + const { navigation } = this.props + navigation.push('Login') }} - /> + > + 旧版授权登陆 + )} - - + ) } } const styles = StyleSheet.create({ + gray: { + backgroundColor: 'rgb(251, 251, 251)' + }, + old: { + position: 'absolute', + zIndex: 1, + bottom: _.bottom, + left: 0, + width: '100%', + padding: _.sm, + textAlign: 'center' + }, bottomContainer: { - width: 200, - height: 200 + width: 280, + height: 420 }, - loading: { - width: 200, - height: 64 + form: { + width: 280, + paddingBottom: 152 }, - gray: { - backgroundColor: 'rgb(251, 251, 251)' + input: { + height: 44 + }, + captchaContainer: { + width: 118, + height: 44, + marginLeft: _.sm, + backgroundColor: _.colorBg + }, + captcha: { + width: 118, + height: 44 }, ps: { position: 'absolute', diff --git a/screens/login/index.js b/screens/login/index.js index 36c81991f..b1f2209f2 100644 --- a/screens/login/index.js +++ b/screens/login/index.js @@ -4,7 +4,7 @@ * @Author: czy0729 * @Date: 2019-03-31 11:21:32 * @Last Modified by: czy0729 - * @Last Modified time: 2019-06-23 00:24:39 + * @Last Modified time: 2019-07-12 00:49:08 */ import React from 'react' import { StyleSheet, View } from 'react-native' @@ -162,7 +162,7 @@ export default class Login extends React.Component { />