Skip to content

Commit

Permalink
Merge pull request #1 from suxuefeng20/master
Browse files Browse the repository at this point in the history
合并
  • Loading branch information
niulin lnc authored Jul 23, 2019
2 parents 5a33f1a + 5b3cf22 commit b41c97e
Show file tree
Hide file tree
Showing 15 changed files with 317 additions and 164 deletions.
12 changes: 3 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#### 实现通过手机验码登录odoo系统,前提是在系统用户中设置好用户的手机号码。也可通过手机验证码进行用户注册
### 适用odoo版本: 11、12 社区版和企业版

### 安装模块依赖(必装)
### 安装SDK

> pip install qcloudsms_py
Expand All @@ -12,14 +12,8 @@
### 请自行购买和好配置相应的短信服务商(腾讯云、阿里云)

# 注意事项:

### 对于腾讯云短信正文模板,目前系统支持参数,在腾讯云配置时一定要注意配置两个自定义内容**{1}{2}**
> 短信正文示例: 验证码:{1},请于{2}分钟内填写。如非本人操作,请忽略本短信。
> {1}:表示验证码; {2}表示有效时长
### 对于阿里云的配置模板本模块只允许传递一个参数:
> 短信正文示例: 验证码${code},如非本人操作,请忽略本短信!
> 阿里云只允许配置一个变量,即${code}:验证码
腾讯云和阿里云的短信正文(模板)都必须按照规定的参数变量进行填写详见下面的示例,
腾讯云变量是使用{1}、{2},阿里云是是使用${name}、${code}这样的。注意区分!


### 短信模板示例(其中变量部分必须一致)
Expand Down
3 changes: 2 additions & 1 deletion odoo_sms/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
'auto_install': False,
'data': [
'security/ir.model.access.csv',
'security/odoo_sms.xml',
'views/menu.xml',
'views/sms_config.xml',
'views/sms_templates.xml',
'views/res_user.xml',
'views/sms_record.xml',
]
}
}
136 changes: 18 additions & 118 deletions odoo_sms/controllers/sms_login.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,6 @@ def create_record(self, user_phone, service, sms_sid, code, timeout):
})
return record

def generate_random_numbers(self, length_size):
"""
生成指定位数的随机数字字符串
:param length_size:
:return:
"""
numbers = ""
for i in range(length_size):
ch = chr(random.randrange(ord('0'), ord('9') + 1))
numbers += ch
return numbers

