class SecurityHandler(web.RequestHandler): key = "user_blacklist" def __init__(self, application, request, **kwargs): super().__init__(application, request, **kwargs) self.r = Redis().r def prepare(self): if self.check_request(): self.set_status(HTTPStatus.FORBIDDEN) self.finish() def data_received(self, chunk): pass def check_request(self): ban = self.__ip_check() user = self.__user_check() result = ban or user if result: self.ban() return result def get_real_ip(self): x_real = self.request.headers.get("X-Real-IP") remote_ip = self.request.remote_ip logging.debug("X-Real-IP:%s, Remote-IP:%s", x_real, remote_ip) return x_real or remote_ip def ban(self): ip = self.get_real_ip() self.r.incr(ip) count = int(self.r.get(ip)) # ban rule: (count-10)*600 if count <= 10: ex = 120 else: ex = (count - 10) * 600 if count >= 30: add_cf_blacklist(ip) self.r.set(ip, count, ex) user = self.get_current_user() if user: self.r.hincrby(self.key, user) def get_current_user(self) -> str: username = self.get_secure_cookie("username") or b"" return username.decode("u8") def __user_check(self): count = self.r.hget(self.key, self.get_current_user()) or 0 count = int(count) if count >= 20: return True def __ip_check(self): d = self.r.get(self.get_real_ip()) or 0 if int(d) >= 10: return True
def login_user(self, username: str, password: str, captcha: str, captcha_id: str, ip: str, browser: str) -> dict: # verify captcha in the first place. redis = Redis().r correct_captcha = redis.get(captcha_id) if correct_captcha is None: return { "status_code": HTTPStatus.BAD_REQUEST, "message": "验证码已过期", "status": False } elif correct_captcha.lower() == captcha.lower(): redis.expire(captcha_id, 0) else: return { "status_code": HTTPStatus.FORBIDDEN, "message": "验证码错误", "status": False } # check user account is locked. data = self.db["users"].find_one({"username": username}) or {} if data.get("status", {}).get("disable"): return { "status_code": HTTPStatus.FORBIDDEN, "status": False, "message": data.get("status", {}).get("reason") } returned_value = {"status_code": 0, "message": ""} if data: # try to login stored_password = data["password"] if pbkdf2_sha256.verify(password, stored_password): returned_value["status_code"] = HTTPStatus.OK else: returned_value["status_code"] = HTTPStatus.FORBIDDEN returned_value["message"] = "用户名或密码错误" else: # register hash_value = pbkdf2_sha256.hash(password) try: self.db["users"].insert_one( dict(username=username, password=hash_value, date=ts_date(), ip=ip, browser=browser)) returned_value["status_code"] = HTTPStatus.CREATED except Exception as e: returned_value[ "status_code"] = HTTPStatus.INTERNAL_SERVER_ERROR returned_value["message"] = str(e) returned_value["username"] = data.get("username") returned_value["group"] = data.get("group", ["user"]) return returned_value
def get_latest_resource() -> dict: redis = Redis().r key = "latest-resource" latest = redis.get(key) if latest: logging.info("Cache hit for latest resource") latest = json.loads(latest) latest["data"] = latest["data"][:100] else: logging.warning("Cache miss for latest resource") latest = ResourceLatestMongoResource().query_db() redis.set(key, json.dumps(latest, ensure_ascii=False)) return latest
def update_user_info(self, username: str, data: dict) -> dict: redis = Redis().r valid_fields = ["email"] valid_data = {} for field in valid_fields: if data.get(field): valid_data[field] = data[field] if valid_data.get("email") and not re.findall(r"\S@\S", valid_data.get("email")): return { "status_code": HTTPStatus.BAD_REQUEST, "status": False, "message": "email format error " } elif valid_data.get("email"): # rate limit user_email = valid_data.get("email") timeout_key = f"timeout-{user_email}" if redis.get(timeout_key): return { "status_code": HTTPStatus.TOO_MANY_REQUESTS, "status": False, "message": f"try again in {redis.ttl(timeout_key)}s" } verify_code = random.randint(10000, 99999) valid_data["email"] = {"verified": False, "address": user_email} # send email confirm subject = "[人人影视下载分享站] 请验证你的邮箱" body = f"{username} 您好,<br>请输入如下验证码完成你的邮箱认证。验证码有效期为24小时。<br>" \ f"如果您未有此请求,请忽略此邮件。<br><br>验证码: {verify_code}" redis.set(timeout_key, username, ex=1800) redis.hset(user_email, mapping={"code": verify_code, "wrong": 0}) redis.expire(user_email, 24 * 3600) send_mail(user_email, subject, body) self.db["users"].update_one({"username": username}, {"$set": valid_data}) return { "status_code": HTTPStatus.CREATED, "status": True, "message": "success" }