Browse Source

代理商异地登录邮件提醒

locky 1 week ago
parent
commit
a22aa3c8ea
1 changed files with 118 additions and 16 deletions
  1. 118 16
      AdminController/UserManageController.py

+ 118 - 16
AdminController/UserManageController.py

@@ -17,6 +17,7 @@ from Controller.CheckUserData import DataValid, RandomStr
 from Model.models import Device_User, Role, UserExModel, CountryModel, MenuModel, FeedBackModel, StatResModel, \
     SysMassModel, App_Info, SysMsgModel, DeviceSuperPassword, CustomizedPush, DeviceTypeModel, CustomCustomerOrderInfo
 from Object.AWS.AmazonS3Util import AmazonS3Util
+from Object.AWS.S3Email import S3Email
 from Object.ApschedulerObject import ApschedulerObject
 from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
@@ -38,14 +39,14 @@ class LoginView(TemplateView):
         return super(LoginView, self).dispatch(*args, **kwargs)
 
     def post(self, request, *args, **kwargs):
+        ip = CommonService.get_ip_address(request)
         request.encoding = 'utf-8'
         request_dict = request.POST
         language = request_dict.get('language', 'en')
         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)
         password = request_dict.get('password', None)
         password_version = request_dict.get('pwdVersion', 'V1')
@@ -55,34 +56,38 @@ class LoginView(TemplateView):
         password = password.strip()
         data_valid = DataValid()
         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):
-            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):
-            return self.do_name_login(username, password, response, password_version)
+            return self.do_name_login(ip, username, password, response, password_version)
         else:
             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))
-        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)
-        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),
                                              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():
             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',
-                               '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':
             check_flag = check_password(password, users['password'])
         else:
@@ -115,6 +120,103 @@ class LoginView(TemplateView):
         else:
             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):