diff --git a/fir_client/src/components/FirDownload.vue b/fir_client/src/components/FirDownload.vue index 772c9d5..267a4cd 100644 --- a/fir_client/src/components/FirDownload.vue +++ b/fir_client/src/components/FirDownload.vue @@ -39,7 +39,7 @@ - + { this.do_get_auth_token(n_params); }) @@ -517,7 +518,8 @@ export default { if (data.code === 1000) { let jdata = data.data; if (jdata.enable) { - this.captcha = data.data; + this.auth_rules = data.data.auth_rules; + this.captcha = this.auth_rules.captcha; if (data.data.s_list) { this.report_info_list = data.data.s_list; } diff --git a/fir_client/src/components/FirLogin.vue b/fir_client/src/components/FirLogin.vue index 5a10fc3..9a0855a 100644 --- a/fir_client/src/components/FirLogin.vue +++ b/fir_client/src/components/FirLogin.vue @@ -44,7 +44,7 @@ - @@ -113,9 +113,10 @@ export default { form: { email: '', password: '', - authcode: '' + verify_code: '' }, captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, + auth_rules: {}, activeName: 'username', allow_ways: {'third': {}}, rutitle: '', @@ -196,7 +197,7 @@ export default { } }, is_captcha() { - let captcha_flag = this.form.authcode.length === this.captcha.length; + let captcha_flag = this.form.verify_code.length === this.captcha.length; if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { captcha_flag = true } @@ -205,9 +206,9 @@ export default { onSubmit() { let email = this.form.email; let password = this.form.password; - let authcode = this.form.authcode; + let verify_code = this.form.verify_code; let login_type = 'up'; - let captcha_flag = this.form.authcode.length === this.captcha.length; + let captcha_flag = this.form.verify_code.length === this.captcha.length; if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { captcha_flag = true } @@ -248,12 +249,12 @@ export default { let params = { "username": email, "password": password, - "authcode": authcode, + "verify_code": verify_code, "captcha_key": this.captcha.captcha_key, "login_type": login_type, }; this.login_disable = true; - if (this.captcha.geetest) { + if (this.auth_rules.geetest) { geetest(this, this.form.email, params, (n_params) => { this.do_login(n_params); }) @@ -318,10 +319,11 @@ export default { get_auth_code() { loginFun(data => { if (data.code === 1000) { - this.captcha = data.data; + this.auth_rules = data.data.auth_rules + this.captcha = this.auth_rules.captcha; this.allow_ways = data.data.login_type; this.register_enable = data.data.register_enable; - this.form.authcode = ''; + this.form.verify_code = ''; this.set_rtitle(); this.set_activename(); } else { diff --git a/fir_client/src/components/FirRegist.vue b/fir_client/src/components/FirRegist.vue index c56bf20..1ae45be 100644 --- a/fir_client/src/components/FirRegist.vue +++ b/fir_client/src/components/FirRegist.vue @@ -29,7 +29,7 @@ - + { this.do_get_auth_token(n_params); }) @@ -277,8 +279,8 @@ export default { if (!this.docheck()) { return } - let authcode = this.form.authcode; - let captcha_flag = authcode.length === this.captcha.length; + let verify_code = this.form.verify_code; + let captcha_flag = verify_code.length === this.captcha.length; if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { captcha_flag = true } @@ -292,7 +294,7 @@ export default { "auth_key": this.form.seicode }; this.register_disable = true; - if (this.captcha.geetest) { + if (this.auth_rules.geetest) { geetest(this, this.form.email, params, (n_params) => { this.do_register(n_params); }) diff --git a/fir_client/src/components/FirResetPwd.vue b/fir_client/src/components/FirResetPwd.vue index ebc0ccd..76932da 100644 --- a/fir_client/src/components/FirResetPwd.vue +++ b/fir_client/src/components/FirResetPwd.vue @@ -20,7 +20,7 @@ - @@ -81,11 +81,12 @@ export default { form: { email: '', password: '', - authcode: '', + verify_code: '', seicode: '', auth_token: '' }, captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, + auth_rules: {}, activeName: 'username', allow_ways: {}, rutitle: '', @@ -108,7 +109,7 @@ export default { } }, is_captcha() { - let captcha_flag = this.form.authcode.length === this.captcha.length; + let captcha_flag = this.form.verify_code.length === this.captcha.length; if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { captcha_flag = true } @@ -116,8 +117,8 @@ export default { }, onSubmit() { let email = this.form.email; - let authcode = this.form.authcode; - let captcha_flag = this.form.authcode.length === this.captcha.length; + let verify_code = this.form.verify_code; + let captcha_flag = this.form.verify_code.length === this.captcha.length; if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { captcha_flag = true } @@ -127,7 +128,7 @@ export default { if (checke || checkp) { let params = { "username": email, - "authcode": authcode, + "verify_code": verify_code, "captcha_key": this.captcha.captcha_key, "login_type": 'reset', }; @@ -140,7 +141,7 @@ export default { params['auth_token'] = auth_token } this.login_disable = true; - if (this.captcha.geetest) { + if (this.auth_rules.geetest) { geetest(this, this.form.email, params, (n_params) => { this.do_login(n_params); }) @@ -196,9 +197,10 @@ export default { get_auth_code() { loginFun(data => { if (data.code === 1000) { - this.captcha = data.data; + this.auth_rules = data.data.auth_rules; + this.captcha = this.auth_rules.captcha; this.register_enable = data.data.register_enable; - this.form.authcode = ''; + this.form.verify_code = ''; } else { this.$message({ message: data.msg, diff --git a/fir_client/src/components/ShortDownload.vue b/fir_client/src/components/ShortDownload.vue index 3ddedf0..202e9fa 100644 --- a/fir_client/src/components/ShortDownload.vue +++ b/fir_client/src/components/ShortDownload.vue @@ -43,7 +43,7 @@
- + 验证码
@@ -355,12 +355,13 @@ export default { data() { return { captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, + auth_rules: {}, report_info: { 'username': '', 'report_type': '', 'report_reason': '', 'seicode': '', - 'authcode': '', + 'verify_code': '', 'email': '', 'auth_token': '', 'act': 'email', @@ -459,12 +460,12 @@ export default { } let picode = { - "authcode": this.report_info.authcode, + "verify_code": this.report_info.verify_code, "captcha_key": this.captcha.captcha_key, "report": this.currentappinfo.app_id }; let params = {'act': this.report_info.act, 'target': this.report_info.email, 'ext': picode}; - if (this.captcha.geetest) { + if (this.auth_rules.geetest) { geetest(this, this.report_info.email, params, (n_params) => { this.do_get_auth_token(n_params); }) @@ -487,7 +488,8 @@ export default { if (data.code === 1000) { let jdata = data.data; if (jdata.enable) { - this.captcha = data.data; + this.auth_rules = data.data.auth_rules; + this.captcha = this.auth_rules.captcha; if (data.data.s_list) { this.report_info_list = data.data.s_list; } diff --git a/fir_client/src/components/user/FirUserProfileCertification.vue b/fir_client/src/components/user/FirUserProfileCertification.vue index 927e270..1aa948d 100644 --- a/fir_client/src/components/user/FirUserProfileCertification.vue +++ b/fir_client/src/components/user/FirUserProfileCertification.vue @@ -100,7 +100,7 @@ - + - + - + { this.get_phone_code(n_params); }) @@ -359,12 +360,14 @@ export default { }, get_auth_code() { - if (this.form.authcode) { - this.form.authcode = ''; + if (this.form.verify_code) { + this.form.verify_code = ''; } changeInfoFun(data => { if (data.code === 1000) { - this.captcha = data.data; + this.change_type = data.data.change_type; + this.auth_rules = data.data.auth_rules; + this.captcha = this.auth_rules.captcha if (this.captcha.captcha_key) { this.form.captcha_key = this.captcha.captcha_key; } diff --git a/fir_client/src/components/user/FirUserProfileInfo.vue b/fir_client/src/components/user/FirUserProfileInfo.vue index 68cb3ac..be35bd8 100644 --- a/fir_client/src/components/user/FirUserProfileInfo.vue +++ b/fir_client/src/components/user/FirUserProfileInfo.vue @@ -114,7 +114,7 @@ - @@ -122,7 +122,7 @@ - + 保存 @@ -131,11 +131,11 @@ - - + - + - @@ -175,7 +175,7 @@ - + 保存 @@ -185,11 +185,11 @@ - - + - + { if (data.code === 1000) { - this.captcha = data.data; + this.auth_rules = data.data.auth_rules; + this.captcha = this.auth_rules.captcha; + this.change_type = data.data.change_type; this.userinfo.captcha_key = this.captcha.captcha_key; - if (this.userinfo.authcode) { - this.userinfo.authcode = ''; + if (this.userinfo.verify_code) { + this.userinfo.verify_code = ''; } } else { this.$message({ @@ -457,7 +461,7 @@ export default { auth_token: this.userinfo.auth_token, auth_key: this.userinfo.auth_key, captcha_key: this.userinfo.captcha_key, - authcode: this.userinfo.authcode, + verify_code: this.userinfo.verify_code, }; }, saveemail() { @@ -564,14 +568,14 @@ export default { }, getsmsemailcode(act, target) { let picode = { - "authcode": this.userinfo.authcode, + "verify_code": this.userinfo.verify_code, "captcha_key": this.captcha.captcha_key, }; let params = { 'act': act, 'target': target, 'ext': picode }; - if (this.captcha.geetest) { - geetest(this, this.form.email, target, params, (n_params) => { + if (this.auth_rules.geetest) { + geetest(this, target, params, (n_params) => { this.do_get_auth_token(n_params); }) } else { diff --git a/fir_ser/admin/views/login.py b/fir_ser/admin/views/login.py index 32e9cd8..905e856 100644 --- a/fir_ser/admin/views/login.py +++ b/fir_ser/admin/views/login.py @@ -9,8 +9,9 @@ import logging from django.contrib import auth from rest_framework.views import APIView +from api.utils.auth.util import AuthInfo from api.utils.serializer import UserInfoSerializer -from api.utils.utils import get_captcha, valid_captcha, set_user_token +from api.utils.utils import set_user_token from common.core.auth import ExpiringTokenAuthentication from common.core.response import ApiResponse from common.core.sysconfig import Config @@ -29,11 +30,11 @@ class LoginView(APIView): code = 1000 msg = 'success' data = None - if Config.LOGIN.get("captcha"): - is_valid = valid_captcha(receive.get("captcha_key", None), receive.get("authcode", None), username) - else: - is_valid = True - if is_valid: + # if Config.LOGIN.get("captcha"): + # is_valid = valid_captcha(receive.get("captcha_key", None), receive.get("verify_code", None), username) + # else: + # is_valid = True + if True: if login_auth_failed("get", username): password = receive.get("password") user = auth.authenticate(username=username, password=password) @@ -68,9 +69,8 @@ class LoginView(APIView): return ApiResponse(code=code, msg=msg, data=data) def get(self, request): - data = {} - if Config.LOGIN.get("captcha"): - data = get_captcha() + auth_obj = AuthInfo(Config.LOGIN.get("captcha"), False) + data = auth_obj.make_rules_info() return ApiResponse(data=data) diff --git a/fir_ser/api/utils/auth/captcha.py b/fir_ser/api/utils/auth/captcha.py new file mode 100644 index 0000000..8a29767 --- /dev/null +++ b/fir_ser/api/utils/auth/captcha.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# author: liuyu +# date: 2022/2/7 +import logging + +from captcha.helpers import captcha_image_url +from captcha.models import CaptchaStore + +from common.core.sysconfig import Config +from common.libs.storage.localApi import LocalStorage + +logger = logging.getLogger(__name__) + + +class CaptchaAuth(object): + def __init__(self, captcha_key='', g_type=1): + self.g_type = g_type + self.captcha_key = captcha_key + self.random_char_fun = self.__generator() + + def __generator(self): + if self.g_type == 1: + # 随机字符串 + return 'captcha.helpers.random_char_challenge' + elif self.g_type == 2: + # 数学运算 + return 'captcha.helpers.math_challenge' + return None + + def __get_captcha_obj(self): + return CaptchaStore.objects.filter(hashkey=self.captcha_key).first() + + def generate(self): + self.captcha_key = CaptchaStore.generate_key(self.random_char_fun) + captcha_image = captcha_image_url(self.captcha_key) + captcha_obj = self.__get_captcha_obj() + code_length = 0 + if captcha_obj: + code_length = len(captcha_obj.response) + local_storage = LocalStorage(**Config.IOS_PMFILE_DOWNLOAD_DOMAIN) + return {"captcha_image": "/".join([local_storage.get_base_url(), captcha_image.strip("/"), '']), + "captcha_key": self.captcha_key, + "length": code_length} + + def valid(self, verify_code): + captcha_obj = self.__get_captcha_obj() + logger.info(f"captcha_key:{self.captcha_key} verify_code:{verify_code} challenge:{captcha_obj}") + if captcha_obj: + if self.captcha_key and verify_code and verify_code.strip(" ").lower() == captcha_obj.response.lower(): + return True + return False diff --git a/fir_ser/api/utils/auth/geetest.py b/fir_ser/api/utils/auth/geetest.py new file mode 100644 index 0000000..8b89d99 --- /dev/null +++ b/fir_ser/api/utils/auth/geetest.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# author: liuyu +# date: 2022/2/7 +import hashlib +import logging + +from common.libs.geetest.geetest_utils import first_register, second_validate + +logger = logging.getLogger(__name__) + + +class GeeTestAuth(object): + def __init__(self, unique_key='', ip_address=''): + self.ip_address = ip_address + self.unique_key = unique_key + + def generate(self): + assert self.unique_key + assert self.ip_address + sha = hashlib.sha1(str(self.unique_key).encode("utf-8")) + return first_register(sha.hexdigest(), self.ip_address) + + def valid(self, geetest_data): + return second_validate(geetest_data).get("result", "") == "success" diff --git a/fir_ser/api/utils/auth/mfa.py b/fir_ser/api/utils/auth/mfa.py new file mode 100644 index 0000000..638201b --- /dev/null +++ b/fir_ser/api/utils/auth/mfa.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# author: liuyu +# date: 2022/2/7 +import logging + +logger = logging.getLogger(__name__) diff --git a/fir_ser/api/utils/auth/util.py b/fir_ser/api/utils/auth/util.py new file mode 100644 index 0000000..5bd4681 --- /dev/null +++ b/fir_ser/api/utils/auth/util.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# author: liuyu +# date: 2022/2/7 +import logging + +from api.utils.auth.captcha import CaptchaAuth +from api.utils.auth.geetest import GeeTestAuth + +logger = logging.getLogger(__name__) + +""" +验证流程 +微信公众号验证码,短信验证码,邮件验证码 +图片验证码,mfa验证,最后将数据通过geetest验证发送到后台进验证 +""" + + +class AuthInfo(object): + def __init__(self, captcha=False, geetest=False): + """ + { + "captcha": True, # 是否开启注册字母验证码 + "geetest": False, # 是否开启geetest验证,如要开启请先配置geetest + } + """ + self.captcha = captcha + self.geetest = geetest + + def make_rules_info(self): + data = {} + if self.captcha: + data['captcha'] = CaptchaAuth().generate() + if self.geetest: + data['geetest'] = True + return data + + def make_geetest_info(self, unique_key='', ip_address=''): + if self.geetest: + return GeeTestAuth(unique_key, ip_address).generate() + return {} + + def valid(self, data): + if self.captcha: + captcha_key = data.get("captcha_key", '') + verify_code = data.get("verify_code", '') + is_valid = CaptchaAuth(captcha_key=captcha_key).valid(verify_code) + if not is_valid: + return False, '验证码有误' + + if self.geetest: + """ + "geetest_challenge" # 极验二次验证表单传参字段 chllenge + "geetest_validate" # 极验二次验证表单传参字段 validate + "geetest_seccode" # 极验二次验证表单传参字段 seccode + """ + geetest = data.get("geetest", {}) + is_valid = GeeTestAuth().valid(geetest) + + # geetest_data = { + # "geetest_challenge": geetest.get("geetest_challenge"), + # "geetest_validate": geetest.get("geetest_validate"), + # "geetest_seccode": geetest.get("geetest_seccode") + # } + # is_valid = GeeTestAuth().valid(geetest_data) + if not is_valid: + return False, '滑动验证有误' + + return True, '验证通过' diff --git a/fir_ser/api/utils/auth/weixin.py b/fir_ser/api/utils/auth/weixin.py new file mode 100644 index 0000000..638201b --- /dev/null +++ b/fir_ser/api/utils/auth/weixin.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# author: liuyu +# date: 2022/2/7 +import logging + +logger = logging.getLogger(__name__) diff --git a/fir_ser/api/utils/utils.py b/fir_ser/api/utils/utils.py index ea5c7e5..9f2616d 100644 --- a/fir_ser/api/utils/utils.py +++ b/fir_ser/api/utils/utils.py @@ -8,52 +8,22 @@ import datetime import logging import os -from captcha.helpers import captcha_image_url -from captcha.models import CaptchaStore from django.core.cache import cache from api.models import UserInfo, AppReleaseInfo, AppScreenShot, Token, UserAdDisplayInfo from api.utils.modelutils import get_app_d_count_by_app_id from common.base.baseutils import get_real_ip_address -from common.cache.storage import UserTokenCache, TempCache +from common.cache.storage import UserTokenCache from common.core.signals import xsign_migrate_data_signal, xsign_clean_data_signal -from common.core.sysconfig import Config from common.libs.storage.localApi import LocalStorage from common.utils.caches import consume_user_download_times -from common.utils.sendmsg import SendMessage from common.utils.storage import Storage -from common.utils.token import generate_numeric_token_of_length, generate_alphanumeric_token_of_length, make_token, \ - verify_token +from common.utils.token import generate_alphanumeric_token_of_length from fir_ser.settings import MEDIA_ROOT logger = logging.getLogger(__name__) -def get_captcha(): - # 随机字符串 - random_char_fun = 'captcha.helpers.random_char_challenge' - - # 数学运算 - math_fun = 'captcha.helpers.math_challenge' - - captcha_key = CaptchaStore.generate_key(random_char_fun) - captcha_image = captcha_image_url(captcha_key) - local_storage = LocalStorage(**Config.IOS_PMFILE_DOWNLOAD_DOMAIN) - return {"captcha_image": "/".join([local_storage.get_base_url(), captcha_image.strip("/"), '']), - "captcha_key": captcha_key, - "length": len(CaptchaStore.objects.filter(hashkey=captcha_key).first().response)} - - -def valid_captcha(captcha_key, code, username): - if username: - captcha_obj = CaptchaStore.objects.filter(hashkey=captcha_key).values("response").first() - logger.info(f"captcha_key:{captcha_key} code:{code} challenge:{captcha_obj}") - if captcha_obj: - if captcha_key and code and code.strip(" ").lower() == captcha_obj.get("response").lower(): - return True - return False - - def upload_oss_default_head_img(user_obj, storage_obj): head_img_full_path = os.path.join(MEDIA_ROOT, "head_img.jpeg") if storage_obj: @@ -61,40 +31,6 @@ def upload_oss_default_head_img(user_obj, storage_obj): return storage_obj.upload_file(head_img_full_path) -def get_sender_token(sender, user_id, target, action, msg=None): - code = generate_numeric_token_of_length(6) - if msg: - code = msg - token = make_token(code, time_limit=300, key=user_id) - TempCache(user_id, token).set_storage_cache(target, 60 * 5) - if action in ('change', 'password', 'register', 'login', 'common'): - sender.send_msg_by_act(target, code, action) - elif action == 'msg': - sender.send_email_msg(target, msg) - else: - logger.error(f"get_sender_token failed. action is {action}") - return None, None - return token, code - - -def get_sender_sms_token(key, phone, action, msg=None): - sender = SendMessage('sms') - if sender.sender: - return get_sender_token(sender, key, phone, action, msg) - return False, False - - -def is_valid_sender_code(key, token, code, success_once=False): - return verify_token(token, code, success_once), TempCache(key, token).get_storage_cache() - - -def get_sender_email_token(key, email, action, msg=None): - sender = SendMessage('email') - if sender.sender: - return get_sender_token(sender, key, email, action, msg) - return False, False - - def check_username_exists(username): user_obj = UserInfo.objects.filter(username=username).values("username").first() if user_obj and user_obj['username'] == username: @@ -109,15 +45,6 @@ def get_random_username(length=16): return username -def send_ios_developer_active_status(user_info, msg): - act = 'email' - email = user_info.email - if email: - get_sender_email_token(act, email, 'msg', msg) - else: - logger.warning(f"user {user_info} has no email. so {msg} can't send!") - - def get_filename_from_apptype(filename, apptype): if apptype == 0: filename = filename + '.apk' diff --git a/fir_ser/api/views/login.py b/fir_ser/api/views/login.py index 11e5dcc..f705909 100644 --- a/fir_ser/api/views/login.py +++ b/fir_ser/api/views/login.py @@ -5,20 +5,19 @@ from rest_framework.response import Response from rest_framework.views import APIView from api.models import UserInfo, UserCertificationInfo, CertificationInfo, Apps +from api.utils.auth.util import AuthInfo from api.utils.modelutils import get_min_default_domain_cname_obj, add_remote_info_from_request from api.utils.response import BaseResponse from api.utils.serializer import UserInfoSerializer, CertificationSerializer, UserCertificationSerializer -from api.utils.utils import get_captcha, valid_captcha, \ - get_sender_sms_token, is_valid_sender_code, get_sender_email_token, get_random_username, \ - check_username_exists, set_user_token, clean_user_token_and_cache +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.core.auth import ExpiringTokenAuthentication from common.core.sysconfig import Config from common.core.throttle import VisitRegister1Throttle, VisitRegister2Throttle, GetAuthC1Throttle, GetAuthC2Throttle -from common.libs.geetest.geetest_utils import first_register, second_validate 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.sendmsg import get_sender_sms_token, is_valid_sender_code, get_sender_email_token logger = logging.getLogger(__name__) @@ -251,130 +250,117 @@ class LoginView(APIView): response = BaseResponse() receive = request.data username = receive.get("username", None) - if Config.LOGIN.get("captcha"): - is_valid = valid_captcha(receive.get("captcha_key", None), receive.get("authcode", None), username) - else: - is_valid = True - if is_valid: - if Config.LOGIN.get("geetest"): - geetest = receive.get("geetest", None) - if geetest and second_validate(geetest).get("result", "") == "success": - pass - else: - response.code = 1001 - response.msg = "geetest 验证有误" - return Response(response.dict) + auth_obj = AuthInfo(Config.LOGIN.get("captcha"), Config.LOGIN.get("geetest")) + is_valid, msg = auth_obj.valid(receive) + if not is_valid or username is None: + response.code = 1001 + response.msg = msg + return Response(response.dict) - login_type = receive.get("login_type", None) - seicode = receive.get("seicode", None) - if login_auth_failed("get", username): - if login_type == 'reset': - user1_obj = None - user2_obj = None - act = None - if is_valid_email(username): - user1_obj = UserInfo.objects.filter(email=username).first() - act = "email" - - if is_valid_phone(username): - user2_obj = UserInfo.objects.filter(mobile=username).first() - act = "sms" - - if user1_obj or user2_obj: - user_obj = user1_obj if user1_obj else user2_obj - password = get_random_username()[:16] - - if login_auth_failed("get", user_obj.uid): - login_auth_failed("set", user_obj.uid) - - if seicode: - is_valid, target = is_valid_sender_code(act, receive.get("auth_token", None), seicode) - if is_valid and str(target) == str(username): - if user2_obj: - a, b = get_sender_sms_token('sms', username, 'password', password) - else: - a, b = get_sender_email_token('email', username, 'password', password) - if a and b: - reset_user_pwd(user_obj, password) - login_auth_failed("del", username) - msg = f'{user_obj} 找回密码成功,您的新密码为 {password} 请用新密码登录之后,及时修改密码' - add_remote_info_from_request(request, msg) - logger.warning(f'{user_obj} 找回密码成功,您的新密码为 {password} 请用新密码登录之后,及时修改密码') - else: - response.code = 1007 - response.msg = "密码重置失败,请稍后重试或者联系管理员" + login_type = receive.get("login_type", None) + seicode = receive.get("seicode", None) + if login_auth_failed("get", username): + if login_type == 'reset': + user1_obj = None + user2_obj = None + act = None + if is_valid_email(username): + user1_obj = UserInfo.objects.filter(email=username).first() + act = "email" + + if is_valid_phone(username): + user2_obj = UserInfo.objects.filter(mobile=username).first() + act = "sms" + + if user1_obj or user2_obj: + user_obj = user1_obj if user1_obj else user2_obj + password = get_random_username()[:16] + + if login_auth_failed("get", user_obj.uid): + login_auth_failed("set", user_obj.uid) + + if seicode: + is_valid, target = is_valid_sender_code(act, receive.get("auth_token", None), seicode) + if is_valid and str(target) == str(username): + if user2_obj: + a, b = get_sender_sms_token('sms', username, 'password', password) else: - response.code = 1009 - response.msg = "验证码有误,请检查或者重新尝试" - + a, b = get_sender_email_token('email', username, 'password', password) + if a and b: + reset_user_pwd(user_obj, password) + login_auth_failed("del", username) + msg = f'{user_obj} 找回密码成功,您的新密码为 {password} 请用新密码登录之后,及时修改密码' + add_remote_info_from_request(request, msg) + logger.warning(f'{user_obj} 找回密码成功,您的新密码为 {password} 请用新密码登录之后,及时修改密码') + else: + response.code = 1007 + response.msg = "密码重置失败,请稍后重试或者联系管理员" else: - res = check_register_userinfo(username, act, 'change', 'reset') - return Response(res.dict) + response.code = 1009 + response.msg = "验证码有误,请检查或者重新尝试" else: - response.code = 1008 - response.msg = "手机或者邮箱已经超过最大发送,请24小时后重试" - else: - response.code = 1002 - response.msg = "邮箱或者手机号不存在" - return Response(response.dict) + res = check_register_userinfo(username, act, 'change', 'reset') + return Response(res.dict) - password = receive.get("password") - user = get_authenticate(username, password, login_type, get_login_type()) - logger.info(f"username:{username} password:{password}") - if user: - if user.is_active: - login_auth_failed("del", username) - # update the token - key, user_info = set_user_token(user, request) - serializer = UserInfoSerializer(user_info, ) - data = serializer.data - response.msg = "验证成功!" - response.userinfo = data - response.token = key - add_remote_info_from_request(request, f"登录成功, token: {key}") else: - response.msg = "用户被禁用" - response.code = 1005 + response.code = 1008 + response.msg = "手机或者邮箱已经超过最大发送,请24小时后重试" else: - login_auth_failed("set", username) - # - # try: - # UserInfo.objects.get(username=username) - response.msg = "密码账户有误或用户被禁用" response.code = 1002 - # except UserInfo.DoesNotExist: - # response.msg = "用户不存在!" - # response.code = 1003 + response.msg = "邮箱或者手机号不存在" + return Response(response.dict) + + password = receive.get("password") + user = get_authenticate(username, password, login_type, get_login_type()) + logger.info(f"username:{username} password:{password}") + if user: + if user.is_active: + login_auth_failed("del", username) + # update the token + key, user_info = set_user_token(user, request) + serializer = UserInfoSerializer(user_info, ) + data = serializer.data + response.msg = "验证成功!" + response.userinfo = data + response.token = key + add_remote_info_from_request(request, f"登录成功, token: {key}") + else: + response.msg = "用户被禁用" + response.code = 1005 else: - response.code = 1006 - logger.error(f"username:{username} failed too try , locked") - response.msg = "用户登录失败次数过多,已被锁定,请1小时之后再次尝试" + login_auth_failed("set", username) + # + # try: + # UserInfo.objects.get(username=username) + response.msg = "密码账户有误或用户被禁用" + response.code = 1002 + # except UserInfo.DoesNotExist: + # response.msg = "用户不存在!" + # response.code = 1003 else: - response.code = 1001 - response.msg = "验证码有误" + response.code = 1006 + logger.error(f"username:{username} failed too try , locked") + response.msg = "用户登录失败次数过多,已被锁定,请1小时之后再次尝试" add_remote_info_from_request(request, response.msg) return Response(response.dict) def get(self, request): response = BaseResponse() response.data = {} - if Config.LOGIN.get("captcha"): - response.data = get_captcha() - if Config.LOGIN.get("geetest"): - response.data['geetest'] = True + auth_obj = AuthInfo(Config.LOGIN.get("captcha"), Config.LOGIN.get("geetest")) + response.data['auth_rules'] = auth_obj.make_rules_info() response.data['login_type'] = get_login_type() allow_f = Config.REGISTER.get("enable") response.data['register_enable'] = allow_f return Response(response.dict) def put(self, request): - import hashlib response = BaseResponse() user_id = request.data.get('user_id', None) if user_id: - sha = hashlib.sha1(str(user_id).encode("utf-8")) - response.data = first_register(sha.hexdigest(), request.META.get('REMOTE_ADDR')) + auth_obj = AuthInfo(Config.LOGIN.get("captcha"), Config.LOGIN.get("geetest")) + response.data = auth_obj.make_geetest_info(user_id, request.META.get('REMOTE_ADDR')) else: response.code = 1002 response.msg = '参数错误' @@ -404,14 +390,12 @@ class RegistView(APIView): response.msg = "不允许该类型注册" return Response(response.dict) - if Config.REGISTER.get("geetest"): - geetest = request.data.get("geetest", None) - if geetest and second_validate(geetest).get("result", "") == "success": - pass - else: - response.code = 1018 - response.msg = "geetest验证有误" - return Response(response.dict) + auth_obj = AuthInfo(Config.REGISTER.get("captcha"), Config.REGISTER.get("geetest")) + is_valid, msg = auth_obj.valid(request.data) + if not is_valid: + response.code = 1001 + response.msg = msg + return Response(response.dict) is_valid, target = is_valid_sender_code(act, receive.get("auth_token", None), receive.get("auth_key", None)) if is_valid and str(target) == str(username): @@ -460,10 +444,8 @@ class RegistView(APIView): response.data = {} allow_f = Config.REGISTER.get("enable") if allow_f: - if Config.REGISTER.get("captcha"): - response.data = get_captcha() - if Config.REGISTER.get("geetest"): - response.data['geetest'] = True + auth_obj = AuthInfo(Config.REGISTER.get("captcha"), Config.REGISTER.get("geetest")) + response.data['auth_rules'] = auth_obj.make_rules_info() response.data['register_type'] = get_register_type() response.data['enable'] = allow_f return Response(response.dict) @@ -510,12 +492,13 @@ class UserInfoView(APIView): mobile = data.get("mobile", None) email = data.get("email", None) if mobile or email: - if Config.CHANGER.get('captcha'): - is_valid = valid_captcha(data.get("captcha_key", None), data.get("authcode", None), request) - if not is_valid: - res.code = 1008 - res.msg = "图片验证码异常" - return Response(res.dict) + + auth_obj = AuthInfo(Config.CHANGER.get("captcha"), False) + is_valid, msg = auth_obj.valid(data) + if not is_valid: + res.code = 1001 + res.msg = msg + return Response(res.dict) if old_password and sure_password: user = auth.authenticate(username=request.user.username, password=old_password) @@ -624,23 +607,14 @@ class AuthorizationView(APIView): else: p_data = Config.REGISTER - if p_data.get("captcha"): - is_valid = valid_captcha(ext.get("captcha_key", None), ext.get("authcode", None), target) - if ext and is_valid: - pass - else: - res.code = 1018 - res.msg = "图片验证码有误" - return Response(res.dict) - - if p_data.get("geetest"): - geetest = request.data.get("geetest", None) - if geetest and second_validate(geetest).get("result", "") == "success": - pass - else: - res.code = 1018 - res.msg = "geetest验证有误" - return Response(res.dict) + auth_obj = AuthInfo(p_data.get("captcha"), p_data.get("geetest")) + auth_data = {'geetest': request.data.get("geetest", None)} + auth_data.update(ext) + is_valid, msg = auth_obj.valid(auth_data) + if not is_valid: + res.code = 1001 + res.msg = msg + return Response(res.dict) if ext and ext.get('report'): app_obj = Apps.objects.filter(app_id=ext.get('report')).first() if app_obj: @@ -666,22 +640,15 @@ class ChangeAuthorizationView(APIView): target = request.data.get("target", None) ext = request.data.get("ext", None) f_type = request.data.get("ftype", None) - if Config.REGISTER.get("captcha"): - if ext and valid_captcha(ext.get("captcha_key", None), ext.get("authcode", None), target): - pass - else: - res.code = 1009 - res.msg = "图片验证码有误" - return Response(res.dict) - if Config.REGISTER.get("geetest"): - geetest = request.data.get("geetest", None) - if geetest and second_validate(geetest).get("result", "") == "success": - pass - else: - res.code = 1018 - res.msg = "geetest验证有误" - return Response(res.dict) + auth_obj = AuthInfo(Config.REGISTER.get("captcha"), Config.REGISTER.get("geetest")) + auth_data = {'geetest': request.data.get("geetest", None)} + auth_data.update(ext) + is_valid, msg = auth_obj.valid(auth_data) + if not is_valid or target is None: + res.code = 1001 + res.msg = msg + return Response(res.dict) res = check_change_userinfo(target, act, 'change', request.user, f_type) return Response(res.dict) @@ -754,15 +721,14 @@ class CertificationView(APIView): return Response(res.dict) try: - if Config.CHANGER.get("captcha"): - if data and valid_captcha(data.get("captcha_key", None), data.get("authcode", None), - data.get("mobile")): - del data["captcha_key"] - del data["authcode"] - else: - res.code = 1009 - res.msg = "图片验证码有误" - return Response(res.dict) + auth_obj = AuthInfo(Config.CHANGER.get("captcha"), False) + is_valid, msg = auth_obj.valid(data) + if not is_valid or not data.get("mobile"): + res.code = 1001 + res.msg = msg + return Response(res.dict) + del data["captcha_key"] + del data["verify_code"] if Config.CHANGER.get('change_type').get('sms'): is_valid, target = is_valid_sender_code('sms', data.get("auth_token", None), data.get("auth_key", None)) if is_valid and str(target) == str(data.get("mobile")): @@ -809,10 +775,8 @@ class ChangeInfoView(APIView): response.data = {} allow_f = Config.CHANGER.get("enable") if allow_f: - if Config.CHANGER.get("captcha"): - response.data = get_captcha() - if Config.CHANGER.get("geetest"): - response.data['geetest'] = True + auth_obj = AuthInfo(Config.CHANGER.get("captcha"), Config.CHANGER.get("geetest")) + response.data['auth_rules'] = auth_obj.make_rules_info() response.data['change_type'] = Config.CHANGER.get("change_type") response.data['enable'] = allow_f return Response(response.dict) diff --git a/fir_ser/api/views/login_wx.py b/fir_ser/api/views/login_wx.py new file mode 100644 index 0000000..09fc4fd --- /dev/null +++ b/fir_ser/api/views/login_wx.py @@ -0,0 +1,75 @@ +import logging + +from rest_framework.response import Response +from rest_framework.views import APIView + +from api.models import UserInfo +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.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 + +logger = logging.getLogger(__name__) + + +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 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) diff --git a/fir_ser/api/views/report.py b/fir_ser/api/views/report.py index 705a031..cc8ab00 100644 --- a/fir_ser/api/views/report.py +++ b/fir_ser/api/views/report.py @@ -9,14 +9,15 @@ from rest_framework.response import Response from rest_framework.views import APIView from api.models import Apps, AppReportInfo +from api.utils.auth.util import AuthInfo from api.utils.modelutils import add_remote_info_from_request from api.utils.response import BaseResponse from api.utils.serializer import AppReportSerializer -from api.utils.utils import is_valid_sender_code, get_captcha from common.base.baseutils import get_real_ip_address, get_choices_dict from common.core.sysconfig import Config from common.core.throttle import InstallThrottle2 from common.utils.caches import login_auth_failed +from common.utils.sendmsg import is_valid_sender_code logger = logging.getLogger(__name__) @@ -29,10 +30,8 @@ class ReportView(APIView): response.data = {} allow_f = Config.REPORT.get("enable") if allow_f: - if Config.REPORT.get("captcha"): - response.data = get_captcha() - if Config.REPORT.get("geetest"): - response.data['geetest'] = True + auth_obj = AuthInfo(Config.REPORT.get("captcha"), Config.REPORT.get("geetest")) + response.data['auth_rules'] = auth_obj.make_rules_info() response.data['report_type'] = Config.REPORT.get("report_type") response.data['s_list'] = get_choices_dict(AppReportInfo.report_type_choices) response.data['enable'] = allow_f diff --git a/fir_ser/common/core/sysconfig.py b/fir_ser/common/core/sysconfig.py index 2da50ac..d25767c 100644 --- a/fir_ser/common/core/sysconfig.py +++ b/fir_ser/common/core/sysconfig.py @@ -302,6 +302,14 @@ class ThirdPartConfCache(ConfigCacheBase): def SENDER(self): return super().get_value('SENDER', SENDERCONF.SENDER) + @property + def WHITE_SENDER_CODE(self): + return super().get_value('WHITE_SENDER_CODE', SENDERCONF.WHITE_SENDER_CODE) + + @property + def WHITE_SENDER_LIST(self): + return super().get_value('WHITE_SENDER_LIST', SENDERCONF.WHITE_SENDER_LIST) + class AppleDeveloperConfCache(ConfigCacheBase): def __init__(self): diff --git a/fir_ser/common/utils/sendmsg.py b/fir_ser/common/utils/sendmsg.py index 27587f6..69b95d7 100644 --- a/fir_ser/common/utils/sendmsg.py +++ b/fir_ser/common/utils/sendmsg.py @@ -6,63 +6,53 @@ import logging +from common.cache.storage import TempCache from common.core.sysconfig import Config from common.libs.sendmsg.aliyunApi import AliMsgSender from common.libs.sendmsg.emailApi import EmailMsgSender from common.libs.sendmsg.jiguangApi import JiGuangMsgSender +from common.utils.token import generate_numeric_token_of_length, make_token, verify_token logger = logging.getLogger(__name__) -def get_default_sender(send_type): - """ - sender_type : - 0: 邮件 - 1: 阿里云 - 2: 极光 - """ - for sender in Config.SENDER: - if sender.get("active", None): - sender_type = sender.get('type', None) - auth = sender.get('auth', {}) - if sender_type == 0 and send_type == 0: - return EmailMsgSender(**auth) - if sender_type == 1: - return AliMsgSender(**auth) - elif sender_type == 2: - return JiGuangMsgSender(**auth) - else: - continue - return None - - -def get_sms_sender(): - return get_default_sender(1) - - -def get_email_sender(): - return get_default_sender(0) - - class SendMessage(object): - def __init__(self, send_type): + def __init__(self, sender_type): """ - :param send_type: sms or email + :param sender_type: sms or email """ - self.send_type = send_type + self.sender_type = sender_type try: - if send_type == 'sms': - self.sender = get_sms_sender() - elif send_type == 'email': - self.sender = get_email_sender() + self.sender = self.__get_default_sender() if self.sender is None: raise except Exception as e: - logger.error(f"get {send_type} sender failed Exception:{e}") + logger.error(f"get {sender_type} sender failed Exception:{e}") self.sender = None + def __get_default_sender(self): + """ + sender_type : + 0: 邮件 + 1: 阿里云 + 2: 极光 + """ + for sender in Config.SENDER: + if sender.get("active", None): + sender_type = sender.get('type', None) + auth = sender.get('auth', {}) + if sender_type == 0 and self.sender_type == 'email': + return EmailMsgSender(**auth) + if sender_type == 1: + return AliMsgSender(**auth) + elif sender_type == 2: + return JiGuangMsgSender(**auth) + else: + continue + return None + def send_email_msg(self, email, text): - if self.send_type == 'email': + if self.sender_type == 'email': status, msg = self.sender.send_email_msg(email, text) logger.info(f"send_email_msg target:{email} text:{text} status:{status} msg:{msg}") return status, msg @@ -71,3 +61,41 @@ class SendMessage(object): status, msg = self.sender.send_msg_by_act(target, code, act) logger.info(f"send_{act}_msg target:{target} code:{code} status:{status} msg:{msg}") return status, msg + + +def get_sender_token(sender, user_id, target, action, msg=None): + code = generate_numeric_token_of_length(6) + if msg: + code = msg + if target in Config.WHITE_SENDER_LIST: + code = str(Config.WHITE_SENDER_CODE) + token = make_token(code, time_limit=300, key=user_id) + TempCache(user_id, token).set_storage_cache(target, 60 * 5) + if target in Config.WHITE_SENDER_LIST: + return token, code + if action in ('change', 'password', 'register', 'login', 'common'): + sender.send_msg_by_act(target, code, action) + elif action == 'msg': + sender.send_email_msg(target, msg) + else: + logger.error(f"get_sender_token failed. action is {action}") + return None, None + return token, code + + +def get_sender_sms_token(key, phone, action, msg=None): + sender = SendMessage('sms') + if sender.sender: + return get_sender_token(sender, key, phone, action, msg) + return False, False + + +def is_valid_sender_code(key, token, code, success_once=False): + return verify_token(token, code, success_once), TempCache(key, token).get_storage_cache() + + +def get_sender_email_token(key, email, action, msg=None): + sender = SendMessage('email') + if sender.sender: + return get_sender_token(sender, key, email, action, msg) + return False, False diff --git a/fir_ser/config.py b/fir_ser/config.py index e80b591..b944ae8 100644 --- a/fir_ser/config.py +++ b/fir_ser/config.py @@ -175,6 +175,10 @@ class STORAGEKEYCONF(object): class SENDERCONF(object): + WHITE_SENDER_CODE = '666666' # 白名单下,默认不发送真正验证码 + WHITE_SENDER_LIST = [ + '17600102953' + ] SENDER = [ { 'name': 'email', diff --git a/fir_ser/xsign/utils/ctasks.py b/fir_ser/xsign/utils/ctasks.py index 072043b..d35a93c 100644 --- a/fir_ser/xsign/utils/ctasks.py +++ b/fir_ser/xsign/utils/ctasks.py @@ -8,11 +8,11 @@ import logging import os import time -from api.utils.utils import send_ios_developer_active_status from common.core.sysconfig import Config from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE from xsign.models import UserInfo, AppIOSDeveloperInfo from xsign.utils.supersignutils import IosUtils +from xsign.utils.utils import send_ios_developer_active_status logger = logging.getLogger(__name__) diff --git a/fir_ser/xsign/utils/supersignutils.py b/fir_ser/xsign/utils/supersignutils.py index 305003f..cc5ad6e 100644 --- a/fir_ser/xsign/utils/supersignutils.py +++ b/fir_ser/xsign/utils/supersignutils.py @@ -17,8 +17,7 @@ from django.db.models import Count, F from api.models import UserInfo, AppReleaseInfo, Apps from api.utils.response import BaseResponse -from api.utils.utils import send_ios_developer_active_status, delete_local_files, \ - download_files_form_oss +from api.utils.utils import delete_local_files, download_files_form_oss from common.base.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \ get_format_time, make_app_uuid, make_from_user_uuid from common.base.magic import run_function_by_locker, call_function_try_attempts @@ -35,7 +34,7 @@ from xsign.utils.iossignapi import ResignApp, AppDeveloperApiV2 from xsign.utils.modelutils import get_ios_developer_public_num, check_ipa_is_latest_sign, \ update_or_create_developer_udid_info, check_uid_has_relevant, get_developer_udided from xsign.utils.serializer import BillAppInfoSerializer, BillDeveloperInfoSerializer -from xsign.utils.utils import delete_app_to_dev_and_file +from xsign.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status logger = logging.getLogger(__name__) diff --git a/fir_ser/xsign/utils/utils.py b/fir_ser/xsign/utils/utils.py index cf57006..1013dc9 100644 --- a/fir_ser/xsign/utils/utils.py +++ b/fir_ser/xsign/utils/utils.py @@ -6,6 +6,7 @@ import logging from api.utils.utils import delete_local_files +from common.utils.sendmsg import get_sender_email_token from common.utils.storage import Storage from xsign.models import APPToDeveloper @@ -20,3 +21,12 @@ def delete_app_to_dev_and_file(developer_obj, app_id): storage = Storage(developer_obj.user_id) storage.delete_file(binary_file) app_to_developer_obj.delete() + + +def send_ios_developer_active_status(user_info, msg): + act = 'email' + email = user_info.email + if email: + get_sender_email_token(act, email, 'msg', msg) + else: + logger.warning(f"user {user_info} has no email. so {msg} can't send!")