一、用户注册…
1.完成短信验证码的校验
application.utils.language.message,代码:
class ErrorMessage():
ok = "ok"
mobile_format_error = "手机号码格式有误!"
mobile_is_use = "对不起,当前手机已经被注册!"
username_is_use = "对不起,当前用户名已经被使用!"
password_not_match = "密码和验证密码不匹配!"
sms_send_error = "短信发送失败!"
sms_interval_time = "短信发送冷却中!"
sms_code_expired = "短信验证码已过期!"
sms_code_error = "短信验证码不正确!"
sms_is_send = "短信发送中,请留意您的手机短信。"application.apps.ursers.marshmallow,代码:
from marshmallow import Schema, fields, validate, validates, ValidationError
from message import ErrorMessage as Message
from .models import User, db
class MobileSchema(Schema):
mobile = fields.String(required=True, validate=validate.Regexp('^1[3-9]\d{9}$', error=Message.mobile_format_error))
@validates('mobile')
def validates_mobile(self, data):
user = User.query.filter(User.mobile == data).first()
if user is not None:
raise ValidationError(message=Message.mobile_is_use)
return data
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field
from marshmallow import post_load, pre_load, validates_schema
from application import redis
class UserSchema(SQLAlchemyAutoSchema):
mobile = auto_field(required=True, load_only=True)
password = fields.String(required=True, load_only=True)
password2 = fields.String(required=True, load_only=True)
sms_code = fields.String(required=True, load_only=True)
class Meta:
model = User
include_fk = True # 启用外键关系
include_relationships = True # 模型关系外部属性
fields = ['id', 'name', 'mobile', 'password', 'password2', 'sms_code'] # 如果要全换全部字段,就不要声明fields或exclude字段即可
sql_session = db.session
@post_load()
def save_object(self, data, **kwargs):
data.pop('password2')
data.pop('sms_code')
data['name'] = data['mobile']
instance = User(**data)
db.session.add(instance)
db.session.commit()
return instance
@validates_schema
def validate(self, data, **kwargs):
# 校验密码和确认密码
if data['password'] != data['password2']:
raise ValidationError(message=Message.password_not_match, field_name='password')
# 校验短信验证码
# 1.从redis中提取验证码
redis_sms_code = redis.get('sms_%s' % data['mobile'])
if redis_sms_code is None:
raise ValidationError(message=Message.sms_code_expired, field_name='sms_code')
redis_sms_code = redis_sms_code.decode()
# 2. 从客户端提交的数据data中提取验证码
sms_code = data['sms_code']
# 3. 字符串比较,如果失败,则抛出异常,否则,直接删除验证码
if sms_code != redis_sms_code:
raise ValidationError(message=Message.sms_code_error, field_name='sms_code')
redis.delete('sms_%s' % data['mobile'])
return data2.基于Celery实现短信异步发送
安装celery
pip install celery==4.4.0在项目根目录下创建mycelery目录,同时创建celery启动主程序文件main.py,代码:
from celery import Celery
from application import init_app
# 初始化celery对象
app = Celery('flask')
# 初始化flask
flask_app = init_app('application.settings.dev').app
# 加载配置
app.config_from_object('mycelery.config')
# 自动注册任务
app.autodiscover_tasks(['mycelery.sms'])
# 运行celery
# 主程序终端下启动: celery -A mycelery.main worker -l info
# 调度器终端下启动: celery -A mycelery.main beat配置文件,mycelery.config,代码:
# 任务队列地址
broker_url = 'redis://127.0.0.1:6379/14'
# 结果队列地址
result_backend = "redis://127.0.0.1:6379/15"在mycelery下创建任务模块包sms,并创建tasks.py任务模块文件,同时,在任务执行过程中, 基于监听器和任务bind属性对失败任务进行记录和重新尝试执行. 代码:
import json
from application import redis
from flask import current_app
from ronglian_sms_sdk import SmsSDK
from mycelery.main import app, flask_app
@app.task(name='send_sms', bind=True)
def send_sms(self, mobile, sms_code):
"""发送短信"""
try:
with flask_app.app_context():
sdk = SmsSDK(
current_app.config.get('SMS_ACCOUNT_ID'),
current_app.config.get('SMS_ACCOUNT_TOKEN'),
current_app.config.get('SMS_APP_ID'),
)
ret = sdk.sendMessage(
current_app.config.get('SMS_TEMPLATE_ID'),
mobile,
(sms_code, current_app.config.get('SMS_EXPIRE_TIME') // 60)
)
result = json.loads(ret)
if result['statusCode'] == '000000':
pipe = redis.pipeline()
pipe.multi() # 开启事务
# 保存短信记录到redis中
pipe.setex('sms_%s' % mobile, current_app.config.get('SMS_EXPIRE_TIME'), sms_code)
# 进行冷却倒计时
pipe.setex('int_%s' % mobile, current_app.config.get('SMS_INTERVAL_TIME'), '_')
pipe.execute() # 提交事务
else:
current_app.log.error('短信发送失败!\r\n%s" % ret')
raise Exception
except Exception as exc:
# 重新尝试执行失败任务
print(self.request.retries) # 本次执行的次数
self.retry(exc=exc, countdown=3, max_retries=5)
"""基于监听器完成任务监听"""
from celery.app.task import Task
class SMSTask(Task):
def on_success(self, retval, task_id, args, kwargs):
print('任务执行成功!')
return super().on_success(retval, task_id, args, kwargs)
def on_failure(self, exc, task_id, args, kwargs, einfo):
print('任务执行失败!%s' % self.request.retries)
# 重新尝试执行失败任务,时间间隔:3秒,最大尝试次数:5次
self.retry(exc=exc, countdown=3, max_retries=5)
return super().on_failure(exc, task_id, args, kwargs, einfo)
def after_return(self, status, retval, task_id, args, kwargs, einfo):
print('this is after return')
return super().after_return(status, retval, task_id, args, kwargs, einfo)
def on_retry(self, exc, task_id, args, kwargs, einfo):
print('this is retry')
return super().on_retry(exc, task_id, args, kwargs, einfo)
@app.task(name='send_sms2', base=SMSTask)
def send_sms2(mobile, sms_code):
"""发送短信"""
with flask_app.app_context():
sdk = SmsSDK(
current_app.config.get("SMS_ACCOUNT_ID"),
current_app.config.get("SMS_ACCOUNT_TOKEN"),
current_app.config.get("SMS_APP_ID")
)
ret = sdk.sendMessage(
current_app.config.get("SMS_TEMPLATE_ID"),
mobile,
(sms_code, current_app.config.get("SMS_EXPIRE_TIME") // 60)
)
result = json.loads(ret)
if result["statusCode"] == "000000":
pipe = redis.pipeline()
pipe.multi() # 开启事务
# 保存短信记录到redis中
pipe.setex("sms_%s" % mobile, current_app.config.get("SMS_EXPIRE_TIME"), sms_code)
# 进行冷却倒计时
pipe.setex("int_%s" % mobile, current_app.config.get("SMS_INTERVAL_TIME"), "_")
pipe.execute() # 提交事务
else:
current_app.log.error("短信发送失败!\r\n%s" % ret)
raise Exception
"""
from mycelery.sms.tasks import send_sms2
send_sms2.delay(mobile="13312345678",sms_code="123456")
send_sms.delay(mobile="18899241027",sms_code="123456")
"""mycelery模块目录结构:
mofangapi/
├── application/
├── manage.py
└── mycelery/
├── config.py
├── __init__.py
├── main.py
└── sms/
├── __init__.py
└── tasks.pyapplication.utils.language.message,代码:
class ErrorMessage():
ok = "ok"
mobile_format_error = "手机号码格式有误!"
mobile_is_use = "对不起,当前手机已经被注册!"
username_is_use = "对不起,当前用户名已经被使用!"
password_not_match = "密码和验证密码不匹配!"
sms_send_error = "短信发送失败!"
sms_interval_time = "短信发送冷却中!"
sms_code_expired = "短信验证码已过期!"
sms_code_error = "短信验证码不正确!"
sms_is_send = "短信发送中,请留意您的手机短信。"flask项目调用异步任务发送短信,application.apps.home.views,代码:
from application import jsonrpc
import re, random, json
from status import APIStatus as status
from message import ErrorMessage as message
from ronglian_sms_sdk import SmsSDK
from flask import current_app
from application import redis
@jsonrpc.method(name='Home.sms')
def sms(mobile):
"""发送短信验证码"""
# 验证手机
if not re.match('^1[3-9]\d{9}$', mobile):
return {'errno': status.CODE_VALIDATE_ERROR, 'errmsg': message.mobile_format_error}
# 短信发送冷却时间
ret = redis.get('int_%s' % mobile)
if ret is not None:
return {'errno': status.CODE_INTERVAL_TIME, 'errmsg': message.sms_interval_time}
# 生成验证码
sms_code = '%06d' % random.randint(0, 999999)
try:
# 异步发送短信
from mycelery.sms.tasks import send_sms
send_sms.delay(mobile=mobile, sms_code=sms_code)
# 返回结果
return {'errno': status.CODE_OK, 'errmsg': message.sms_is_send}
except Exception as e:
return {'errno': status.CODE_SMS_ERROR, 'errmsg': message.sms_send_error}二、用户登录
1.jwt登陆认证
当前我们开发的项目属于前后端分离,而目前最适合我们使用的认证方式就是jwt token认证。
在flask中,我们可以通过flask_jwt_extended模块来快速实现jwt用户登录认证。
注意:
flask_jwt_extended的作者开发当前模块主要适用于flask的普通视图方法的。其认证方式主要通过装饰器来完成。而我们当前所有服务端接口都改造成了jsonrpc规范接口,所以我们在使用过程中,需要对部分源代码进行调整才能正常使用。- 事实上,在我们当前使用的
flask_jsonrpc也提供了用户登陆认证功能,但是这个功能是依靠用户账户username和密码password来实现。如果我们基于当前这种方式,也可以实现jwt登陆认证,只是相对于上面的flask_jwt_extended模块而言,要补充的代码会更多,所以在此,我们放弃这块功能的使用。
模块安装
pip install flask-jwt-extended官网文档:https://flask-jwt-extended.readthedocs.io/en/latest/ 配置说明:https://flask-jwt-extended.readthedocs.io/en/latest/options/
快速使用
在磨方项目中对模块进行初始化,application/__init__.py,代码:
import os, sys
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate, MigrateCommand
from flask_jsonrpc import JSONRPC
from flask_marshmallow import Marshmallow
from flask_jwt_extended import JWTManager
from application.utils import init_blueprint
from application.utils.config import load_config
from application.utils.session import init_session
from application.utils.logger import Log
from application.utils.commands import load_command
# 创建终端脚本管理对象
manage = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
# 数据迁移实例对象
migrate = Migrate()
# 日志对象
log = Log()
# 初始化jsonrpc模块
jsonrpc = JSONRPC()
# 数据转换器的对象创建
ma = Marshmallow()
# jwt认证模块实例化
jwt = JWTManager()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载导包路径
sys.path.insert(0, os.path.join(app.BASE_DIR, 'application/utils/language'))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# 数据转换器的初始化
ma.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 数据迁移初始化
migrate.init_app(app, db)
# 添加数据迁移的命令到终端脚本工具中
manage.add_command('db', MigrateCommand)
# 日志初始化
app.log = log.init_app(app)
# 蓝图注册
init_blueprint(app)
# 初始化json-rpc
jsonrpc.service_url = '/api' # api接口的url地址前缀
jsonrpc.init_app(app)
# jwt初始化
jwt.init_app(app)
# 初始化终端脚本工具
= app
# 注册自定义命令
load_command(manage)
return manage配置文件,application.settings.dev,代码:
from . import InitConfig
class Config(InitConfig):
"""项目开发环境下的配置"""
DEBUG = True
# 数据库
SQLALCHEMY_DATABASE_URI = 'mysql://mofang_user:mofang@127.0.0.1:3306/mofang?charset=utf8mb4'
SQLALCHEMY_ECHO = True
# redis
REDIS_URL = 'redis://@127.0.0.1:6379/0'
# session存储配置
SESSION_REDIS_HOST = '127.0.0.1'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
# 日志配置
LOG_LEVEL = 'DEBUG' # 日志输出到文件中的最低等级
LOG_DIR = '/logs/mofang.log' # 日志存储目录
LOG_MAX_BYTES = 300 * 1024 * 1024 # 单个日志文件的存储上限[单位: b]
LOG_BACKPU_COUNT = 20 # 日志文件的最大备份数量
LOG_NAME = 'mofang' # 日志器的名字
# 注册蓝图
INSTALLED_APPS = [
'application.apps.home',
'application.apps.users',
'application.apps.marsh',
]
# 短信相关配置
SMS_ACCOUNT_ID = "8aaf0708754a3ef2017563ddb22d0773" # 接口主账号
SMS_ACCOUNT_TOKEN = "0b41612bc8a8429d84b5d37f29178743" # 认证token令牌
SMS_APP_ID = "8aaf0708754a3ef2017563ddb3110779" # 应用ID
SMS_TEMPLATE_ID = 1 # 短信模板ID
SMS_EXPIRE_TIME = 60 * 5 # 短信有效时间,单位:秒/s
SMS_INTERVAL_TIME = 60 # 短信发送冷却时间,单位:秒/s
# jwt 相关配置
# 加密算法,默认: HS256
JWT_ALGORITHM = "HS256"
# 秘钥,默认是flask配置中的SECRET_KEY
JWT_SECRET_KEY = "y58Rsqzmts6VCBRHes1Sf2DHdGJaGqPMi6GYpBS4CKyCdi42KLSs9TQVTauZMLMw"
# token令牌有效期,单位: 秒/s,默认: datetime.timedelta(minutes=15) 或者 15 * 60
JWT_ACCESS_TOKEN_EXPIRES = 60
# refresh刷新令牌有效期,单位: 秒/s,默认:datetime.timedelta(days=30) 或者 30*24*60*60
JWT_REFRESH_TOKEN_EXPIRES = 30*24*60*60
# 设置通过哪种方式传递jwt,默认是http请求头,也可以是query_string,json,cookies
JWT_TOKEN_LOCATION = "headers"
# 当通过http请求头传递jwt时,请求头参数名称设置,默认值: Authorization
JWT_HEADER_NAME="Authorization"
# 当通过http请求头传递jwt时,令牌的前缀。
# 默认值为 "Bearer",例如:Authorization: Bearer <JWT>
JWT_HEADER_TYPE="jwt"application.apps.users.views,代码:
from application import jsonrpc,db
from .marshmallow import MobileSchema,UserSchema
from marshmallow import ValidationError
from message import ErrorMessage as Message
from status import APIStatus as status
@jsonrpc.method("User.mobile")
def mobile(mobile):
"""验证手机号码是否已经注册"""
ms = MobileSchema()
try:
ms.load({"mobile":mobile})
ret = {"errno":status.CODE_OK, "errmsg":Message.ok}
except ValidationError as e:
ret = {"errno":status.CODE_VALIDATE_ERROR, "errmsg": e.messages["mobile"][0]}
return ret
@jsonrpc.method("User.register")
def register(mobile,password,password2, sms_code):
"""用户信息注册"""
try:
ms = MobileSchema()
ms.load({"mobile": mobile})
us = UserSchema()
user = us.load({
"mobile":mobile,
"password":password,
"password2":password2,
"sms_code": sms_code
})
data = {"errno": status.CODE_OK,"errmsg":us.dump(user)}
except ValidationError as e:
data = {"errno": status.CODE_VALIDATE_ERROR,"errmsg":e.messages}
return data
from flask_jwt_extended import create_access_token,create_refresh_token,jwt_required,get_jwt_identity,jwt_refresh_token_required
from flask import jsonify,json
@jsonrpc.method("User.login")
def login(account,password):
"""根据用户登录信息生成token"""
# 1. 根据账户信息和密码获取用户
# 2. 生成jwt token
access_token = create_access_token(identity=account)
refresh_token = create_refresh_token(identity=account)
print(access_token)
print(refresh_token)
return "ok"
@jsonrpc.method("")
@jwt_required # 验证jwt
def info():
"""获取用户信息"""
user_data = json.loads(get_jwt_identity()) # get_jwt_identity 用于获取载荷中的数据
print(user_data)
return "ok"
@jsonrpc.method("User.refresh")
@jwt_refresh_token_required
def refresh():
"""重新获取新的认证令牌token"""
current_user = get_jwt_identity()
# 重新生成token
access_token = create_access_token(identity=current_user)
return access_token装饰器jwt_required就是用于获取客户端提交的数据中的jwt的方法,这里,我们需要进行2处调整。以方便它更好的展示错误信息。flask_jwt_extended/view_decorators.py,代码:
from jwt.exceptions import ExpiredSignatureError
from flask_jwt_extended.exceptions import InvalidHeaderError
from message import ErrorMessage as message
from status import APIStatus as status
def jwt_required(fn):
"""
A decorator to protect a Flask endpoint.
If you decorate an endpoint with this, it will ensure that the requester
has a valid access token before allowing the endpoint to be called. This
does not check the freshness of the access token.
See also: :func:`~flask_jwt_extended.fresh_jwt_required`
"""
@wraps(fn)
def wrapper(*args, **kwargs):
try:
verify_jwt_in_request()
except NoAuthorizationError:
return {"errno":status.CODE_NO_AUTHORIZATION,"errmsg":message.no_authorization}
except ExpiredSignatureError:
return {"errno":status.CODE_SIGNATURE_EXPIRED,"errmsg":message.authorization_has_expired}
except InvalidHeaderError:
return {"errno":status.CODE_INVALID_AUTHORIZATION,"errmsg":message.authorization_is_invalid}
return fn(*args, **kwargs)
return wrapper当前文件,另一个验证函数jwt_refresh_token_required,代码:
def jwt_refresh_token_required(fn):
"""
A decorator to protect a Flask endpoint.
If you decorate an endpoint with this, it will ensure that the requester
has a valid refresh token before allowing the endpoint to be called.
"""
@wraps(fn)
def wrapper(*args, **kwargs):
try:
verify_jwt_refresh_token_in_request()
except NoAuthorizationError:
return {"errno":status.CODE_NO_AUTHORIZATION,"errmsg":message.no_authorization}
except ExpiredSignatureError:
return {"errno":status.CODE_SIGNATURE_EXPIRED,"errmsg":message.authorization_has_expired}
except InvalidHeaderError:
return {"errno":status.CODE_INVALID_AUTHORIZATION,"errmsg":message.authorization_is_invalid}
return fn(*args, **kwargs)
return wrapperapi接口的状态码和接口提示文本application.utils.language.status,代码:
class APIStatus():
CODE_OK = 1000 # 接口操作成功
CODE_VALIDATE_ERROR = 1001 # 验证有误!
CODE_SMS_ERROR = 1002 # 短信功能执行失败
CODE_INTERVAL_TIME = 1003 # 短信发送冷却中
CODE_NO_AUTHORIZATION = 1004 # 请求中没有附带认证信息
CODE_SIGNATURE_EXPIRED = 1005 # 请求中的认证信息已过期
CODE_INVALID_AUTHORIZATION = 1006 # 请求中的认证信息无效application.utils.language.message,代码:
class ErrorMessage():
ok = "ok"
mobile_format_error = "手机号码格式有误!"
mobile_is_use = "对不起,当前手机已经被注册!"
username_is_use = "对不起,当前用户名已经被使用!"
password_not_match = "密码和验证密码不匹配!"
sms_send_error = "短信发送失败!"
sms_interval_time = "短信发送冷却中!"
sms_code_expired = "短信验证码已过期!"
sms_code_error = "短信验证码不正确!"
sms_is_send = "短信发送中,请留意您的手机短信。"
no_authorization = "用户认证信息校验失败!"
authorization_has_expired = "用户认证信息已过期,请重新登录!"
authorization_is_invalid = "无效的认证信息!"2.服务端提供用户登录的API接口
application.apps.users.views,视图实现并完成登陆接口,代码:
from application import jsonrpc, db
from .marshmallow import MobileSchema, UserSchema
from marshmallow import ValidationError
from message import ErrorMessage as Message
from status import APIStatus as status
@jsonrpc.method('User.mobile')
def mobile(mobile):
"""验证手机号码是否已经注册"""
ms = MobileSchema()
try:
ms.load({'mobile': mobile})
ret = {'errno': status.CODE_OK, 'errmsg': Message.ok}
except ValidationError as e:
ret = {'errno': status.CODE_VALIDATE_ERROR, 'errmsg': e.messages['mobile'][0]}
return ret
@jsonrpc.method('User.register')
def register(mobile, password, password2, sms_code):
"""用户信息注册"""
try:
ms = MobileSchema()
ms.load({'mobile': mobile})
us = UserSchema()
user = us.load({
'mobile': mobile,
'password': password,
'password2': password2,
'sms_code': sms_code
})
data = {'errno': status.CODE_OK, 'errmsg': us.dump(user)}
except ValidationError as e:
data = {'errno': status.CODE_VALIDATE_ERROR, 'errmsg': e.messages}
return data
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity, jwt_refresh_token_required
from flask import jsonify, json
from sqlalchemy import or_
from .models import User
from message import ErrorMessage as message
from status import APIStatus as status
@jsonrpc.method("User.login")
def login(account, password):
"""根据用户登录信息生成token"""
# 1. 根据账户信息和密码获取用户
if len(account) < 1:
return {'errno': status.CODE_NO_ACCOUNT, 'errmsg': message.account_no_data}
user = User.query.filter(or_(
User.mobile == account,
User.email == account,
== account,
)).first()
if user is None:
return {'errno': status.CODE_NO_USER, 'errmsg': message.user_not_exists}
# 验证密码
if not user.check_password(password):
return {'errno': status.CODE_PASSWORD_ERROR, 'errmsg': message.password_error}
# 2. 生成jwt token
access_token = create_access_token(identity=account)
refresh_token = create_refresh_token(identity=account)
return {'access_token': access_token, 'refresh_token': refresh_token}
@jsonrpc.method('')
@jwt_required # 验证jwt
def info():
"""获取用户信息"""
user_data = json.loads(get_jwt_identity()) # get_jwt_identity 用于获取载荷中的数据
print(user_data)
return 'ok'
@jsonrpc.method('User.refresh')
@jwt_refresh_token_required
def refresh():
"""重新获取新的认证令牌token"""
current_user = get_jwt_identity()
# 重新生成token
access_token = create_access_token(identity=current_user)
return access_tokenapplication.utils.language.status,代码:
class APIStatus():
CODE_OK = 1000 # 接口操作成功
CODE_VALIDATE_ERROR = 1001 # 验证有误!
CODE_SMS_ERROR = 1002 # 短信功能执行失败
CODE_INTERVAL_TIME = 1003 # 短信发送冷却中
CODE_NO_AUTHORIZATION = 1004 # 请求中没有附带认证信息
CODE_SIGNATURE_EXPIRED = 1005 # 请求中的认证信息已过期
CODE_INVALID_AUTHORIZATION = 1006 # 请求中的认证信息无效
CODE_NO_ACCOUNT = 1007 # 请求中没有账户信息
CODE_NO_USER = 1008 # 用户不存在
CODE_PASSWORD_ERROR = 1009 # 密码错误application.utils.language.message,代码:
class ErrorMessage():
ok = 'ok'
mobile_format_error = "手机号码格式有误!"
mobile_is_use = "对不起,当前手机已经被注册!"
username_is_use = "对不起,当前用户名已经被使用!"
password_not_match = "密码和验证密码不匹配!"
sms_send_error = "短信发送失败!"
sms_interval_time = "短信发送冷却中!"
sms_code_expired = "短信验证码已过期!"
sms_code_error = "短信验证码不正确!"
sms_is_send = "短信发送中,请留意您的手机短信。"
no_authorization = "用户认证信息校验失败!"
authorization_has_expired = "用户认证信息已过期,请重新登录!"
authorization_is_invalid = "无效的认证信息!"
account_no_data = "对不起,用户账户必须填写!"
user_not_exists = "用户不存在!"
password_error = "密码错误!"

















