写点什么

IDaaS 系统 ArkID 一账通内置插件:图形验证码认证因素的配置流程

作者:龙归科技
  • 2022 年 9 月 13 日
    江苏
  • 本文字数:11456 字

    阅读完需:约 38 分钟

IDaaS 系统ArkID一账通内置插件:图形验证码认证因素的配置流程

图形验证码插件功能介绍


图形验证码认证因素插件对用户认证凭证表单进行扩充,插入图形验证码并实现相关验证功能,是 IDaaS 一账通 ArkID 系统内置功能插件之一。


注意:图形验证码认证因素不具有认证/注册/修改密码等功能,仅对其他认证因素进行凭证元素扩充


普通用户:在 “登录” 页面实现向指定表单插入图形验证码



​图形验证码认证因素配置流程


01 插件租赁


经由左侧菜单栏依次进入【租户管理】->【插件管理】,在插件租赁页面中找到图形验证码认证因素插件卡片,点击租赁



02 租户配置


租赁完成后,进入已租赁列表,找到图形验证码认证因素插件卡片,点击租户配置,配置相关数据



03 认证因素配置


经由左侧菜单栏依次进入【认证管理】-> 【认证因素】,点击创建按钮,类型选择"authcode", 无须配置相关参数,至此配置完成


实现思路


普通用户:图形验证码:



抽象方法实现


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_authcode.AuthCodeAuthFactorExtension (AuthFactorExtension)

图形验证码认证因素插件

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