@http.route('/web/check/sms/verification/code', type='http', auth="none")
def check_verification_code(self, **kw):
"""
Expand All @@ -222,7 +210,6 @@ def check_verification_code(self, **kw):
record.sudo().write({'state': 'invalid'})
return json.dumps({'state': False, 'msg': "验证码已失效!请重新获取!"})
records.sudo().write({'state': 'invalid'})

return self._web_post_login(phone)

def _web_post_login(self, phone):
Expand Down Expand Up @@ -252,15 +239,11 @@ def _web_post_login(self, phone):
if user.odoo_sms_token:
password = base64.b64decode(user.odoo_sms_token).decode(encoding='utf-8', errors='strict')
else:
# 发送修改密码的短信至手机
result = self._send_change_password_sms(login, login, phone)
if not result['state']:
return json.dumps({
'state': False,
'msg': "抱歉,由于系统发送修改密码通知短信不成功,操作回退!请联系管理员确认;具体错误Error:{}".format(result['msg'])
})
user.sudo().write({'password': login})
password = login
try:
user.sudo().write({'password': login})
password = login
except Exception as e:
return json.dumps({'state': False, 'msg': "登录失败,具体原因为;{}".format(str(e))})
try:
uid = request.session.authenticate(request.session.db, login, password)
if uid is not False:
Expand All @@ -271,101 +254,6 @@ def _web_post_login(self, phone):
except Exception as e:
return json.dumps({'state': False, 'msg': "登录失败!原因为:{}".format(str(e))})

def _send_change_password_sms(self, login, password, phone):
"""
发送修改密码通知短信
:param login:
:param password:
:param phone:
:return:
"""
services = request.env['sms.service.config'].sudo().search([('state', '=', 'open')])
if not services:
return json.dumps({'state': False, 'msg': "短信服务平台已关闭,请联系管理员处理."})
result = False
for service in services:
if service.sms_type == 'tencent':
result = self.send_change_pwd_sms_by_tencent(login, password, service, phone)
logging.info(result)
if result['state']:
break
elif service.sms_type == 'ali':
logging.info("正在使用阿里云短信平台")
result = self.send_change_pwd_sms_by_aliyun(login, password, service, phone)
logging.info(result)
if result['state']:
break
if result['state']:
return {"state": True, 'msg': "通知短信已发送"}
else:
return {"state": False, 'msg': result['msg']}

def send_change_pwd_sms_by_tencent(self, login, password, service, phone):
"""
腾讯云发送修改密码通知短信
腾讯云短信通知模板: "你好: 你的账户信息已发生改变,新的账户信息为:用户名:{1},密码:{2},请及时登录系统并进行修改!"
:param login:
:param password:
:param service:
:param phone:
:return:
"""
template_id, sms_sign, timeout = self._get_config_template(service, 'change_pwd')
if not template_id or not sms_sign or not timeout:
return {"state": False, 'msg': "在(短信服务配置)中没有找到可用于(修改密码通知模板)的模板,请联系管理员设置!"}
s_sender = SmsSingleSender(service.app_id, service.app_key)
params = [login, password]
try:
result = s_sender.send_with_param(86, phone, template_id, params, sign=sms_sign, extend="", ext="")
logging.info("tencent-sms-change-pwd:{}".format(result))
if result['result'] == 0:
return {"state": True}
else:
return {"state": False, 'msg': "腾讯云发送修改密码短信失败!,Error:{}".format(result['errmsg'])}
except Exception as e:
return {"state": False, 'msg': "腾讯云发送修改密码短信失败,Error:{}".format(str(e))}

def send_change_pwd_sms_by_aliyun(self, login, password, service, phone):
"""
通过阿里云发送修改密码通知短信
短信模板为: "你好: 你的账户信息已发生改变,新的账户信息为:用户名:${name},密码:${pwd},请及时登录系统查看或进行修改!"
:param login:
:param password:
:param service:
:param phone:
:return:
"""
client = AcsClient(service.app_id, service.app_key, 'default')
com_request = CommonRequest()
com_request.set_accept_format('json')
com_request.set_domain("dysmsapi.aliyuncs.com")
com_request.set_method('POST')
com_request.set_protocol_type('https')
com_request.set_version('2017-05-25')
com_request.set_action_name('SendSms')
template_id, sms_sign, timeout = self._get_config_template(service, 'change_pwd')
if not template_id or not sms_sign or not timeout:
return {"state": False, 'msg': "在(短信服务配置)中没有找到可用于(登录时发送验证码)的模板,请联系管理员设置!"}
com_request.add_query_param('PhoneNumbers', phone)
com_request.add_query_param('SignName', sms_sign)
com_request.add_query_param('TemplateCode', template_id)
param_data = {
'name': login,
'pwd': password
}
param_json = json.dumps(param_data)
com_request.add_query_param('TemplateParam', param_json)
try:
cli_response = client.do_action_with_exception(com_request)
cli_res = json.loads(str(cli_response, encoding='utf-8'))
logging.info("ali-sms-result: {}".format(cli_res))
if cli_res['Code'] == 'OK':
return {"state": True}
else:
return {"state": False, 'msg': "阿里云发送修改密码短信失败!,Error:{}".format(cli_res['Message'])}
except Exception as e:
return {"state": False, 'msg': "阿里云发送修改密码短信失败,Error:{}".format(str(e))}

def _get_config_template(self, service, tem_type):
"""
获取可发送验证码的短信模板、签名、超时时长
Expand All @@ -382,4 +270,16 @@ def _get_config_template(self, service, tem_type):
template_id = template.template_id
sms_sign = template.sign_name
timeout = template.timeout
return template_id, sms_sign, timeout
return template_id, sms_sign, timeout

