优化微信登录和获取任务长轮询请求方式

xsign
nineven 3 years ago
parent d571b399d7
commit dd1fb29ba0
  1. 73
      fir_client/src/components/FirDownload.vue
  2. 52
      fir_client/src/components/FirLogin.vue
  3. 71
      fir_client/src/components/ShortDownload.vue
  4. 58
      fir_client/src/components/user/FirUserProfileInfo.vue
  5. 17
      fir_client/src/restful/index.js
  6. 4
      fir_ser/api/urls.py
  7. 72
      fir_ser/api/views/login.py
  8. 26
      fir_ser/api/views/login_wx.py
  9. 6
      fir_ser/api/views/thirdlogin.py
  10. 18
      fir_ser/common/base/magic.py
  11. 6
      fir_ser/common/cache/storage.py
  12. 1
      fir_ser/fir_ser/settings.py
  13. 44
      fir_ser/xsign/views/receiveudids.py

@ -423,7 +423,9 @@ export default {
timer: '',
gomobile: true,
mobileprovision: '',
ad_info: {ad_pic: '', ad_uri: ''}
ad_info: {ad_pic: '', ad_uri: ''},
task_spend_time: 0,
task_msg: ''
}
},
beforeDestroy() {
@ -550,37 +552,44 @@ export default {
duration: 0
});
},
loop_check_task() {
let c_count = 1;
// eslint-disable-next-line no-unused-vars
const loop_t = window.setInterval(res => {
gettask(data => {
c_count += 1;
if (c_count > 120) {
window.clearInterval(loop_t);
loop_check_task(c_count = 1, loop_t) {
if (c_count === 1) {
// eslint-disable-next-line no-unused-vars
loop_t = window.setInterval(res => {
this.task_spend_time += 1;
this.msg = this.task_msg + ' ○ ' + this.task_spend_time;
}, 3000)
}
gettask(data => {
c_count += 1;
if (c_count > 30) {
window.clearInterval(loop_t)
return;
}
if (data.code === 1000) {
window.clearInterval(loop_t);
if (data.msg) {
this.show_err_msg(data.msg);
} else {
this.$message.success('签名完成,请点击下载安装');
this.wrong = false;
}
if (data.code === 1000) {
} else {
if (data.code === 1002) {
window.clearInterval(loop_t);
if (data.msg) {
this.show_err_msg(data.msg);
} else {
this.$message.success('签名完成,请点击下载安装');
this.wrong = false;
}
} else {
if (data.code === 1002) {
window.clearInterval(loop_t);
data.msg = '应用不存在'
}
if (data.msg) {
this.msg = data.msg + ' ○ ' + c_count;
}
data.msg = '应用不存在'
}
}, {
"short": this.$route.params.short,
data: {"task_id": this.$route.query.task_id}
})
}, 3000)
if (data.msg) {
this.task_msg = data.msg;
}
if (data.code === 1001) {
this.loop_check_task(c_count, loop_t)
}
}
}, {
"short": this.$route.params.short,
data: {"task_id": this.$route.query.task_id}
})
},
jiaocheng(act) {
let signhelp = this.$refs.signhelp;
@ -835,6 +844,9 @@ export default {
if (this.mcurrentappinfo.release_type === 2 && !this.currentappinfo.issupersign) {
this.signhelplist = this.inhousehelplist;
}
if (this.agent !== '') {
this.check_msg();
}
} else if (data.code === 1002) {
window.location.href = location.href.replace(location.search, '');
} else if (data.code === 302 && data.data) {
@ -908,9 +920,6 @@ export default {
this.getDownloadTokenFun();
this.full_url = location.href;
this.qrcode();
if (this.agent !== '') {
this.check_msg();
}
}, filters: {
getiOStype: function (type) {
let ftype = '';

@ -139,40 +139,34 @@ export default {
set_auth_token();
this.$router.push({name: 'FirApps'})
},
loop_get_wx_info(wx_login_ticket) {
loop_get_wx_info(wx_login_ticket, c_count = 1) {
if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试");
return
}
let c_count = 1;
// eslint-disable-next-line no-unused-vars
const loop_t = window.setInterval(res => {
if (!this.loop_flag) {
window.clearInterval(loop_t);
wxLoginFun(data => {
c_count += 1;
if (c_count > 30) {
return;
}
wxLoginFun(data => {
c_count += 1;
if (c_count > 120) {
window.clearInterval(loop_t);
}
if (data.code === 1000) {
window.clearInterval(loop_t);
this.set_cookie_and_token(data);
} else if (data.code === 1005) {
window.clearInterval(loop_t);
this.wx_visible = false;
this.loop_flag = false;
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket}
})
}, 3000)
if (data.code === 1000) {
this.set_cookie_and_token(data);
} else if (data.code === 1005) {
this.wx_visible = false;
this.loop_flag = false;
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
} else if (data.code === 1006) {
return this.loop_get_wx_info(wx_login_ticket, c_count)
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket}
})
// }, 3000)
},
wxLogin() {

@ -412,7 +412,9 @@ export default {
timer: '',
gomobile: true,
mobileprovision: '',
ad_info: {ad_pic: '', ad_uri: ''}
ad_info: {ad_pic: '', ad_uri: ''},
task_spend_time: 0,
task_msg: ''
}
},
beforeDestroy() {
@ -516,36 +518,43 @@ export default {
this.msg = msg;
alert(this.msg)
},
loop_check_task() {
let c_count = 1;
// eslint-disable-next-line no-unused-vars
const loop_t = window.setInterval(res => {
gettask(data => {
c_count += 1;
if (c_count > 120) {
window.clearInterval(loop_t);
loop_check_task(c_count = 1, loop_t) {
if (c_count === 1) {
// eslint-disable-next-line no-unused-vars
loop_t = window.setInterval(res => {
this.task_spend_time += 1;
this.msg = this.task_msg + ' ○ ' + this.task_spend_time;
}, 3000)
}
gettask(data => {
c_count += 1;
if (c_count > 30) {
window.clearInterval(loop_t);
return;
}
if (data.code === 1000) {
window.clearInterval(loop_t);
if (data.msg) {
this.show_err_msg(data.msg);
} else {
this.wrong = false;
}
if (data.code === 1000) {
} else {
if (data.code === 1002) {
window.clearInterval(loop_t);
if (data.msg) {
this.show_err_msg(data.msg);
} else {
this.wrong = false;
}
} else {
if (data.code === 1002) {
window.clearInterval(loop_t);
data.msg = '应用不存在'
}
if (data.msg) {
this.msg = data.msg + ' ○ ' + c_count;
}
data.msg = '应用不存在'
}
}, {
"short": this.$route.params.short,
data: {"task_id": this.$route.query.task_id}
})
}, 3000)
if (data.msg) {
this.task_msg = data.msg;
}
if (data.code === 1001) {
this.loop_check_task(c_count, loop_t)
}
}
}, {
"short": this.$route.params.short,
data: {"task_id": this.$route.query.task_id}
})
},
jiaocheng(act) {
let signhelp = this.$refs.signhelp;
@ -790,6 +799,9 @@ export default {
if (this.mcurrentappinfo.release_type === 2 && !this.currentappinfo.issupersign) {
this.signhelplist = this.inhousehelplist;
}
if (this.agent !== '') {
this.check_msg();
}
} else if (data.code === 1002) {
window.location.href = location.href.replace(location.search, '');
} else if (data.code === 302 && data.data) {
@ -866,9 +878,6 @@ export default {
this.getDownloadTokenFun();
this.full_url = location.href;
this.qrcode();
if (this.agent !== '') {
this.check_msg();
}
}, filters: {
getiOStype: function (type) {
let ftype = '';

@ -286,7 +286,7 @@
</template>
<script>
import {changeInfoFun, getAuthcTokenFun, userinfos, wxLoginFun, wxutils} from '@/restful'
import {changeInfoFun, getAuthcTokenFun, userinfos, wxBindFun, wxLoginFun, wxutils} from '@/restful'
import {deepCopy, geetest} from "@/utils";
export default {
@ -359,49 +359,41 @@ export default {
}
})
},
loop_get_wx_info(wx_login_ticket) {
loop_get_wx_info(wx_login_ticket, c_count = 1) {
if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试");
return
}
let c_count = 1;
// eslint-disable-next-line no-unused-vars
const loop_t = window.setInterval(res => {
if (!this.loop_flag) {
window.clearInterval(loop_t);
wxLoginFun(data => {
c_count += 1;
if (c_count > 30) {
return;
}
wxLoginFun(data => {
c_count += 1;
if (c_count > 120) {
window.clearInterval(loop_t);
}
if (data.code === 1000) {
window.clearInterval(loop_t);
if (this.userinfo.uid === data.userinfo.uid) {
this.$message.success("绑定成功");
this.wx_visible = false;
this.loop_flag = false;
}
} else if (data.code === 1005) {
window.clearInterval(loop_t);
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
if (data.code === 1000) {
if (this.userinfo.uid === data.userinfo.uid) {
this.$message.success("绑定成功");
this.wx_visible = false;
this.loop_flag = false;
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket}
})
}, 3000)
} else if (data.code === 1005) {
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
} else if (data.code === 1006) {
return this.loop_get_wx_info(wx_login_ticket, c_count)
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket}
})
},
wxLogin() {
this.wx_visible = !this.wx_visible;
this.wx_login_qr_url = '';
if (this.wx_visible) {
userinfos(data => {
wxBindFun(data => {
if (data.code === 1000) {
this.wx_login_qr_url = data.data.qr;
this.loop_flag = true;

@ -238,6 +238,23 @@ export function wxLoginFun(callBack, params, load = true) {
);
}
/**微信公众号关注绑定 */
export function wxBindFun(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/third.wx.bind',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**获取验证token */
export function getAuthTokenFun(callBack, params, load = true) {
getData(

@ -20,7 +20,8 @@ from api.views.apps import AppsView, AppInfoView, AppReleaseInfoView, AppsQrcode
from api.views.domain import DomainCnameView, DomainInfoView
from api.views.download import ShortDownloadView, InstallView, DownloadView
from api.views.login import LoginView, UserInfoView, RegistView, AuthorizationView, ChangeAuthorizationView, \
UserApiTokenView, CertificationView, ChangeInfoView, WeChatLoginView, WeChatLoginCheckView
UserApiTokenView, CertificationView, ChangeInfoView
from api.views.login_wx import WeChatLoginView, WeChatLoginCheckView, WeChatBindView
from api.views.logout import LogoutView
from api.views.order import PriceView, OrderView, PaySuccess
from api.views.report import ReportView
@ -60,6 +61,7 @@ urlpatterns = [
re_path("^domain_info$", DomainInfoView.as_view()),
re_path("^mp.weixin$", ValidWxChatToken.as_view()),
re_path("^third.wx.login$", WeChatLoginView.as_view()),
re_path("^third.wx.bind$", WeChatBindView.as_view()),
re_path("^third.wx.sync$", WeChatLoginCheckView.as_view()),
re_path("^twx/info$", ThirdWxAccount.as_view()),
re_path(r"^install/(?P<app_id>\w+)$", InstallView.as_view(), name="install"),

@ -10,13 +10,11 @@ from api.utils.modelutils import get_min_default_domain_cname_obj, add_remote_in
from api.utils.response import BaseResponse
from api.utils.serializer import UserInfoSerializer, CertificationSerializer, UserCertificationSerializer
from api.utils.utils import get_random_username, check_username_exists, set_user_token, clean_user_token_and_cache
from common.base.baseutils import is_valid_phone, is_valid_email, get_real_ip_address
from common.base.baseutils import is_valid_phone, is_valid_email
from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config
from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle, GetAuthC1Throttle, GetAuthC2Throttle
from common.libs.mp.wechat import make_wx_login_qrcode, show_qrcode_url
from common.utils.caches import login_auth_failed, set_wx_ticket_login_info_cache, get_wx_ticket_login_info_cache, \
new_user_download_times_gift
from common.utils.caches import login_auth_failed, new_user_download_times_gift
from common.utils.sendmsg import get_sender_sms_token, is_valid_sender_code, get_sender_email_token
logger = logging.getLogger(__name__)
@ -451,24 +449,6 @@ class RegistView(APIView):
return Response(response.dict)
def wx_qr_code_response(ret, code, qr_info, ip_addr):
if code:
logger.info(f"微信登录码获取成功, {qr_info}")
errmsg = qr_info.get('errmsg')
if errmsg:
ret.code = 1003
ret.msg = "微信登录码获取失败,请稍后"
else:
ticket = qr_info.get('ticket')
if ticket:
set_wx_ticket_login_info_cache(ticket, {'ip_addr': ip_addr})
ret.data = {'qr': show_qrcode_url(ticket), 'ticket': ticket}
else:
ret.code = 1003
ret.msg = "微信登录码获取失败,请稍后"
return Response(ret.dict)
class UserInfoView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
@ -578,12 +558,6 @@ class UserInfoView(APIView):
return Response(res.dict)
def post(self, request):
ret = BaseResponse()
uid = request.user.uid
code, qr_info = make_wx_login_qrcode(f"web.bind.{uid}")
return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request))
class AuthorizationView(APIView):
throttle_classes = [GetAuthC1Throttle, GetAuthC2Throttle]
@ -780,45 +754,3 @@ class ChangeInfoView(APIView):
response.data['change_type'] = Config.CHANGER.get("change_type")
response.data['enable'] = allow_f
return Response(response.dict)
class WeChatLoginView(APIView):
throttle_classes = [VisitRegister1Throttle, VisitRegister2Throttle]
def get(self, request):
ret = BaseResponse()
if get_login_type().get('third', '').get('wxp'):
code, qr_info = make_wx_login_qrcode()
return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request))
return Response(ret.dict)
class WeChatLoginCheckView(APIView):
def post(self, request):
ret = BaseResponse()
if not get_login_type().get('third', '').get('wxp'):
return Response(ret.dict)
ticket = request.data.get("ticket")
if ticket:
wx_ticket_data = get_wx_ticket_login_info_cache(ticket)
if wx_ticket_data and wx_ticket_data.get('pk'):
if wx_ticket_data.get('pk', -1) == -1:
ret.msg = "还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定"
ret.code = 1005
else:
user = UserInfo.objects.filter(pk=wx_ticket_data['pk']).first()
if user.is_active:
key, user_info = set_user_token(user, request)
serializer = UserInfoSerializer(user_info)
data = serializer.data
ret.msg = "验证成功!"
ret.userinfo = data
ret.token = key
else:
ret.msg = "用户被禁用"
ret.code = 1005
else:
ret.code = 1006
else:
ret.code = 1006
return Response(ret.dict)

@ -8,6 +8,9 @@ from api.utils.response import BaseResponse
from api.utils.serializer import UserInfoSerializer
from api.utils.utils import set_user_token
from common.base.baseutils import get_real_ip_address
from common.base.magic import get_pending_result
from common.core.auth import ExpiringTokenAuthentication
from common.core.sysconfig import Config
from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle
from common.libs.mp.wechat import make_wx_login_qrcode, show_qrcode_url
from common.utils.caches import set_wx_ticket_login_info_cache, get_wx_ticket_login_info_cache
@ -38,21 +41,36 @@ class WeChatLoginView(APIView):
def get(self, request):
ret = BaseResponse()
if get_login_type().get('third', '').get('wxp'):
if Config.LOGIN.get("login_type").get('third', '').get('wxp'):
code, qr_info = make_wx_login_qrcode()
return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request))
return Response(ret.dict)
class WeChatBindView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def post(self, request):
ret = BaseResponse()
uid = request.user.uid
code, qr_info = make_wx_login_qrcode(f"web.bind.{uid}")
return wx_qr_code_response(ret, code, qr_info, get_real_ip_address(request))
def login_res(result, *args, **kwargs):
return result and result.get('pk')
class WeChatLoginCheckView(APIView):
def post(self, request):
ret = BaseResponse()
if not get_login_type().get('third', '').get('wxp'):
if not Config.LOGIN.get("login_type").get('third', '').get('wxp'):
return Response(ret.dict)
ticket = request.data.get("ticket")
if ticket:
wx_ticket_data = get_wx_ticket_login_info_cache(ticket)
if wx_ticket_data and wx_ticket_data.get('pk'):
status, wx_ticket_data = get_pending_result(get_wx_ticket_login_info_cache, login_res, ticket=ticket,
locker_key=ticket)
if status:
if wx_ticket_data.get('pk', -1) == -1:
ret.msg = "还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定"
ret.code = 1005

@ -72,10 +72,10 @@ def update_or_create_wx_userinfo(to_user, user_obj, create=True):
if code:
wx_user_info = {
'openid': wx_user_info.get('openid'),
'nickname': wx_user_info.get('nickname'),
'sex': wx_user_info.get('sex'),
'nickname': wx_user_info.get('nickname'), # 最新微信接口已经取消该字段
'sex': wx_user_info.get('sex'), # 最新微信接口已经取消该字段
'subscribe_time': wx_user_info.get('subscribe_time'),
'head_img_url': wx_user_info.get('headimgurl'),
'head_img_url': wx_user_info.get('headimgurl'), # 最新微信接口已经取消该字段
'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}",
'subscribe': wx_user_info.get('subscribe'),
}

@ -63,3 +63,21 @@ def call_function_try_attempts(try_attempts=3, sleep_time=2, failed_callback=Non
return wrapper
return decorator
def get_pending_result(func, expect_func, timeout=10, sleep_time=3, *args, **kwargs):
try:
with cache.lock("%s_%s" % ('get_pending_result', kwargs.get('locker_key')), timeout=timeout + sleep_time):
count = 1
del kwargs['locker_key']
while True:
result = func(*args, **kwargs)
if expect_func(result, *args, **kwargs):
return True, result
time.sleep(sleep_time)
if timeout < count * sleep_time:
return False, 'timeout'
count += 1
except Exception as e:
logger.warning(f'get pending result exception: {e}')
return False, 'timeout'

@ -209,3 +209,9 @@ class SystemConfigCache(RedisCacheBase):
def __init__(self, prefix_key):
self.cache_key = f"{CACHE_KEY_TEMPLATE.get('sysconfig_key')}_{prefix_key}"
super().__init__(self.cache_key)
class TaskStateCache(RedisCacheBase):
def __init__(self, app_pk, task_id):
self.cache_key = f"{CACHE_KEY_TEMPLATE.get('task_state_key')}_{app_pk}_{task_id}"
super().__init__(self.cache_key)

@ -260,6 +260,7 @@ CORS_ALLOW_HEADERS = (
CACHE_KEY_TEMPLATE = {
'sysconfig_key': 'sysconfig',
'task_state_key': 'task_state',
'user_can_download_key': 'user_can_download',
'download_times_key': 'app_download_times',
'make_token_key': 'make_token',

@ -16,6 +16,8 @@ from api.utils.modelutils import get_redirect_server_domain, add_remote_info_fro
get_app_download_uri
from api.utils.response import BaseResponse
from common.base.baseutils import get_real_ip_address, make_random_uuid, get_server_domain_from_request
from common.base.magic import get_pending_result
from common.cache.storage import TaskStateCache
from common.core.sysconfig import Config
from common.core.throttle import ReceiveUdidThrottle1, ReceiveUdidThrottle2
from common.utils.caches import check_app_permission
@ -78,6 +80,29 @@ class IosUDIDView(APIView):
"%s/%s?udid=%s%s" % (server_domain, short, format_udid_info.get("udid"), msg))
def except_result(result, *args, **kwargs):
app_info = kwargs.get('app_info')
logger.info(f"app {app_info} sign task state {result.state} AA {result.successful()}")
cache = TaskStateCache(app_info.pk, kwargs.get('task_id'))
if result.state == 'SUCCESS':
cache.del_storage_cache()
return True
cache_data = cache.get_storage_cache()
if not cache_data:
cache.set_storage_cache(result.state)
return True
else:
if cache_data == result.state:
return False
else:
cache.set_storage_cache(result.state)
return True
def task_func(task_id, *args, **kwargs):
return app.AsyncResult(task_id)
class TaskView(APIView):
def get(self, request, short):
@ -86,15 +111,16 @@ class TaskView(APIView):
if task_id:
app_info = Apps.objects.filter(short=short).first()
if app_info:
result = app.AsyncResult(task_id)
logger.info(f"app {app_info} sign task state {result.state} AA {result.successful()}")
if result.successful():
res.msg = result.get(propagate=False)
return Response(res.dict)
elif result.state in ['PENDING']:
res.msg = '签名队列中'
elif result.state in ['STARTED']:
res.msg = '正在签名中'
status, result = get_pending_result(task_func, except_result, task_id=task_id,
locker_key=task_id, app_info=app_info)
if status:
if result.successful():
res.msg = result.get(propagate=False)
return Response(res.dict)
elif result.state in ['PENDING']:
res.msg = '签名队列中'
elif result.state in ['STARTED']:
res.msg = '正在签名中'
else:
res.msg = ''
res.code = 1001

Loading…
Cancel
Save