class AuthCodeAuthFactorExtension(AuthFactorExtension):    """图形验证码认证因素插件    """    def load(self):        """加载插件        """        self.create_extension_config_schema()        self.create_extension_settings_schema()        self.register_extension_api()
super().load()
# 初始化部分配置数据 tenant = Tenant.platform_tenant() if self.get_settings(tenant): settings = { "width": 180, "height": 60, "auth_code_length": 4 }
self.update_or_create_settings(tenant, settings, True, False)
if not self.get_tenant_configs(tenant): config = { "login_enabled": False, "register_enabled": False, "reset_password_enabled": False }
self.create_tenant_config(tenant, config, "图形验证码", "authcode")
def authenticate(self, event, **kwargs): pass
@transaction.atomic() def register(self, event, **kwargs): pass
def reset_password(self, event, **kwargs): pass
def create_login_page(self, event, config, config_data): pass
def fix_login_page(self, event, **kwargs): items = [ { "type": "text", "name": "authcode", "append":{ "type": "image", "http": { "url": self.generate_code_path, "method": "get", }, }, "placeholder":_("图形验证码") }, { "type": "hidden", "name": "authcode_key", }, ] for login_pages,ext in event.data["login_pages"]: for config_id,login_page in login_pages.items(): if config_id == uuid.UUID(event.data["main_auth_factor_id"]).hex: for form in login_page[self.LOGIN]["forms"]: form["items"].extend(items)
def check_auth_data(self, event, **kwargs): """ 响应检查认证凭证事件
Args: event: 事件 """ tenant = event.tenant request = event.request
data = request.POST or json.load(request.body)
authcode = data.get('authcode') authcode_key = data.get('authcode_key')
if not self.check_authcode(event.tenant,authcode,authcode_key):
settings = self.get_settings(tenant) key, code, image = self.get_authcode_picture( settings.settings.get("auth_code_length",4), settings.settings.get("width",180), settings.settings.get("height",60) )
cache.set(event.tenant,key,code,expired=settings.settings.get("expired",10)*60) rs = self.error(ErrorCode.AUTHCODE_NOT_MATCH) rs["data"] = { "image": str(image, 'utf8'), "authcode_key": key }
return False,rs return True,None
def create_register_page(self, event, config, config_data): pass
def create_password_page(self, event, config, config_data): pass
def create_other_page(self, event, config, config_data): pass
def create_auth_manage_page(self): pass
def create_extension_config_schema(self): """创建插件运行时配置schema描述 """ AuthCodeAuthFactorSchema = create_extension_schema( 'AuthCodeAuthFactorSchema', __file__, [ ( 'login_enabled', bool, Field( default=False, title=_('login_enabled', '启用登录'), readonly=True ) ), ( 'register_enabled', bool, Field( default=False, title=_('register_enabled', '启用注册'), readonly=True ) ), ( 'reset_password_enabled', bool, Field( default=False, title=_('reset_password_enabled', '启用重置密码'), readonly=True ) ), ], BaseAuthFactorSchema, ) self.register_auth_factor_schema(AuthCodeAuthFactorSchema, 'authcode')
def create_extension_settings_schema(self): """创建租户配置schama """ AuthCodeAuthFactorSettingsSchema = create_extension_schema( 'AuthCodeAuthFactorSettingsSchema', __file__, [ ('width', int, Field(title=_("验证码图片宽度"),default=180)), ('height', int, Field(title=_("验证码图片高度"),default=60)), ('auth_code_length', int, Field(title=_("验证码长度"),default=4)), ( 'expired', Optional[int], Field( title=_('expired', '有效期/分钟'), default=10, ) ), ] )
self.register_settings_schema(AuthCodeAuthFactorSettingsSchema)
def get_random_char(self,auth_code_length=4)->str: """获取随机字符组合
Args: auth_code_length (int, optional): 图形验证码长度. Defaults to 4.
Returns: str: 随机字符串 """ chr_all = string.ascii_letters + string.digits str_random = ''.join(random.sample(chr_all, auth_code_length)) return str_random
def get_random_color(self, low, high): """获取随机颜色
Args: low (int): 下限 high (int): 上限
Returns: tuple(int,int,int): RGB """ return ( random.randint(low, high), random.randint(low, high), random.randint(low, high), )
def get_authcode_picture(self,auth_code_length=4,width=180,height=60): """制作验证码图片
Args: auth_code_length (int, optional): 验证码长度. Defaults to 4. width (int, optional): 图形宽度. Defaults to 180. height (int, optional): 图形高度. Defaults to 60.
Returns: tuple(str,str,image): 缓存key,图形验证码,图片 """ # 创建空白画布 image = Image.new('RGB', (width, height), self.get_random_color(20, 100)) # 验证码的字体 font = ImageFont.truetype( os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), 'assets/stxinwei.ttf' ), 40 ) # 创建画笔 draw = ImageDraw.Draw(image) # 获取验证码 char_4 = self.get_random_char(auth_code_length) # 向画布上填写验证码 for i in range(auth_code_length): draw.text( (40 * i + 10, 0), char_4[i], font=font, fill=self.get_random_color(100, 200), ) # 绘制干扰点 for x in range(random.randint(200, 600)): x = random.randint(1, width - 1) y = random.randint(1, height - 1) draw.point((x, y), fill=self.get_random_color(50, 150)) # 模糊处理 image = image.filter(ImageFilter.BLUR) key = self.generate_key() buf = BytesIO() # 将图片保存在内存中,文件类型为png image.save(buf, 'png') byte_data = buf.getvalue() base64_str = base64.b64encode(byte_data) return key, char_4, base64_str
def generate_key(self): """生成随机key
Returns: str: 随机key """ key = '{}'.format(uuid.uuid4().hex) return key
@operation(GenrateAuthCodeOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER]) def get_authcode(self, request, tenant_id: str): """ 获取图形验证码 """ tenant = Tenant.active_objects.get(id=tenant_id) settings = self.get_settings(tenant) key, code, image = self.get_authcode_picture( settings.settings.get("auth_code_length",4), settings.settings.get("width",180), settings.settings.get("height",60) )
cache.set(request.tenant,key,code,expired=settings.settings.get("expired",10)*60) return { "data": { "image": str(image, 'utf8'), "authcode_key": key } }
@operation(CheckAuthCodeOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER]) def check_auth_code(self,request,tenant_id:str,data:CheckAuthCodeIn): """ 校验图形验证码 """ if self.check_authcode(request.tenant,data.authcode, data.authcode_key): return self.success() else: return self.error( ErrorCode.AUTHCODE_NOT_MATCH )
def check_authcode(self,tenant,authcode,authcode_key): """校验图形验证码 """ return authcode_key and cache.get(tenant,authcode_key).lower() == authcode.lower()
def register_extension_api(self): """注册插件API """ self.generate_code_path = self.register_api( '/auth_code/', 'GET', self.get_authcode, tenant_path=True, auth=None, response=GenrateAuthCodeOut, )
self.check_code_path = self.register_api( '/auth_code/', 'POST', self.check_auth_code, tenant_path=True, auth=None, response=CheckAuthCodeOut, )
复制代码


authenticate(self, event, **kwargs)

抽象方法:认证

Parameters:

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def authenticate(self, event, **kwargs): pass

check_auth_code(self, request, tenant_id, data)

校验图形验证码

Source code in extension_root/com_longgui_auth_factor_authcode/init.py


@operation(CheckAuthCodeOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER])
def check_auth_code(self,request,tenant_id:str,data:CheckAuthCodeIn):
""" 校验图形验证码
"""
if self.check_authcode(request.tenant,data.authcode, data.authcode_key):
return self.success()
else:
return self.error(
ErrorCode.AUTHCODE_NOT_MATCH
)
复制代码

check_auth_data(self, event, **kwargs)