def generate_random_numbers(self, length_size):
"""
生成指定位数的随机数字字符串
:param length_size:
:return:
"""
numbers = ""
for i in range(length_size):
ch = chr(random.randrange(ord('0'), ord('9') + 1))
numbers += ch
return numbers
128 changes: 127 additions & 1 deletion odoo_sms/models/res_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
#
###################################################################################
import base64
import json
import logging
from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
from qcloudsms_py import SmsSingleSender
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest

_logger = logging.getLogger(__name__)

Expand All @@ -44,6 +48,128 @@ def constrains_login_phone(self):
raise UserError("抱歉!{}手机号码已被'{}'占用,请解除或更换号码!".format(res.login_phone, users[0].name))

def _set_password(self):
"""
修改密码后,短信通知到用户
:return:
"""
for user in self:
user.sudo().write({'odoo_sms_token': base64.b64encode(user.password.encode('utf-8'))})
if user.login_phone:
result = self.send_change_password_sms(user.login, user.password, user.login_phone)
if not result['state']:
raise ValidationError("抱歉,系统发送修改密码通知短信不成功,请检查原因;Error:{}".format(result['msg']))
super(ResUsers, self)._set_password()

def send_change_password_sms(self, login, password, phone):
"""
发送修改密码通知短信
:param login:
:param password:
:param phone:
:return:
"""
services = self.env['sms.service.config'].sudo().search([('state', '=', 'open')])
if not services:
return {'state': False, 'msg': "短信服务平台已关闭,请联系管理员处理"}
result = False
for service in services:
if service.sms_type == 'tencent':
result = self._send_change_pwd_sms_by_tencent(login, password, service, phone)
logging.info(result)
if result['state']:
break
elif service.sms_type == 'ali':
logging.info("正在使用阿里云短信平台")
result = self._send_change_pwd_sms_by_aliyun(login, password, service, phone)
logging.info(result)
if result['state']:
break
if result['state']:
return {"state": True, 'msg': "通知短信已发送"}
else:
return {"state": False, 'msg': result['msg']}

def _send_change_pwd_sms_by_tencent(self, login, password, service, phone):
"""
腾讯云发送修改密码通知短信
腾讯云短信通知模板: "你好: 你的账户信息已发生改变,新的账户信息为:用户名:{1},密码:{2},请及时登录系统并进行修改!"
:param login:
:param password:
:param service:
:param phone:
:return:
"""
template_id, sms_sign, timeout = self._get_sms_config_template(service, 'change_pwd')
if not template_id or not sms_sign or not timeout:
return {"state": False, 'msg': "在(短信服务配置)中没有找到可用于(修改密码通知模板)的模板,请联系管理员设置!"}
s_sender = SmsSingleSender(service.app_id, service.app_key)
params = [login, password]
try:
result = s_sender.send_with_param(86, phone, template_id, params, sign=sms_sign, extend="", ext="")
logging.info("tencent-sms-change-pwd:{}".format(result))
if result['result'] == 0:
return {"state": True}
else:
return {"state": False, 'msg': "腾讯云发送修改密码短信失败!,Error:{}".format(result['errmsg'])}
except Exception as e:
return {"state": False, 'msg': "腾讯云发送修改密码短信失败,Error:{}".format(str(e))}

