예제 #1
0
    def update_email_by_token(self, db: Session, response: Response, *,
                              obj_in: schema_user.UserUpdateEmailIn):
        """
        绑定/更换 邮箱
        :param email:           邮箱
        :param email_code:      邮箱验证码
        :return:                用户信息, 提示信息
        """
        # --------- 校验邮箱是否已被使用 --------- #
        email_user = crud_user.get_user_by_email(db, email=obj_in.email)
        if email_user:
            message = f"邮箱 {obj_in.email} 已被使用"
            response.status_code = status.HTTP_400_BAD_REQUEST
            return None, message

        # --------- 校验邮箱验证码 ---------#
        redis_email_code = redis_client.get(obj_in.email)
        if obj_in.email_code != redis_email_code:
            message = "验证码不正确或已过期"
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            return None, message

        # --------- 绑定/更换 邮箱 --------- #
        db_user_obj = crud_user.update_email_by_id(db,
                                                   id=token_user.id,
                                                   email=obj_in.email)
        if not db_user_obj:
            message = "邮箱更新失败"
            response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            logger.error(message)
        else:
            redis_client.set(obj_in.email, "", ex=1)
            message = "邮箱更新成功"
            response.status_code = status.HTTP_200_OK
        return db_user_obj, message
예제 #2
0
def check_jwt_token(
        token: Optional[str] = Header(..., description="登录 token")
) -> Union[dict, Any]:
    """
    解析验证token  默认验证headers里面为token字段的数据
    可以给 headers 里面token替换别名, 以下示例为 X-Token
    token: Optional[str] = Header(None, alias="X-Token")
    :param token:
    :return:
    """
    try:
        decode_token = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])

        current_time = datetime.now()       # 系统当前时间
        expiration_time = datetime.fromtimestamp(decode_token.get("exp")) - timedelta(hours=8)  # token 过期时间
        if current_time > expiration_time:  # 验证 token 是否过期
            raise custom_exc.TokenExpired()

        # -------- 验证 token 是否失效(和 redis 中的对比,是否存在)--------- #
        redis_userid = decode_token.get("sub")      # 获取存储在 redis 中的 key(这里的 sub 为 userid)
        redis_token = redis_client.get(redis_userid)
        if not redis_token:
            raise custom_exc.TokenAuthError()
        return decode_token
    except jwt.ExpiredSignatureError:
        raise custom_exc.TokenExpired()
    except (jwt.JWTError, ValidationError, AttributeError):
        raise custom_exc.TokenAuthError()
예제 #3
0
    def retrieve_password_by_account(
            self, db: Session, response: Response, *, account: str,
            obj_in: schema_user.UserRetrievePasswordIn):
        """
        通过账号(手机号/邮箱)找回密码
        :param account:         手机号/邮箱
        :param verify_code:     验证码
        :param password:        密码
        :return:                用户信息, 提示信息
        """
        # ----------- 判断输入账号是 手机号/邮箱 ---------- #
        if re.match(RE_PHONE, account):  # 手机
            db_user = crud_user.get_user_by_phone(db, phone=account)
        elif re.match(RE_EMAIL, account):  # 邮箱
            db_user = crud_user.get_user_by_email(db, email=account)
        else:
            message = "账号输入有误"
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            return None, message

        if not db_user:
            message = f"账号 {account} 未注册"
            response.status_code = status.HTTP_404_NOT_FOUND
            logger.error(message)
            return None, message

        if db_user.status == 1:
            message = f"账号 {account} 已被禁用"
            response.status_code = status.HTTP_403_FORBIDDEN
        else:
            # ----------- 校验验证码 ---------- #
            redis_verify_code = redis_client.get(account)
            if obj_in.verify_code != redis_verify_code:
                message = "验证码不正确或已过期"
                response.status_code = status.HTTP_401_UNAUTHORIZED
                return None, message

            # ---------- 重新设置密码 ---------- #
            hashed_new_password = security.get_password_hash(obj_in.password)
            db_user_obj = crud_user.update_password_by_id(
                db, id=db_user.id, hashed_password=hashed_new_password)
            if not db_user_obj:
                message = "找回密码失败"
                response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            else:
                redis_client.set(db_user.id, "", ex=1)
                redis_client.set(account, "", ex=1)
                message = "找回密码成功"
                response.status_code = status.HTTP_200_OK
                return db_user_obj, message
        logger.error(message)
        return None, message