响应检查认证凭证事件

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def check_auth_data(self, event, **kwargs):    """ 响应检查认证凭证事件
Args: event: 事件 """ tenant = event.tenant request = event.request
data = request.POST or json.load(request.body)
authcode = data.get('authcode') authcode_key = data.get('authcode_key')
if not self.check_authcode(event.tenant,authcode,authcode_key):
settings = self.get_settings(tenant) key, code, image = self.get_authcode_picture( settings.settings.get("auth_code_length",4), settings.settings.get("width",180), settings.settings.get("height",60) )
cache.set(event.tenant,key,code,expired=settings.settings.get("expired",10)*60) rs = self.error(ErrorCode.AUTHCODE_NOT_MATCH) rs["data"] = { "image": str(image, 'utf8'), "authcode_key": key }
return False,rs return True,None
复制代码

check_authcode(self, tenant, authcode, authcode_key)

校验图形验证码

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def check_authcode(self,tenant,authcode,authcode_key):    """校验图形验证码    """    return authcode_key and cache.get(tenant,authcode_key).lower() == authcode.lower()
复制代码

create_auth_manage_page(self)

认证管理页面描述


Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def create_auth_manage_page(self):    pass
复制代码


create_extension_config_schema(self)

创建插件运行时配置 schema 描述

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def create_extension_config_schema(self):    """创建插件运行时配置schema描述    """    AuthCodeAuthFactorSchema = create_extension_schema(        'AuthCodeAuthFactorSchema',        __file__,        [            (                'login_enabled',                 bool,                 Field(                    default=False,                     title=_('login_enabled', '启用登录'),                    readonly=True                )            ),            (                'register_enabled',                 bool,                 Field(                    default=False,                     title=_('register_enabled', '启用注册'),                    readonly=True                )            ),            (                'reset_password_enabled',                 bool,                 Field(                    default=False,                     title=_('reset_password_enabled', '启用重置密码'),                    readonly=True                )            ),        ],        BaseAuthFactorSchema,    )    self.register_auth_factor_schema(AuthCodeAuthFactorSchema, 'authcode')
复制代码


create_extension_settings_schema(self)

创建租户配置 schama

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def create_extension_settings_schema(self):    """创建租户配置schama    """    AuthCodeAuthFactorSettingsSchema = create_extension_schema(        'AuthCodeAuthFactorSettingsSchema',        __file__,        [            ('width', int, Field(title=_("验证码图片宽度"),default=180)),            ('height',  int, Field(title=_("验证码图片高度"),default=60)),            ('auth_code_length',  int, Field(title=_("验证码长度"),default=4)),            (                'expired',                 Optional[int],                Field(                    title=_('expired', '有效期/分钟'),                    default=10,                )            ),        ]    )
self.register_settings_schema(AuthCodeAuthFactorSettingsSchema)
复制代码


create_login_page(self, event, config, config_data)

抽象方法:组装登录页面表单



Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def create_login_page(self, event, config, config_data): pass
复制代码

create_other_page(self, event, config, config_data)

抽象方法:组装登录页上其他操作表单


Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def create_other_page(self, event, config, config_data): pass
复制代码

create_password_page(self, event, config, config_data)


抽象方法:组装重置密码页面表单


Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def create_password_page(self, event, config, config_data): pass


create_register_page(self, event, config, config_data)

抽象方法:组装注册页面表单

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

fix_login_page(self, event, **kwargs)

向 login_pages 填入认证元素

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def fix_login_page(self, event, **kwargs):    items = [        {            "type": "text",            "name": "authcode",            "append":{                "type": "image",                "http": {                    "url": self.generate_code_path,                    "method": "get",                },            },            "placeholder":_("图形验证码")        },        {            "type": "hidden",            "name": "authcode_key",        },    ]    for login_pages,ext in event.data["login_pages"]:        for config_id,login_page in login_pages.items():            if config_id == uuid.UUID(event.data["main_auth_factor_id"]).hex:                for form in login_page[self.LOGIN]["forms"]:                    form["items"].extend(items)
复制代码

generate_key(self)

生成随机 key

Returns:

Source code in extension_root/com_longgui_auth_factor_authcode/init.py


def generate_key(self):    """生成随机key
Returns: str: 随机key """ key = '{}'.format(uuid.uuid4().hex) return key
复制代码

get_authcode(self, request, tenant_id)

获取图形验证码

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

@operation(GenrateAuthCodeOut, roles=[TENANT_ADMIN, PLATFORM_ADMIN, NORMAL_USER])def get_authcode(self, request, tenant_id: str):    """ 获取图形验证码    """    tenant = Tenant.active_objects.get(id=tenant_id)    settings = self.get_settings(tenant)    key, code, image = self.get_authcode_picture(        settings.settings.get("auth_code_length",4),        settings.settings.get("width",180),        settings.settings.get("height",60)    )
cache.set(request.tenant,key,code,expired=settings.settings.get("expired",10)*60) return { "data": { "image": str(image, 'utf8'), "authcode_key": key } }
复制代码

get_authcode_picture(self, auth_code_length=4, width=180, height=60)

