odoo12之应用:一、双因子验证(Two-factor authentication, 2FA)(HOTP,TOTP)附源码

前言双因子认证:双因子认证(2FA)是指结合密码以及实物(信用卡、SMS手机、令牌或指纹等生物标志)两种条件对用户进行认证的方法。--百度百科跟我一样"老"的网瘾少年想必一定见过买点卡后上面送的密保(类似但不完全一样),还有"将军令",以及网银的网盾,是一种二次验证的机制;它通常是6位的数字,每次使用后(HOTP)或者一定时间后(TOTP)都将会刷新,大大加大了用户的安全性,OTP(One-Tim...

odoo12之应用:一、双因子验证(Two-factor authentication, 2FA)(HOTP,TOTP)附源码

前言

双因子认证:双因子认证(2FA)是指结合密码以及实物(信用卡、SMS手机、令牌或指纹等生物标志)两种条件对用户进行认证的方法。--百度百科

跟我一样"老"的网瘾少年想必一定见过买点卡后上面送的密保(类似但不完全一样),还有"将军令",以及网银的网盾,是一种二次验证的机制;它通常是6位的数字,每次使用后(HOTP)或者一定时间后(TOTP)都将会刷新,大大加大了用户的安全性,OTP(One-Time Password)分为HOTP(HMAC-based One-Time Password)和TOTP(Time-based One-Time Password)。

HOTP是基于 HMAC 算法加密的一次性密码,以事件同步机制,把事件次序(counter)及相同的密钥(secret)作为输入,通过 HASH 算法运算出一致的密码。

TOTP是基于时间戳算法的一次性密码,基于客户端的时间和服务器的时间及相同的密钥(secret)作为输入,产生数字进行对比,这就需要客户端的时间和服务器的时间保持相对的一致性。

Odoo12集成双因子认证

为了让odoo12的登录也可以使用双因子认证以提高安全性,我们需要:

1、实现OTP验证逻辑2、为ODOO用户界面展示二维码3、为管理员用户提供OTP开关4、在登录界面增加对OTP的验证

我们需要依赖的包:

pip install pyotppip install pyqrcodepip install pypng

实现OTP验证逻辑

首先,我们需要对res.users用户进行重写,添加OTP验证逻辑

# -*- coding: utf-8 -*-import base64import pyotpimport pyqrcodeimport iofrom odoo import models, fields, api, _, toolsfrom odoo.http import requestfrom odoo.exceptions import AccessDeniedimport logging_logger = logging.getLogger(__name__)class ResUsers(models.Model): _inherit = 'res.users' otp_type = fields.Selection(selection=[('time', _('Time based')), ('count', _('Counter based'))], default='time',  string="Type",  help="Type of 2FA, time = new code for each period, counter = new code for each login") otp_secret = fields.Char(string="Secret", size=16, help='16 character base32 secret',  default=lambda self: pyotp.random_base32()) otp_counter = fields.Integer(string="Counter", default=0) otp_digits = fields.Integer(string="Digits", default=6, help="Length of the code") otp_period = fields.Integer(string="Period", default=30, help="Seconds to update code") otp_qrcode = fields.Binary(compute="_compute_otp_qrcode") otp_uri = fields.Char(compute='_compute_otp_uri', string="URI") # 生成二维码 @api.model def create_qr_code(self, uri):  buffer = io.BytesIO()  qr = pyqrcode.create(uri)  qr.png(buffer, scale=3)  return base64.b64encode(buffer.getvalue()).decode() # 将二维码的值赋给otp_qrcode变量 @api.depends('otp_uri') def _compute_otp_qrcode(self):  self.ensure_one()  self.otp_qrcode = self.create_qr_code(self.otp_uri) # 计算otp_uri @api.depends('otp_type', 'otp_period', 'otp_digits', 'otp_secret', 'company_id', 'otp_counter') def _compute_otp_uri(self):  self.ensure_one()  if self.otp_type == 'time':self.otp_uri = pyotp.utils.build_uri(secret=self.otp_secret, name=self.login, issuer_name=self.company_id.name, period=self.otp_period)  else:self.otp_uri = pyotp.utils.build_uri(secret=self.otp_secret, name=self.login, initial_count=self.otp_counter, issuer_name=self.company_id.name, digits=self.otp_digits) # 验证otp验证码是否正确 @api.model def check_otp(self, otp_code):  res_user = self.env['res.users'].browse(self.env.uid)  if res_user.otp_type == 'time':totp = pyotp.TOTP(res_user.otp_secret)return totp.verify(otp_code)  elif res_user.otp_type == 'count':hotp = pyotp.HOTP(res_user.otp_secret)# 允许用户不小心多点20次,但是已经用过的码则无法再次使用for count in range(res_user.otp_counter, res_user.otp_counter20): if count > 0 and hotp.verify(otp_code, count):  res_user.otp_counter = count1  return True  return False # 覆盖原生_check_credentials,增加双因子验证 def _check_credentials(self, password):  super(ResUsers, self)._check_credentials(password)  # 判断是否打开双因子验证并校验验证码  if self.company_id.is_open_2fa and not self.check_otp(request.params.get('tfa_code')):# passraise AccessDenied(_('Validation Code Error!'))

在这里,我们继承了res.users,添加了如下方法:

_compute_otp_uri: 计算otp_uri  create_qr_code: 通过计算的otp_uri生成二维码_compute_otp_qrcode: 调用create_qr_code生成二维码,赋值给otp_qrcode变量 check_otp: 用于验证otp验证码是否正确 _check_credentials: 覆盖原生_check_credentials,判断双因子的开关,调用check_otp进行双因子验证

_check_credentials方法中,我们判断了双因子的开关,而双因子开关是以公司为单位的,因此我们还需要对res.company进行继承添加字段:

# -*- coding: utf-8 -*-from odoo import models, api, fieldsclass ResCompany(models.Model): _inherit = "res.company" is_open_2fa = fields.Boolean(string="Open 2FA", default=False)

为ODOO用户界面展示二维码

我们写好逻辑后,需要在用户界面中将二维码以及配置展示出来:

<?xml version="1.0" encoding="utf-8"?>
源文地址:https://www.guoxiongfei.cn/cntech/26815.html