添加手机验证码
This commit is contained in:
parent
e68e0776bc
commit
738768d412
|
@ -15,6 +15,11 @@ def get_user_info(db: Session, email: str):
|
|||
return user
|
||||
|
||||
|
||||
def get_user_info_by_phone(db: Session, phone: str):
|
||||
user = db.query(User).filter_by(phone=phone).first()
|
||||
return user
|
||||
|
||||
|
||||
def get_full_user_info(db: Session, email: str):
|
||||
user = db.query(User).filter_by(email=email).first()
|
||||
if not user:
|
||||
|
|
|
@ -8,6 +8,7 @@ class User(Base):
|
|||
__tablename__ = "user"
|
||||
# openid = Column(String(255), primary_key=True, comment="用户OpenID")
|
||||
email = Column(String(255), primary_key=True, comment="邮箱")
|
||||
phone = Column(String(255), comment="手机号")
|
||||
name = Column(String(32), comment="用户名")
|
||||
department = Column(Text, comment="部门")
|
||||
post = Column(Text, comment="职务")
|
||||
|
@ -24,6 +25,7 @@ class User(Base):
|
|||
class UserInfo(Base):
|
||||
__tablename__ = "user_info"
|
||||
email = Column(String(255), primary_key=True, index=True, comment="邮箱")
|
||||
phone = Column(String(255), comment="手机号")
|
||||
name = Column(String(32), comment="用户名")
|
||||
department = Column(Text, comment="部门")
|
||||
post = Column(Text, comment="职务")
|
||||
|
|
|
@ -8,7 +8,7 @@ from Schemas.UserSchemas import TokenData
|
|||
from Utils.AuthUtils import token_data_depend, create_token, registered_depend
|
||||
from Utils.CrudUtils import auto_create_crud
|
||||
from Utils.SqlAlchemyUtils import get_db
|
||||
from Utils.VerifyCodeUtils import EmailVerifyCode, EmailVerifyType
|
||||
from Utils.VerifyCodeUtils import EmailVerifyCode, EmailVerifyType, PhoneVerifyCode, PhoneVerifyType
|
||||
from Utils.wxAppUtils import code2Session
|
||||
|
||||
router = APIRouter(
|
||||
|
@ -75,7 +75,7 @@ def change_user_info(req: UserSchemas.ChangeUserInfoReq, token_data: TokenData =
|
|||
@router.post('/bind_email', tags=["用户接口"], summary='邮箱登录')
|
||||
def bind_email(req: UserSchemas.BindEmailReq,
|
||||
db: Session = Depends(get_db)):
|
||||
email= req.email.replace(" ","")
|
||||
email = req.email.replace(" ", "")
|
||||
checked = EmailVerifyCode.check_code(email, req.email_code, EmailVerifyType.change)
|
||||
if not checked:
|
||||
raise HTTPException(detail="邮箱验证码错误", status_code=303)
|
||||
|
@ -96,6 +96,38 @@ def bind_email(req: UserSchemas.BindEmailReq,
|
|||
return {'msg': "成功", 'state': 1, 'data': {'token': token}}
|
||||
|
||||
|
||||
@router.post("/get_phone_verify_code", tags=["用户接口"], summary="获取短信验证码")
|
||||
def get_phone_verify_code(body: UserSchemas.GetPhoneVerifyCodeReq, db=Depends(get_db)):
|
||||
phone = body.phone
|
||||
user = UserCrud.get_user_info_by_phone(db, phone)
|
||||
if not user:
|
||||
raise HTTPException(detail="手机号未录入系统", status_code=303)
|
||||
try:
|
||||
PhoneVerifyCode.send_code(phone, PhoneVerifyType.login)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise HTTPException(detail="验证码发送失败", status_code=403)
|
||||
return {"msg": "验证码已发送至手机,请查看", "state": 1}
|
||||
|
||||
|
||||
@router.post('/login_by_phone', tags=["用户接口"], summary='手机号登录')
|
||||
def login_by_phone(req: UserSchemas.LoginByPhoneReq,
|
||||
db: Session = Depends(get_db)):
|
||||
phone = req.phone.replace(" ", "")
|
||||
checked = PhoneVerifyCode.check_code(phone, req.code, PhoneVerifyType.login)
|
||||
if not checked:
|
||||
raise HTTPException(detail="验证码错误", status_code=303)
|
||||
user = UserCrud.get_user_info_by_phone(db, phone)
|
||||
if not user:
|
||||
raise HTTPException(detail="手机号未录入系统", status_code=303)
|
||||
auth_data = AuthCrud.get_user_auth(db, user.email)
|
||||
user_data = user.to_dict()
|
||||
user_data["auth_data"] = auth_data
|
||||
token_data = TokenData(**user_data).dict()
|
||||
token = create_token(token_data)
|
||||
return {'msg': "成功", 'state': 1, 'data': {'token': token}}
|
||||
|
||||
|
||||
@router.post("/get_email_verify_code", tags=["用户接口"], summary="获取邮箱验证码")
|
||||
def get_email_verify_code(body: UserSchemas.EmailSendReqBody):
|
||||
print(body)
|
||||
|
|
|
@ -39,7 +39,7 @@ class GetUserInfoRes(BaseModel):
|
|||
department: Optional[str]
|
||||
department_list: Optional[List[DepartmentInfo]]
|
||||
registered: Optional[bool]
|
||||
auth_data:Optional[str]
|
||||
auth_data: Optional[str]
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
|
@ -79,6 +79,15 @@ class EmailSendReqBody(BaseModel):
|
|||
email: str = "xxxx@fecr.com.cn"
|
||||
|
||||
|
||||
class LoginByPhoneReq(BaseModel):
|
||||
phone: str
|
||||
code: str
|
||||
|
||||
|
||||
class GetPhoneVerifyCodeReq(BaseModel):
|
||||
phone: str
|
||||
|
||||
|
||||
@unique
|
||||
class DepartmentTypeEnum(Enum):
|
||||
enum01 = "董监高"
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import uuid
|
||||
import hashlib
|
||||
import base64
|
||||
import requests
|
||||
|
||||
# 必填,请参考"开发准备"获取如下数据,替换为实际值
|
||||
url = 'https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1' # APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
|
||||
APP_KEY = "3h499M186sTF8046f9J9I28J1L9m" # APP_Key
|
||||
APP_SECRET = "TMCaZWxoMbuW55l27zdxoDfsiKAH" # APP_Secret
|
||||
sender = "1069368924410005073" # 国内短信签名通道号或国际/港澳台短信通道号
|
||||
TEMPLATE_ID = "527eb7a7b95f4466834347adbde6d53e" # 模板ID
|
||||
|
||||
# 条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
|
||||
# 国际/港澳台短信不用关注该参数
|
||||
signature = "华为云短信测试" # 签名名称
|
||||
|
||||
# 必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔
|
||||
receiver = "+86151****6789,+86152****7890" # 短信接收人号码
|
||||
|
||||
# 选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
|
||||
statusCallBack = ""
|
||||
|
||||
'''
|
||||
选填,使用无变量模板时请赋空值 TEMPLATE_PARAM = '';
|
||||
单变量模板示例:模板内容为"您的验证码是${1}"时,TEMPLATE_PARAM可填写为'["369751"]'
|
||||
双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,TEMPLATE_PARAM可填写为'["3","人民公园正门"]'
|
||||
模板中的每个变量都必须赋值,且取值不能为空
|
||||
查看更多模板和变量规范:产品介绍>模板和变量规范
|
||||
'''
|
||||
TEMPLATE_PARAM = '["369751"]' # 模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
|
||||
|
||||
'''
|
||||
构造X-WSSE参数值
|
||||
@param appKey: string
|
||||
@param appSecret: string
|
||||
@return: string
|
||||
'''
|
||||
|
||||
|
||||
def buildWSSEHeader(appKey, appSecret):
|
||||
now = time.strftime('%Y-%m-%dT%H:%M:%SZ') # Created
|
||||
nonce = str(uuid.uuid4()).replace('-', '') # Nonce
|
||||
digest = hashlib.sha256((nonce + now + appSecret).encode()).hexdigest()
|
||||
|
||||
digestBase64 = base64.b64encode(digest.encode()).decode() # PasswordDigest
|
||||
return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce,
|
||||
now);
|
||||
|
||||
|
||||
def send_phone_code(phone, code):
|
||||
# 请求Headers
|
||||
header = {'Authorization': 'WSSE realm="SDP",profile="UsernameToken",type="Appkey"',
|
||||
'X-WSSE': buildWSSEHeader(APP_KEY, APP_SECRET)}
|
||||
# 请求Body
|
||||
form_data = {'from': sender,
|
||||
'to': "+86" + phone,
|
||||
'templateId': TEMPLATE_ID,
|
||||
'templateParas': f'["{code}"]',
|
||||
'statusCallback': statusCallBack,
|
||||
'signature': signature # 使用国内短信通用模板时,必须填写签名名称
|
||||
}
|
||||
# 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
|
||||
r = requests.post(url, data=form_data, headers=header, verify=False)
|
||||
print(r.text)
|
||||
|
||||
# send_phone_code("+8618090478123","34567812")
|
|
@ -2,6 +2,7 @@ import uuid
|
|||
from enum import Enum
|
||||
from captcha.image import ImageCaptcha
|
||||
from Utils.EmailUtils import send_email
|
||||
from Utils.PhoneMsgUtils import send_phone_code
|
||||
from Utils.RandomUtils import get_random_num_code, get_random_letter_and_num_code
|
||||
from Utils.RedisUtils import redis_pool
|
||||
|
||||
|
@ -13,6 +14,13 @@ class EmailVerifyType(Enum):
|
|||
change = 4
|
||||
|
||||
|
||||
class PhoneVerifyType(Enum):
|
||||
login = 1
|
||||
register = 2
|
||||
reset_password = 3
|
||||
change = 4
|
||||
|
||||
|
||||
class ImageCaptchaVerify:
|
||||
@classmethod
|
||||
def make_captcha_image(cls, code=None, expire_time_s: int = 120):
|
||||
|
@ -33,6 +41,39 @@ class ImageCaptchaVerify:
|
|||
return True
|
||||
|
||||
|
||||
class PhoneVerifyCode:
|
||||
@classmethod
|
||||
def get_phone_id(cls, phone, verify_type: PhoneVerifyType = PhoneVerifyType.login):
|
||||
"""
|
||||
:param phone:
|
||||
:param verify_type: 不同业务验证类型对应不同的id
|
||||
:return:str
|
||||
"""
|
||||
return f"{phone}_EmailVerifyCodeId_{verify_type.name}"
|
||||
|
||||
@classmethod
|
||||
def make_code(cls, phone, expire_time_s=60 * 5, verify_type: PhoneVerifyType = PhoneVerifyType.login):
|
||||
client = redis_pool.get_redis_client()
|
||||
email_id = cls.get_phone_id(phone, verify_type)
|
||||
code = get_random_num_code(4)
|
||||
client.set(email_id, code)
|
||||
if expire_time_s:
|
||||
client.expire(email_id, expire_time_s)
|
||||
return code
|
||||
|
||||
@classmethod
|
||||
def send_code(cls, phone, verify_type: PhoneVerifyType):
|
||||
code = cls.make_code(phone, verify_type=verify_type)
|
||||
send_phone_code(phone, code)
|
||||
|
||||
@classmethod
|
||||
def check_code(cls, phone, code, verify_type: PhoneVerifyType = PhoneVerifyType.login):
|
||||
phone_id = cls.get_phone_id(phone, verify_type=verify_type)
|
||||
client = redis_pool.get_redis_client()
|
||||
phone_code = client.get(phone_id)
|
||||
return code and code == phone_code
|
||||
|
||||
|
||||
class EmailVerifyCode:
|
||||
@classmethod
|
||||
def get_email_id(cls, email, verify_type: EmailVerifyType = EmailVerifyType.login):
|
||||
|
|
Loading…
Reference in New Issue