예제 #4
0
    def update_phone_by_token(self, db: Session, response: Response, *,
                              decode_token: dict,
                              obj_in: schema_user.UserUpdatePhoneIn):
        """
        绑定/更换 手机号
        :param decode_token:    解析之后的 token
        :param phone:           手机号
        :param phone_code:      手机验证码
        :return:                用户信息, 提示信息
        """
        # --------- 校验手机号是否已被使用 --------- #
        phone_user = crud_user.get_user_by_phone(db, phone=obj_in.phone)
        if phone_user:
            message = f"手机号 {obj_in.phone} 已被使用"
            response.status_code = status.HTTP_400_BAD_REQUEST
            return None, message

        # --------- 校验手机验证码 ---------#
        redis_phone_code = redis_client.get(obj_in.phone)
        if obj_in.phone_code != redis_phone_code:
            message = "验证码不正确或已过期"
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            return None, message

        # --------- 绑定/更换 手机号 --------- #
        db_user_obj = crud_user.update_phone_by_id(db,
                                                   id=phone_user.id,
                                                   phone=obj_in.phone)
        if not db_user_obj:
            message = "手机号更新失败"
            response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            logger.error(message)
        else:
            redis_client.set(obj_in.phone, "", ex=1)
            message = "手机号更新成功"
            response.status_code = status.HTTP_200_OK
        return db_user_obj, message
예제 #5
0
async def items_test(
        *,
        bar: str = Query(..., title="测试字段", description="测试字段描述"),
        db: Session = Depends(deps.get_db),
) -> Any:
    """
    用户登录
    :param bar:
    :param db:
    :return:
    """
    # 测试redis使用
    redis_client.set("test_items", bar, ex=60)
    redis_test = redis_client.get("test_items")

    # 用不惯orm查询的可以直接sql(建议把CURD操作单独放到service文件夹下,统一管理)
    test_sql = "SELECT nickname,avatar from sys_user WHERE id>=:id"
    admin_user_res = db.execute(text(test_sql), {"id": 1}).fetchall()

    return response_code.resp_200(data={
        "items": "ok",
        "admin_user_info": admin_user_res,
        "redis_test": redis_test
    })
예제 #6
0
    def signup(self, db: Session, request: Request, response: Response, *,
               obj_in: schema_user.UserSignupIn):
        """
        通过 手机号/邮箱 注册
        :param account:             手机号/邮箱
        :param verify_code:         验证码
        :param password:            密码
        :return:                    用户信息, 提示信息
        """
        # --------- 校验验证码 --------- #
        redis_verify_code = redis_client.get(obj_in.account)
        if obj_in.verify_code != redis_verify_code:
            message = "验证码不正确或已过期"
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            return None, message

        # --------- 先查询一次,在进行注册 -------- #
        if re.match(RE_PHONE, obj_in.account):  # 手机号
            db_user = crud_user.get_user_by_phone(db, phone=obj_in.account)
            if db_user:
                message = f"账号 {obj_in.account} 已被注册"
                response.status_code = status.HTTP_400_BAD_REQUEST
                return None, message
            else:
                dict_obj_in = {
                    "phone": obj_in.account,
                    "hashed_password":
                    security.get_password_hash(obj_in.password),
                    "ip": request.client.host
                }
                db_create_user = crud_user.create_by_phone(
                    db, obj_in=dict_obj_in)  # 通过手机号进行注册
        elif re.match(RE_EMAIL, obj_in.account):  # 邮箱
            db_user = crud_user.get_user_by_email(db, email=obj_in.account)
            if db_user:
                message = f"账号 {obj_in.account} 已被注册"
                response.status_code = status.HTTP_400_BAD_REQUEST
                return None, message
            else:
                dict_obj_in = {
                    "email": obj_in.account,
                    "hashed_password":
                    security.get_password_hash(obj_in.password),
                    "ip": request.client.host
                }
                db_create_user = crud_user.create_by_email(
                    db, obj_in=dict_obj_in)  # 通过邮箱进行注册
        else:
            message = "账号输入有误"  # 用户名
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            logger.error(message)
            return None, message

        if not db_create_user:
            message = f"用户 {obj_in.account} 注册失败"
            response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            logger.error(message)
        else:
            message = f"用户 {obj_in.account} 注册成功"
            response.status_code = status.HTTP_201_CREATED
            redis_client.set(obj_in.account, "", ex=1)  # 注册成功之后使验证码立即失效
        return db_create_user, message