制作验证码图片

Parameters:

Returns:

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def get_authcode_picture(self,auth_code_length=4,width=180,height=60):    """制作验证码图片
Args: auth_code_length (int, optional): 验证码长度. Defaults to 4. width (int, optional): 图形宽度. Defaults to 180. height (int, optional): 图形高度. Defaults to 60.
Returns: tuple(str,str,image): 缓存key,图形验证码,图片 """ # 创建空白画布 image = Image.new('RGB', (width, height), self.get_random_color(20, 100)) # 验证码的字体 font = ImageFont.truetype( os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), 'assets/stxinwei.ttf' ), 40 ) # 创建画笔 draw = ImageDraw.Draw(image) # 获取验证码 char_4 = self.get_random_char(auth_code_length) # 向画布上填写验证码 for i in range(auth_code_length): draw.text( (40 * i + 10, 0), char_4[i], font=font, fill=self.get_random_color(100, 200), ) # 绘制干扰点 for x in range(random.randint(200, 600)): x = random.randint(1, width - 1) y = random.randint(1, height - 1) draw.point((x, y), fill=self.get_random_color(50, 150)) # 模糊处理 image = image.filter(ImageFilter.BLUR) key = self.generate_key() buf = BytesIO() # 将图片保存在内存中,文件类型为png image.save(buf, 'png') byte_data = buf.getvalue() base64_str = base64.b64encode(byte_data) return key, char_4, base64_str
复制代码

get_random_char(self, auth_code_length=4)

获取随机字符组合

Parameters:

Returns:

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def get_random_char(self,auth_code_length=4)->str:    """获取随机字符组合
Args: auth_code_length (int, optional): 图形验证码长度. Defaults to 4.
Returns: str: 随机字符串 """ chr_all = string.ascii_letters + string.digits str_random = ''.join(random.sample(chr_all, auth_code_length)) return str_random
复制代码


get_random_color(self, low, high)

获取随机颜色

Parameters:

Returns:

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def get_random_color(self, low, high):    """获取随机颜色
Args: low (int): 下限 high (int): 上限
Returns: tuple(int,int,int): RGB """ return ( random.randint(low, high), random.randint(low, high), random.randint(low, high), )
复制代码

load(self)

加载插件

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def load(self):    """加载插件    """    self.create_extension_config_schema()    self.create_extension_settings_schema()    self.register_extension_api()
super().load()
# 初始化部分配置数据 tenant = Tenant.platform_tenant() if self.get_settings(tenant): settings = { "width": 180, "height": 60, "auth_code_length": 4 }
self.update_or_create_settings(tenant, settings, True, False)
if not self.get_tenant_configs(tenant): config = { "login_enabled": False, "register_enabled": False, "reset_password_enabled": False }
self.create_tenant_config(tenant, config, "图形验证码", "authcode")
复制代码

register_extension_api(self)

注册插件 API

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def register_extension_api(self):    """注册插件API    """    self.generate_code_path = self.register_api(        '/auth_code/',        'GET',        self.get_authcode,        tenant_path=True,        auth=None,        response=GenrateAuthCodeOut,    )
self.check_code_path = self.register_api( '/auth_code/', 'POST', self.check_auth_code, tenant_path=True, auth=None, response=CheckAuthCodeOut, )
复制代码

reset_password(self, event, **kwargs)

抽象方法:响应重置密码事件

Parameters:

Source code in extension_root/com_longgui_auth_factor_authcode/init.py

def reset_password(self, event, **kwargs):    pass
复制代码

ArkID 方舟一账通

一款插件化、多租户、云原生的开源统一身份认证授权管理解决方案/身份云管理平台,采用 AGPL-3.0 开源协议;支持多种标准协议(LDAP, OAuth2, SAML, OpenID),细粒度权限控制,完整的 WEB 管理功能,钉钉、企业微信集成等。ArkID 既可以作为企业终端客户资产统一管理 CIAM,可作为企业内部雇员、外部伙伴统一身份管理平台 EIAM;助企业构建标准化的用户身份体系。如果希望快速的了解系统的基本使用,可以访问官方 IDaaS 注册账号后创建自己的租户,即可使用系统的大部分功能。如果希望体验超级管理员,安装配置插件等,推荐使用私有化部署的方式。

发布于: 刚刚阅读数: 5
用户头像

龙归科技

关注

还未添加个人签名 2021.01.11 加入

「龙归科技」致力于让每个组织拥有专属的自动化办公操作系统,助力企业或政府拥抱(Cloud Native First)云原生优先战略,帮助客户构筑以「身份与应用」为中心的现代化 IT 基础设施!

评论

发布
暂无评论
IDaaS 系统ArkID一账通内置插件:图形验证码认证因素的配置流程_单点登录_龙归科技_InfoQ写作社区