def _send_change_pwd_sms_by_aliyun(self, login, password, service, phone):
"""
通过阿里云发送修改密码通知短信
短信模板为: "你好: 你的账户信息已发生改变,新的账户信息为:用户名:${name},密码:${pwd},请及时登录系统查看或进行修改!"
:param login:
:param password:
:param service:
:param phone:
:return:
"""
client = AcsClient(service.app_id, service.app_key, 'default')
com_request = CommonRequest()
com_request.set_accept_format('json')
com_request.set_domain("dysmsapi.aliyuncs.com")
com_request.set_method('POST')
com_request.set_protocol_type('https')
com_request.set_version('2017-05-25')
com_request.set_action_name('SendSms')
template_id, sms_sign, timeout = self._get_sms_config_template(service, 'change_pwd')
if not template_id or not sms_sign or not timeout:
return {"state": False, 'msg': "在(短信服务配置)中没有找到可用于(登录时发送验证码)的模板,请联系管理员设置!"}
com_request.add_query_param('PhoneNumbers', phone)
com_request.add_query_param('SignName', sms_sign)
com_request.add_query_param('TemplateCode', template_id)
param_data = {
'name': login,
'pwd': password
}
param_json = json.dumps(param_data)
com_request.add_query_param('TemplateParam', param_json)
try:
cli_response = client.do_action_with_exception(com_request)
cli_res = json.loads(str(cli_response, encoding='utf-8'))
logging.info("ali-sms-result: {}".format(cli_res))
if cli_res['Code'] == 'OK':
return {"state": True}
else:
return {"state": False, 'msg': "阿里云发送修改密码短信失败!,Error:{}".format(cli_res['Message'])}
except Exception as e:
return {"state": False, 'msg': "阿里云发送修改密码短信失败,Error:{}".format(str(e))}

@api.model
def _get_sms_config_template(self, service, tem_type):
"""
获取可发送验证码的短信模板、签名、超时时长
:param service:
:param tem_type:
:return:
"""
template_id = 0 # 短信模板ID,需要在短信控制台中申请
sms_sign = "" # 短信签名
timeout = "" # 超时时长 {2}
# 获取可发送验证码的短信模板和签名
for template in service.template_ids:
if template.used_for == tem_type:
template_id = template.template_id
sms_sign = template.sign_name
timeout = template.timeout
return template_id, sms_sign, timeout
18 changes: 13 additions & 5 deletions odoo_sms/models/sms_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ class SmsConfig(models.Model):
active = fields.Boolean('Active', default=True)
name = fields.Char(string='名称')
sms_type = fields.Selection(string=u'平台类型', selection=SmsType, default='tencent', required=True)
app_id = fields.Char(string='AppID标识')
app_key = fields.Char(string='App Key', help="App Key是用来校验短信发送请求合法性的密码")
app_id = fields.Char(string='AppID标识', required=True)
app_key = fields.Char(string='App Key', help="App Key是用来校验短信发送请求合法性的密码", required=True)
priority = fields.Integer(string=u'优先级(1-10)', default=3)
code_length = fields.Integer(string=u'验证码长度', default=6)
state = fields.Selection(string=u'状态', selection=[('close', '关闭'), ('open', '启用')], default='close')
Expand All @@ -47,15 +47,23 @@ class SmsConfig(models.Model):
@api.multi
def update_sms_status(self):
"""
开启服务时,要检查模板列表是否满足要求
开启服务时,强制要检查模板列表是否满足条件
强制要求为:每个配置项必须包含"发送验证码模板、修改密码通知模板"
:return:
"""
for res in self:
is_login = False
is_change_pwd = False
if res.state == 'close':
for template in res.template_ids:
if template.used_for != 'login' and template.used_for != 'change_pwd':
raise UserError(u"短信模板数量不满足要求\r\n需要配置('登录时发送验证码'、'修改密码通知模板')服务模板!")
if template.used_for == 'login':
is_login = True
elif template.used_for == 'change_pwd':
is_change_pwd = True
if is_login and is_change_pwd:
res.write({'state': 'open'})
else:
raise UserError(u"短信模板数量不满足要求\r\n需要同时包含('登录时发送验证码'、'修改密码通知模板')服务模板!")
res.write({'state': 'open'})
else:
res.write({'state': 'close'})
Expand Down
Loading

0 comments on commit b41c97e

Please sign in to comment.