예제 #7
0
    def signin_by_verify_code(self, db: Session, request: Request,
                              response: Response, *,
                              obj_in: schema_user.UserVerifyCodeSigninIn):
        """
        通过 短信/邮箱 验证码登录
        :param account:        手机号/邮箱/用户名
        :param verify_code:    验证码
        :return:               token, 提示信息
        """
        # --------- 比较验证码是否匹配 ----------- #
        redis_verify_code = redis_client.get(obj_in.account)
        if obj_in.verify_code != redis_verify_code:
            message = "验证码不正确或已过期"
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            logger.error(message)
            return None, message

        if re.match(RE_PHONE, obj_in.account):  # 手机
            db_user = crud_user.get_user_by_phone(db, phone=obj_in.account)
        elif re.match(RE_EMAIL, obj_in.account):  # 邮箱
            db_user = crud_user.get_user_by_email(db, email=obj_in.account)
        else:
            message = "账号输入有误,请重新输入"
            response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
            logger.error(message)
            return None, message

        # ----------- 判断账号是否注册过 ----------- #
        userid = None
        authorityid = None
        if not db_user:
            if re.match(RE_PHONE, obj_in.account):  # 手机
                dict_obj_in = {
                    "phone": obj_in.account,
                    "hashed_password": None,
                    "ip": request.client.host
                }
                db_user_obj = crud_user.create_by_phone(db, obj_in=dict_obj_in)
            elif re.match(RE_EMAIL, obj_in.account):  # 邮箱
                dict_obj_in = {
                    "email": obj_in.account,
                    "hashed_password": None,
                    "ip": request.client.host
                }
                db_user_obj = crud_user.create_by_email(db, obj_in=dict_obj_in)

            if not db_user_obj:
                message = "登录失败"
                response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
                logger.error(message)
                return None, message
            userid = db_user_obj.id  # 将新增之后的 用户id 赋值给变量
            authorityid = db_user_obj.authorityid  # 默认权限
        elif db_user.status == 1:
            message = f"账号 {obj_in.account} 已被禁用"
            response.status_code = status.HTTP_403_FORBIDDEN
            logger.error(message)
            return None, message

        if db_user:
            userid = db_user.id
            authorityid = db_user.authorityid

        # -------- # 登录token 存储了userid 和 authorityid ------- #
        access_token_expires = timedelta(
            minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
        token = security.create_access_token(
            userid, authorityid, expires_delta=access_token_expires)
        redis_client.set(userid,
                         token,
                         ex=settings.ACCESS_TOKEN_EXPIRE_MINUTES)

        # --------- 登录成功之后向数据库添加一条登录日志信息 ---------- #
        ip = request.client.host  # 用户ip
        signin_log_id = crud_user.add_signin_log(db, userid=userid, ip=ip)
        if not signin_log_id:
            message = f"用户 {obj_in.account} 登录失败"
            response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
            logger.error(message)
            return None, message
        else:
            redis_client.set(obj_in.account, "", ex=1)  # 使验证码失效
            message = f"用户 {obj_in.account} 登录成功"
            response.status_code = status.HTTP_200_OK
            return token, message