|
@@ -17,6 +17,7 @@ from Controller.CheckUserData import DataValid, RandomStr
|
|
|
from Model.models import Device_User, Role, UserExModel, CountryModel, MenuModel, FeedBackModel, StatResModel, \
|
|
from Model.models import Device_User, Role, UserExModel, CountryModel, MenuModel, FeedBackModel, StatResModel, \
|
|
|
SysMassModel, App_Info, SysMsgModel, DeviceSuperPassword, CustomizedPush, DeviceTypeModel, CustomCustomerOrderInfo
|
|
SysMassModel, App_Info, SysMsgModel, DeviceSuperPassword, CustomizedPush, DeviceTypeModel, CustomCustomerOrderInfo
|
|
|
from Object.AWS.AmazonS3Util import AmazonS3Util
|
|
from Object.AWS.AmazonS3Util import AmazonS3Util
|
|
|
|
|
+from Object.AWS.S3Email import S3Email
|
|
|
from Object.ApschedulerObject import ApschedulerObject
|
|
from Object.ApschedulerObject import ApschedulerObject
|
|
|
from Object.RedisObject import RedisObject
|
|
from Object.RedisObject import RedisObject
|
|
|
from Object.ResponseObject import ResponseObject
|
|
from Object.ResponseObject import ResponseObject
|
|
@@ -38,14 +39,14 @@ class LoginView(TemplateView):
|
|
|
return super(LoginView, self).dispatch(*args, **kwargs)
|
|
return super(LoginView, self).dispatch(*args, **kwargs)
|
|
|
|
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
|
+ ip = CommonService.get_ip_address(request)
|
|
|
request.encoding = 'utf-8'
|
|
request.encoding = 'utf-8'
|
|
|
request_dict = request.POST
|
|
request_dict = request.POST
|
|
|
language = request_dict.get('language', 'en')
|
|
language = request_dict.get('language', 'en')
|
|
|
response = ResponseObject(language, 'pc')
|
|
response = ResponseObject(language, 'pc')
|
|
|
|
|
+ return self.validates(ip, request_dict, response)
|
|
|
|
|
|
|
|
- return self.validates(request_dict, response)
|
|
|
|
|
-
|
|
|
|
|
- def validates(self, request_dict, response):
|
|
|
|
|
|
|
+ def validates(self, ip, request_dict, response):
|
|
|
username = request_dict.get('username', None)
|
|
username = request_dict.get('username', None)
|
|
|
password = request_dict.get('password', None)
|
|
password = request_dict.get('password', None)
|
|
|
password_version = request_dict.get('pwdVersion', 'V1')
|
|
password_version = request_dict.get('pwdVersion', 'V1')
|
|
@@ -55,34 +56,38 @@ class LoginView(TemplateView):
|
|
|
password = password.strip()
|
|
password = password.strip()
|
|
|
data_valid = DataValid()
|
|
data_valid = DataValid()
|
|
|
if data_valid.email_validate(username):
|
|
if data_valid.email_validate(username):
|
|
|
- return self.do_email_login(username, password, response, password_version)
|
|
|
|
|
|
|
+ return self.do_email_login(ip, username, password, response, password_version)
|
|
|
elif data_valid.mobile_validate(username):
|
|
elif data_valid.mobile_validate(username):
|
|
|
- return self.do_phone_login(username, password, response, password_version)
|
|
|
|
|
|
|
+ return self.do_phone_login(ip, username, password, response, password_version)
|
|
|
elif data_valid.name_validate(username):
|
|
elif data_valid.name_validate(username):
|
|
|
- return self.do_name_login(username, password, response, password_version)
|
|
|
|
|
|
|
+ return self.do_name_login(ip, username, password, response, password_version)
|
|
|
else:
|
|
else:
|
|
|
return response.json(107)
|
|
return response.json(107)
|
|
|
|
|
|
|
|
- def do_email_login(self, email, password, response, password_version):
|
|
|
|
|
|
|
+ def do_email_login(self, ip, email, password, response, password_version):
|
|
|
user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
|
|
user_qs = Device_User.objects.filter(Q(username=email) | Q(userEmail=email))
|
|
|
- return self.valid_login(user_qs, password, response, password_version)
|
|
|
|
|
|
|
+ return self.valid_login(ip, user_qs, password, response, password_version)
|
|
|
|
|
|
|
|
- def do_phone_login(self, phone, password, response, password_version):
|
|
|
|
|
|
|
+ def do_phone_login(self, ip, phone, password, response, password_version):
|
|
|
user_qs = Device_User.objects.filter(Q(phone=phone) | Q(username=phone), is_active=True, user_isValid=True)
|
|
user_qs = Device_User.objects.filter(Q(phone=phone) | Q(username=phone), is_active=True, user_isValid=True)
|
|
|
- return self.valid_login(user_qs, password, response, password_version)
|
|
|
|
|
|
|
+ return self.valid_login(ip, user_qs, password, response, password_version)
|
|
|
|
|
|
|
|
- def do_name_login(self, username, password, response, password_version):
|
|
|
|
|
|
|
+ def do_name_login(self, ip, username, password, response, password_version):
|
|
|
user_qs = Device_User.objects.filter(Q(username=username) | Q(phone=username) | Q(userEmail=username),
|
|
user_qs = Device_User.objects.filter(Q(username=username) | Q(phone=username) | Q(userEmail=username),
|
|
|
is_active=True, user_isValid=True)
|
|
is_active=True, user_isValid=True)
|
|
|
- return self.valid_login(user_qs, password, response, password_version)
|
|
|
|
|
|
|
+ return self.valid_login(ip, user_qs, password, response, password_version)
|
|
|
|
|
|
|
|
- def valid_login(self, user_qs, password, response, password_version):
|
|
|
|
|
|
|
+ def valid_login(self, ip, user_qs, password, response, password_version):
|
|
|
if not user_qs.exists():
|
|
if not user_qs.exists():
|
|
|
return response.json(104)
|
|
return response.json(104)
|
|
|
- # users = user_qs.values('role__rid', 'role__roleName', 'userID', 'role', 'NickName', 'username', 'userEmail',
|
|
|
|
|
- # 'phone', 'password', 'userIconPath', 'user_isValid', 'is_active')[0]
|
|
|
|
|
|
|
+
|
|
|
users = user_qs.values('role__rid', 'role__roleName', 'userID', 'NickName', 'username', 'userEmail',
|
|
users = user_qs.values('role__rid', 'role__roleName', 'userID', 'NickName', 'username', 'userEmail',
|
|
|
- 'phone', 'password', 'userIconPath')[0]
|
|
|
|
|
|
|
+ 'phone', 'password', 'userIconPath', 'language')[0]
|
|
|
|
|
+ # 代理商判断是否为账号异地登录
|
|
|
|
|
+ role_name = users['role__roleName']
|
|
|
|
|
+ if role_name == '代理商':
|
|
|
|
|
+ self.auth_login_location(users['userID'], users['username'], users['userEmail'], users['language'], ip)
|
|
|
|
|
+
|
|
|
if password_version == 'V1':
|
|
if password_version == 'V1':
|
|
|
check_flag = check_password(password, users['password'])
|
|
check_flag = check_password(password, users['password'])
|
|
|
else:
|
|
else:
|
|
@@ -115,6 +120,103 @@ class LoginView(TemplateView):
|
|
|
else:
|
|
else:
|
|
|
return response.json(tko.code)
|
|
return response.json(tko.code)
|
|
|
|
|
|
|
|
|
|
+ @staticmethod
|
|
|
|
|
+ def auth_login_location(user_id: str, username: str, email: str, lang: str, ip: str):
|
|
|
|
|
+ """
|
|
|
|
|
+ 异地登录检测并发送邮件提醒
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ user_id: 用户ID
|
|
|
|
|
+ username: 用户名
|
|
|
|
|
+ email: 用户邮箱
|
|
|
|
|
+ lang: 语言
|
|
|
|
|
+ ip: 当前登录IP
|
|
|
|
|
+ """
|
|
|
|
|
+ redis: RedisObject = RedisObject()
|
|
|
|
|
+ redis_key: str = f'user_login_ip:{user_id}'
|
|
|
|
|
+ last_ip: str = redis.get_data(redis_key)
|
|
|
|
|
+
|
|
|
|
|
+ # 如果上次登录IP存在且与当前IP不一致,发送异地登录邮件
|
|
|
|
|
+ if last_ip and last_ip != ip:
|
|
|
|
|
+ # 获取当前时间
|
|
|
|
|
+ login_time: str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
|
+
|
|
|
|
|
+ # 根据语言选择邮件内容
|
|
|
|
|
+ if lang == 'cn':
|
|
|
|
|
+ subject: str = '异地登录提醒'
|
|
|
|
|
+ msg: str = f"""<html>
|
|
|
|
|
+<head></head>
|
|
|
|
|
+<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
|
|
|
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
|
|
|
+ <h2 style="color: #d9534f; border-bottom: 2px solid #d9534f; padding-bottom: 10px;">异地登录提醒</h2>
|
|
|
|
|
+ <p>尊敬的客户:</p>
|
|
|
|
|
+ <p>您好!</p>
|
|
|
|
|
+ <p style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; margin: 15px 0;">
|
|
|
|
|
+ <strong>您的账号在异地登录。</strong>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd; width: 100px;"><strong>账号:</strong></td>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">{username if username else email}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>登录时间:</strong></td>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">{login_time}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>登录IP:</strong></td>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">{ip}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ <p style="background-color: #f8d7da; border-left: 4px solid #dc3545; padding: 10px; margin: 15px 0;">
|
|
|
|
|
+ <strong>安全提示:</strong>如非本人操作,您的账号密码可能已泄露,请及时修改密码。
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</body>
|
|
|
|
|
+</html>"""
|
|
|
|
|
+ else:
|
|
|
|
|
+ subject: str = 'Remote Login Alert'
|
|
|
|
|
+ msg: str = f"""<html>
|
|
|
|
|
+<head></head>
|
|
|
|
|
+<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
|
|
|
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
|
|
|
+ <h2 style="color: #d9534f; border-bottom: 2px solid #d9534f; padding-bottom: 10px;">Remote Login Alert</h2>
|
|
|
|
|
+ <p>Dear Customer,</p>
|
|
|
|
|
+ <p>Hello!</p>
|
|
|
|
|
+ <p style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; margin: 15px 0;">
|
|
|
|
|
+ <strong>Your account has been logged in from a different location.</strong>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <table style="width: 100%; border-collapse: collapse; margin: 20px 0;">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd; width: 120px;"><strong>Account:</strong></td>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">{username if username else email}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>Login Time:</strong></td>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">{login_time}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;"><strong>Login IP:</strong></td>
|
|
|
|
|
+ <td style="padding: 8px; border-bottom: 1px solid #ddd;">{ip}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ <p style="background-color: #f8d7da; border-left: 4px solid #dc3545; padding: 10px; margin: 15px 0;">
|
|
|
|
|
+ <strong>Security Notice:</strong> If this was not you, your account password may have been compromised. Please change your password immediately.
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</body>
|
|
|
|
|
+</html>"""
|
|
|
|
|
+
|
|
|
|
|
+ # 发送邮件(如果有邮箱地址)
|
|
|
|
|
+ if email:
|
|
|
|
|
+ try:
|
|
|
|
|
+ S3Email.send_email(subject, msg, email)
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f'发送异地登录邮件失败: {repr(e)}')
|
|
|
|
|
+
|
|
|
|
|
+ # 更新Redis中的IP地址,保存90天
|
|
|
|
|
+ redis.set_data(redis_key, ip, expire=90 * 24 * 60 * 60)
|
|
|
|
|
+
|
|
|
|
|
|
|
|
# 获取登录权限
|
|
# 获取登录权限
|
|
|
class GetPermissions(TemplateView):
|
|
class GetPermissions(TemplateView):
|