功能說明
對使用者表擴充手機号碼字段,允許使用者通過手機号碼與驗證碼的方式進行認證,注冊,重置密碼以及更換手機号。
多因素身份認證 (MFA) 是保護企業 IT 資源通路安全的一種關鍵工具,也是零信任安全模型的核心組成。特别在遠端辦公以及資料洩露事件層出不窮的背景下,越來越多企業都開始考慮實施多因素身份認證政策。
根據近期一項對 IT 專業人員的調查,52.6%的中小型企業已經要求為所有應用程式和系統登入添加多因素身份認證。其中手機推送認證形式(無密碼認證)的二次認證是保證良好使用者體驗的絕佳選擇。
開源IDaaS方舟一賬通ArkID系統内置手機驗證碼認證插件簡單配置、馬上可用,降低實施成本,提升應用內建效率。

普通使用者:
- 在 “注冊” 頁面實作手機号碼+驗證碼使用者注冊
- 在 “登入” 頁面實作手機号碼+驗證碼使用者登入
- 在 “更改密碼” 頁面實作手機号碼+驗證碼方式下密碼更改
- 在 “我的 - 認證管理“ 中添加重置手機号碼的功能
租戶管理者
- 在”使用者管理 - 使用者清單“中編輯頁面添加手機号碼編輯功能
前置條件
需配合短信插件使用,系統已預設提供阿裡雲短信插件,如需檢視配置方法請移步阿裡雲短信插件文檔。
配置指南
01 插件租賃
經由左側菜單欄依次進入【租戶管理】->【插件管理】,在插件租賃頁面中找到手機驗證碼認證因素插件卡片,點選租賃
02 認證因素配置
經由左側菜單欄依次進入【認證管理】-> 【認證因素】,點選建立按鈕,類型選擇"mobile",短信插件運作時選擇一個合适的已配置的短信插件運作時配置,至此配置完成
03 登入界面
04 注冊界面
05 密碼修改界面
06 更換手機号碼界面
實作思路
普通使用者:手機号碼+驗證碼使用者注冊/登入/重置密碼:
普通使用者:重置手機号碼:
管理者使用者: 更換使用者手機号碼
抽象方法實作
load
authenticate
register
reset_password
create_login_page
create_register_page
create_password_page
create_other_page
create_auth_manage_page
check_auth_data
fix_login_page
代碼
extension_root.com_longgui_auth_factor_mobile.MobileAuthFactorExtension (AuthFactorExtension)
手機短信驗證碼認證因素插件
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
class MobileAuthFactorExtension(AuthFactorExtension):
"""手機短信驗證碼認證因素插件
"""
def load(self):
"""加載插件
"""
super().load()
self.create_extension_config_schema()
self.register_extend_field(UserMobile, "mobile")
from api.v1.schema.auth import AuthIn
from api.v1.schema.user import UserCreateIn,UserItemOut,UserUpdateIn,UserListItemOut
from api.v1.schema.mine import ProfileSchemaOut
self.register_extend_api(
AuthIn,
UserCreateIn,
UserItemOut,
UserUpdateIn,
UserListItemOut,
mobile=(Optional[str],Field(title=_("電話号碼"))),
# areacode=(str,Field(title=_("區号")))
)
self.register_extend_api(
ProfileSchemaOut,
mobile=(Optional[str],Field(readonly=True))
)
# 注冊發送短信接口
self.url_send_sms_code = self.register_api(
'/config/{config_id}/send_sms_code/',
'POST',
self.send_sms_code,
tenant_path=True,
auth=None,
response=SendSMSCodeOut,
)
print(self.url_send_sms_code)
def authenticate(self, event, **kwargs):
""" 認證
通過手機号碼查找使用者并校驗短信驗證碼
Args:
event (Event): 事件
"""
tenant = event.tenant
request = event.request
data = request.POST or json.load(request.body)
mobile = data.get('mobile')
sms_code = data.get('sms_code')
user = User.expand_objects.filter(tenant=tenant,mobile=mobile)
if len(user) > 1:
logger.error(f'{mobile}在資料庫中比對到多個使用者')
return self.auth_failed(event, data=self.error(ErrorCode.CONTACT_MANAGER))
if user:
user = user[0]
if check_sms_code(mobile, sms_code):
user = User.active_objects.get(id=user.get("id"))
return self.auth_success(user,event)
else:
msg = ErrorCode.SMS_CODE_MISMATCH
else:
msg = ErrorCode.MOBILE_NOT_EXISTS_ERROR
return self.auth_failed(event, data=self.error(msg))
@transaction.atomic()
def register(self, event, **kwargs):
""" 注冊使用者
Args:
event (Event): 事件
"""
tenant = event.tenant
request = event.request
data = request.POST or json.load(request.body)
mobile = data.get('mobile')
sms_code = data.get('sms_code')
username = data.get('username')
config = self.get_current_config(event)
ret, message = self.check_mobile_exists(mobile, tenant)
if not ret:
return self.error(message)
if not check_sms_code(mobile, sms_code):
return self.error(ErrorCode.SMS_CODE_MISMATCH)
ret, message = self.check_username_exists(username, tenant)
if not ret:
return self.error(message)
user = User(tenant=tenant)
user.mobile = mobile
user.username = username
user.save()
tenant.users.add(user)
tenant.save()
return user
def reset_password(self, event, **kwargs):
""" 重置密碼
Args:
event (Event): 事件
"""
tenant = event.tenant
request = event.request
data = request.POST or json.load(request.body)
mobile = data.get('mobile')
sms_code = data.get('sms_code')
password = data.get('password')
checkpassword = data.get('checkpassword')
if password != checkpassword:
return self.error(ErrorCode.PASSWORD_IS_INCONSISTENT)
if not check_sms_code(mobile, sms_code):
return self.error(ErrorCode.SMS_CODE_MISMATCH)
user = User.expand_objects.filter(tenant=tenant,mobile=mobile)
if len(user) > 1:
logger.error(f'{mobile}在資料庫中比對到多個使用者')
return self.error(ErrorCode.CONTACT_MANAGER)
if user:
user = user[0]
user = User.active_objects.get(id=user.get("id"))
user.password = make_password(password)
user.save()
return self.success()
return self.error(ErrorCode.MOBILE_NOT_EXISTS_ERROR)
def create_login_page(self, event, config, config_data):
""" 生成手機驗證碼登入頁面Schema描述
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
items = [
{
"type": "text",
"name":"mobile",
"placeholder": "手機号碼",
"append": {
"title": "發送驗證碼",
"http": {
"url": self.url_send_sms_code,
"method": "post",
"params": {
"mobile": "mobile",
"areacode": "86",
},
},
"delay": 60
}
},
{
"type": "text",
"name":"sms_code",
"placeholder": "驗證碼",
}
]
self.add_page_form(config, self.LOGIN, "手機驗證碼登入", items, config_data)
def create_register_page(self, event, config, config_data):
"""生成手機驗證碼使用者注冊頁面Schema描述
因本插件提供重置密碼功能,此處需使用者指定賬号使用者名
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
items = [
{
"type": "text",
"name": "username",
"placeholder": "使用者名"
},
{
"type": "text",
"name":"mobile",
"placeholder": "手機号碼",
"append": {
"title": "發送驗證碼",
"http": {
"url": self.url_send_sms_code,
"method": "post",
"params": {
"mobile": "mobile",
"areacode": "86",
},
},
"delay": 60
}
},
{
"type": "text",
"name":"sms_code",
"placeholder": "驗證碼"
}
]
self.add_page_form(config, self.REGISTER, "手機驗證碼注冊", items, config_data)
def create_password_page(self, event, config, config_data):
"""生成重置密碼頁面Schema描述
通過手機驗證碼重置密碼時需提供手機号碼以及對應驗證碼,同時此處添加新密碼确認機制
注意:重置密碼功能需要啟用使用者名密碼認證插件以提供完整支援
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
items = [
{
"type": "text",
"name":"mobile",
"placeholder": "手機号碼",
"append": {
"title": "發送驗證碼",
"http": {
"url": self.url_send_sms_code,
"method": "post",
"params": {
"mobile": "mobile",
"areacode": "86",
},
},
}
},
{
"type": "text",
"name":"sms_code",
"placeholder": "驗證碼"
},
{
"type": "password",
"name":"password",
"placeholder": "密碼"
},
{
"type": "password",
"name":"checkpassword",
"placeholder": "密碼确認"
}
]
self.add_page_form(config, self.RESET_PASSWORD, "手機驗證碼重置密碼", items, config_data)
def create_other_page(self, event, config, config_data):
"""建立其他頁面(本插件無相關頁面)
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
pass
def check_mobile_exists(self, mobile, tenant):
"""檢查電話号碼是否已存在
Args:
mobile (str): 手機号
tenant (Tenant): 租戶
Returns:
(bool,ErrorCode): mobile是否存在以及對應錯誤
"""
if not mobile:
return False, ErrorCode.MOBILE_EMPTY
if User.expand_objects.filter(tenant=tenant,mobile=mobile).count():
return False, ErrorCode.MOBILE_EXISTS_ERROR
return True, None
def check_username_exists(self,username,tenant):
"""檢查使用者名是否已存在
Args:
username (str): 使用者名
tenant (Tenant): 租戶
Returns:
(bool,ErrorCode): username是否存在以及對應錯誤
"""
# 檢查username是否為空
if not username:
return False, ErrorCode.USERNAME_EMPTY
# 檢查username是否已存在
if User.expand_objects.filter(tenant=tenant,username=username).count():
return False, ErrorCode.USERNAME_EXISTS_ERROR
return True, None
def check_auth_data(self, event, **kwargs):
pass
def fix_login_page(self, event, **kwargs):
pass
def create_auth_manage_page(self):
""" 建立“我的-認證管理”中的更換手機号碼頁面
"""
_pages = []
mine_mobile_path = self.register_api(
"/mine_mobile/",
"GET",
self.mine_mobile,
tenant_path=True,
auth=GlobalAuth(),
response=MineMobileOut
)
upodate_mine_mobile_path = self.register_api(
"/mine_mobile/",
'POST',
self.update_mine_mobile,
tenant_path=True,
auth=GlobalAuth(),
response=UpdateMineMobileOut
)
name = '更改手機号碼'
page = pages.FormPage(name=name)
page.create_actions(
init_action=actions.DirectAction(
path=mine_mobile_path,
method=actions.FrontActionMethod.GET,
),
global_actions={
'confirm': actions.ConfirmAction(
path=upodate_mine_mobile_path
),
}
)
_pages.append(page)
return _pages
def create_extension_config_schema(self):
"""建立插件運作時配置schema描述
"""
select_sms_page = pages.TablePage(select=True,name=_("指定短信插件運作時"))
self.register_front_pages(select_sms_page)
select_sms_page.create_actions(
init_action=actions.DirectAction(
path='/api/v1/tenants/{tenant_id}/config_select/?extension__type=sms',
method=actions.FrontActionMethod.GET
)
)
MobileAuthFactorSchema = create_extension_schema(
'MobileAuthFactorSchema',
__file__,
[
(
'sms_config',
MobileAuthFactorConfigSchema,
Field(
title=_('sms extension config', '短信插件運作時'),
page=select_sms_page.tag,
),
),
(
'code_length',
int,
Field(
title=_('code_length', '驗證碼長度'),
default=6
)
),
(
'expired',
Optional[int],
Field(
title=_('expired', '有效期/分鐘'),
default=10,
)
),
],
BaseAuthFactorSchema,
)
self.register_auth_factor_schema(MobileAuthFactorSchema, 'mobile')
@operation(SendSMSCodeOut)
def send_sms_code(self,request,tenant_id,config_id:str,data:SendSMSCodeIn):
"""發送短信驗證碼
"""
tenant = request.tenant
mobile = data.mobile
config = self.get_config_by_id(config_id)
if not config:
return self.error(ErrorCode.CONFIG_IS_NOT_EXISTS)
if not mobile or mobile=="mobile":
return self.error(ErrorCode.MOBILE_EMPTY)
code = create_sms_code(tenant,mobile,config.config.get('code_length',6),config.config.get("expired",10)*60)
responses = dispatch_event(
Event(
tag=SEND_SMS,
tenant=tenant,
request=request,
data={
"config_id":config.config["sms_config"]["id"],
"mobile":data.mobile,
"code": code,
"areacode": data.areacode,
"username": request.user.username if request.user else ""
},
packages=config.config["sms_config"]["package"]
)
)
if not responses:
return self.error(ErrorCode.SMS_EXTENSION_NOT_EXISTS)
useless, (data, extension) = responses[0]
if data:
return self.success()
else:
return self.error(ErrorCode.SMS_SEND_FAILED)
@operation(UpdateMineMobileOut,roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER])
def update_mine_mobile(self, request, tenant_id: str,data:UpdateMineMobileIn):
""" 普通使用者:更新手機号碼
"""
mobile = data.mobile
ret, message = self.check_mobile_exists(mobile, request.tenant)
if not ret:
return self.error(message)
if not check_sms_code(request.tenant,mobile,data.code):
return self.error(ErrorCode.SMS_CODE_MISMATCH)
user = request.user
user.mobile=data.mobile
user.save()
return self.success()
@operation(MineMobileOut,roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER])
def mine_mobile(self,request,tenant_id: str):
user = request.user
user_expand = User.expand_objects.filter(id=user.id).first()
config = self.get_tenant_configs(request.tenant).first()
if not config:
return self.error(
ErrorCode.CONFIG_IS_NOT_EXISTS
)
return self.success(
data={
"current_mobile": user_expand.get("mobile",None),
"mobile": "",
"code": "",
"config_id": config.id.hex,
},
)
authenticate(self, event, **kwargs)
認證
通過手機号碼查找使用者并校驗短信驗證碼
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def authenticate(self, event, **kwargs):
""" 認證
通過手機号碼查找使用者并校驗短信驗證碼
Args:
event (Event): 事件
"""
tenant = event.tenant
request = event.request
data = request.POST or json.load(request.body)
mobile = data.get('mobile')
sms_code = data.get('sms_code')
user = User.expand_objects.filter(tenant=tenant,mobile=mobile)
if len(user) > 1:
logger.error(f'{mobile}在資料庫中比對到多個使用者')
return self.auth_failed(event, data=self.error(ErrorCode.CONTACT_MANAGER))
if user:
user = user[0]
if check_sms_code(mobile, sms_code):
user = User.active_objects.get(id=user.get("id"))
return self.auth_success(user,event)
else:
msg = ErrorCode.SMS_CODE_MISMATCH
else:
msg = ErrorCode.MOBILE_NOT_EXISTS_ERROR
return self.auth_failed(event, data=self.error(msg))
th_data(self, event, **kwargs):
pass
check_auth_data(self, event, **kwargs)
響應檢查認證憑證事件
Returns:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def check_auth_data(self, event, **kwargs):
pass
check_username_exists(self, username, tenant)
檢查使用者名是否已存在
Parameters:
Returns:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def check_mobile_exists(self, mobile, tenant):
"""檢查電話号碼是否已存在
Args:
mobile (str): 手機号
tenant (Tenant): 租戶
Returns:
(bool,ErrorCode): mobile是否存在以及對應錯誤
"""
if not mobile:
return False, ErrorCode.MOBILE_EMPTY
if User.expand_objects.filter(tenant=tenant,mobile=mobile).count():
return False, ErrorCode.MOBILE_EXISTS_ERROR
return True, None
create_auth_manage_page(self)
建立“我的-認證管理”中的更換手機号碼頁面
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def check_username_exists(self,username,tenant):
"""檢查使用者名是否已存在
Args:
username (str): 使用者名
tenant (Tenant): 租戶
Returns:
(bool,ErrorCode): username是否存在以及對應錯誤
"""
# 檢查username是否為空
if not username:
return False, ErrorCode.USERNAME_EMPTY
# 檢查username是否已存在
if User.expand_objects.filter(tenant=tenant,username=username).count():
return False, ErrorCode.USERNAME_EXISTS_ERROR
return True, None
create_extension_config_schema(self)
建立插件運作時配置schema描述
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def create_extension_config_schema(self):
"""建立插件運作時配置schema描述
"""
select_sms_page = pages.TablePage(select=True,name=_("指定短信插件運作時"))
self.register_front_pages(select_sms_page)
select_sms_page.create_actions(
init_action=actions.DirectAction(
path='/api/v1/tenants/{tenant_id}/config_select/?extension__type=sms',
method=actions.FrontActionMethod.GET
)
)
MobileAuthFactorSchema = create_extension_schema(
'MobileAuthFactorSchema',
__file__,
[
(
'sms_config',
MobileAuthFactorConfigSchema,
Field(
title=_('sms extension config', '短信插件運作時'),
page=select_sms_page.tag,
),
),
(
'code_length',
int,
Field(
title=_('code_length', '驗證碼長度'),
default=6
)
),
(
'expired',
Optional[int],
Field(
title=_('expired', '有效期/分鐘'),
default=10,
)
),
],
BaseAuthFactorSchema,
)
self.register_auth_factor_schema(MobileAuthFactorSchema, 'mobile')
create_login_page(self, event, config, config_data)
生成手機驗證碼登入頁面Schema描述
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def create_login_page(self, event, config, config_data):
""" 生成手機驗證碼登入頁面Schema描述
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
items = [
{
"type": "text",
"name":"mobile",
"placeholder": "手機号碼",
"append": {
"title": "發送驗證碼",
"http": {
"url": self.url_send_sms_code,
"method": "post",
"params": {
"mobile": "mobile",
"areacode": "86",
},
},
"delay": 60
}
},
{
"type": "text",
"name":"sms_code",
"placeholder": "驗證碼",
}
]
self.add_page_form(config, self.LOGIN, "手機驗證碼登入", items, config_data)
create_other_page(self, event, config, config_data)
建立其他頁面(本插件無相關頁面)
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def create_other_page(self, event, config, config_data):
"""建立其他頁面(本插件無相關頁面)
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
pass
create_password_page(self, event, config, config_data)
生成重置密碼頁面Schema描述
通過手機驗證碼重置密碼時需提供手機号碼以及對應驗證碼,同時此處添加新密碼确認機制
注意:重置密碼功能需要啟用使用者名密碼認證插件以提供完整支援
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def create_password_page(self, event, config, config_data):
"""生成重置密碼頁面Schema描述
通過手機驗證碼重置密碼時需提供手機号碼以及對應驗證碼,同時此處添加新密碼确認機制
注意:重置密碼功能需要啟用使用者名密碼認證插件以提供完整支援
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
items = [
{
"type": "text",
"name":"mobile",
"placeholder": "手機号碼",
"append": {
"title": "發送驗證碼",
"http": {
"url": self.url_send_sms_code,
"method": "post",
"params": {
"mobile": "mobile",
"areacode": "86",
},
},
}
},
{
"type": "text",
"name":"sms_code",
"placeholder": "驗證碼"
},
{
"type": "password",
"name":"password",
"placeholder": "密碼"
},
{
"type": "password",
"name":"checkpassword",
"placeholder": "密碼确認"
}
]
self.add_page_form(config, self.RESET_PASSWORD, "手機驗證碼重置密碼", items, config_data)
create_register_page(self, event, config, config_data)
生成手機驗證碼使用者注冊頁面Schema描述
因本插件提供重置密碼功能,此處需使用者指定賬号使用者名
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def create_register_page(self, event, config, config_data):
"""生成手機驗證碼使用者注冊頁面Schema描述
因本插件提供重置密碼功能,此處需使用者指定賬号使用者名
Args:
event (Event): 事件
config (TenantExtensionConfig): 插件運作時配置
"""
items = [
{
"type": "text",
"name": "username",
"placeholder": "使用者名"
},
{
"type": "text",
"name":"mobile",
"placeholder": "手機号碼",
"append": {
"title": "發送驗證碼",
"http": {
"url": self.url_send_sms_code,
"method": "post",
"params": {
"mobile": "mobile",
"areacode": "86",
},
},
"delay": 60
}
},
{
"type": "text",
"name":"sms_code",
"placeholder": "驗證碼"
}
]
self.add_page_form(config, self.REGISTER, "手機驗證碼注冊", items, config_data)
fix_login_page(self, event, **kwargs)
向login_pages填入認證元素
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def fix_login_page(self, event, **kwargs):
pass
load(self)
加載插件
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def load(self):
"""加載插件
"""
super().load()
self.create_extension_config_schema()
self.register_extend_field(UserMobile, "mobile")
from api.v1.schema.auth import AuthIn
from api.v1.schema.user import UserCreateIn,UserItemOut,UserUpdateIn,UserListItemOut
from api.v1.schema.mine import ProfileSchemaOut
self.register_extend_api(
AuthIn,
UserCreateIn,
UserItemOut,
UserUpdateIn,
UserListItemOut,
mobile=(Optional[str],Field(title=_("電話号碼"))),
# areacode=(str,Field(title=_("區号")))
)
self.register_extend_api(
ProfileSchemaOut,
mobile=(Optional[str],Field(readonly=True))
)
# 注冊發送短信接口
self.url_send_sms_code = self.register_api(
'/config/{config_id}/send_sms_code/',
'POST',
self.send_sms_code,
tenant_path=True,
auth=None,
response=SendSMSCodeOut,
)
print(self.url_send_sms_code)
register(self, event, **kwargs)
注冊使用者
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/init.py
@transaction.atomic()
def register(self, event, **kwargs):
""" 注冊使用者
Args:
event (Event): 事件
"""
tenant = event.tenant
request = event.request
data = request.POST or json.load(request.body)
mobile = data.get('mobile')
sms_code = data.get('sms_code')
username = data.get('username')
config = self.get_current_config(event)
ret, message = self.check_mobile_exists(mobile, tenant)
if not ret:
return self.error(message)
if not check_sms_code(mobile, sms_code):
return self.error(ErrorCode.SMS_CODE_MISMATCH)
ret, message = self.check_username_exists(username, tenant)
if not ret:
return self.error(message)
user = User(tenant=tenant)
user.mobile = mobile
user.username = username
user.save()
tenant.users.add(user)
tenant.save()
return user
reset_password(self, event, **kwargs)
重置密碼
Parameters:
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
def reset_password(self, event, **kwargs):
""" 重置密碼
Args:
event (Event): 事件
"""
tenant = event.tenant
request = event.request
data = request.POST or json.load(request.body)
mobile = data.get('mobile')
sms_code = data.get('sms_code')
password = data.get('password')
checkpassword = data.get('checkpassword')
if password != checkpassword:
return self.error(ErrorCode.PASSWORD_IS_INCONSISTENT)
if not check_sms_code(mobile, sms_code):
return self.error(ErrorCode.SMS_CODE_MISMATCH)
user = User.expand_objects.filter(tenant=tenant,mobile=mobile)
if len(user) > 1:
logger.error(f'{mobile}在資料庫中比對到多個使用者')
return self.error(ErrorCode.CONTACT_MANAGER)
if user:
user = user[0]
user = User.active_objects.get(id=user.get("id"))
user.password = make_password(password)
user.save()
return self.success()
return self.error(ErrorCode.MOBILE_NOT_EXISTS_ERROR)
send_sms_code(self, request, tenant_id, config_id, data)
發送短信驗證碼
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
@operation(SendSMSCodeOut)
def send_sms_code(self,request,tenant_id,config_id:str,data:SendSMSCodeIn):
"""發送短信驗證碼
"""
tenant = request.tenant
mobile = data.mobile
config = self.get_config_by_id(config_id)
if not config:
return self.error(ErrorCode.CONFIG_IS_NOT_EXISTS)
if not mobile or mobile=="mobile":
return self.error(ErrorCode.MOBILE_EMPTY)
code = create_sms_code(tenant,mobile,config.config.get('code_length',6),config.config.get("expired",10)*60)
responses = dispatch_event(
Event(
tag=SEND_SMS,
tenant=tenant,
request=request,
data={
"config_id":config.config["sms_config"]["id"],
"mobile":data.mobile,
"code": code,
"areacode": data.areacode,
"username": request.user.username if request.user else ""
},
packages=config.config["sms_config"]["package"]
)
)
if not responses:
return self.error(ErrorCode.SMS_EXTENSION_NOT_EXISTS)
useless, (data, extension) = responses[0]
if data:
return self.success()
else:
return self.error(ErrorCode.SMS_SEND_FAILED)
update_mine_mobile(self, request, tenant_id, data)
普通使用者:更新手機号碼
Source code in extension_root/com_longgui_auth_factor_mobile/__init__.py
@operation(UpdateMineMobileOut,roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER])
def update_mine_mobile(self, request, tenant_id: str,data:UpdateMineMobileIn):
""" 普通使用者:更新手機号碼
"""
mobile = data.mobile
ret, message = self.check_mobile_exists(mobile, request.tenant)
if not ret:
return self.error(message)
if not check_sms_code(request.tenant,mobile,data.code):
return self.error(ErrorCode.SMS_CODE_MISMATCH)
user = request.user
user.mobile=data.mobile
user.save()
return self.success()
extension_root.com_longgui_auth_factor_mobile.sms
check_sms_code(tenant, mobile, code)
驗證短信驗證碼
Source code in extension_root/com_longgui_auth_factor_mobile/sms.py
def check_sms_code(tenant,mobile, code):
""" 驗證短信驗證碼
"""
c_code = cache.get(tenant,gen_sms_code_key(mobile))
return c_code == code
create_sms_code(tenant, phone_number, uth_code_length=6, expired=None)
生成短信驗證碼并存儲至緩存
def create_sms_code(tenant,phone_number,uth_code_length=6,expired:Optional[int]=None):
"""生成短信驗證碼并存儲至緩存
"""
code = gen_sms_code(uth_code_length)
cache.set(tenant,gen_sms_code_key(phone_number), code, expired=expired)
return code
def gen_sms_code_key(mobile):
'''
生成短信驗證碼的key
'''
return f'sms:{mobile}'