如何将BI 工具与业务系统进行单点登录对接,实现用户权限通用

发布时间:2023/05/04 14:05 发布者:Leo

返回博客中心

BI工具通常服务于业务系统,现有的业务系统有着自己的用户权限系统,并且通常已经稳定的运行了很长时间。那么在引入BI工具之后,就需要解决权限系统和业务系统对接的问题。本文将以Wyn为例,介绍如何将不同系统的用户体系通过单点登录进行对接。

首先来看下两套系统的用户体系功能,左边是BI 工具,右边是业务系统,需要实现用户权限对接和打通:

单点登录体系及用户场景



  • 场景1. 用户登录Wyn BI页面使用第三方业务系统账号

  • 场景2. 用户使用第三方账号登录wyn BI以后需要获取用户信息(包括组织机构、用户上下文)

  • 场景3. 用户在第三方页面调用wyn BI登录接口,获取wyn BI的登录token

Wyn BI安全提供程序接口

ISecurityProvider

  • GenerateTokenAsync 生成用户token的核心方法(也是校验用户的核心方法)

  • ValidateTokenAsync 校验用户token

  • DisposeTokenAsync 注销用户token

  • GetUserRolesAsync 获取用户权限

  • GetUserOrganizationsAsync 获取用户组织机构

  • GetUserDescriptorAsync 获取用户描述

  • GetUserContextAsync 获取用户上下文

ISecurityProviderFactory

  • CreateAsync 根据外部配置生成ISecurityProvider

IExternalUserContext

  • GetValueAsync 获取用户上下文单值属性

  • GetValuesAsync 获取用户上下文多值属性

IExternalUserDescriptor

  • ExternalUserId 用户ID

  • ExternalUserName 用户名称

  • ExternalProvider


可以看到Wyn 提供的这几个接口,指的就是Wyn的用户权限控制模块。

前置配置

当对接Wyn权限体系使用 数据库或 API接口等方式时,往往希望能把关键接口地址 或者数据库配置信息能在前端显示修改, 这样能方便后续修改该配置而不用再修改代码。

关于配置项在前端往往显示为 key:value 形式, 表现为代码层面就是 ISecurityProviderFactory 工厂类的 SupportedSettings 属性,不难看出 SupportedSettings 类型便是 ConfigurationItem 的数组(可迭代)形式, 一般可把需要设置的setting key 通过硬编码的方式赋给 SupportedSettings 对象,来完成配置项对接。

ISecurityProviderFactory 该工厂类的 CreateAsync 方法便是安全提供程序的初始化入口, 在这里可以将外部配置信息通过 ConfigurationItem 对象来注入安全提供程序中, 以便后续查询用户信息使用。

场景1
Picture1.png


由上图可以看出整个 Wyn 登录的接口入口函数就是 GenerateTokenAsync 函数来生成token,该函数的参数就是用户登录输入的用户名称、密码 (其他参数,场景3细讲), 最后产生结果就是一条用户token,这个token 可以理解为用户在wyn 中的当前用户信息索引。

从校验token信息之后的所有函数方法参数都是这条生成的token,所以易知后面的获取用户上下文、用户信息描述、用户权限、用户组织机构. 它们的基本思路都是 token 索引-->获取用户信息-->由用户信息构建要获取的对象(上下文、组织机构......)--> 返回获取对象。


这里构建token的方法没有规定,所以可以使用多种方法来生成token。

  • 将用户信息通过编码加密方式直接存为token,后续获取用户信息直接反向解密即可拿到

  • 将用户信息放到内存(redis)Map(dict)容器中,token即为对应键值对的key,后续通过 get(key) 的方式来获取用户信息

  • 将第三方查询该用户信息的关键参数如 userId, userName 等参数编码为token, 后续通过解密为查询参数然后重新查询用户信息来获取

场景2


Diagram<br /><br />Description automatically generated

显而易见,IExternalUserContext 实际上就是用户信息的访问器, 指定的访问器方法分别是 GetValueAsync 和 GetValuesAsync . 这两个分别对应单值参数和多值参数, 通常情况下, 都会在 UserContext 对象中内置一个 User 对象来实现上述两个访问器的实现逻辑。当然在构建 UserContext 时,用户对象就要建立好, 这个就不赘述了。


IExternalUserDescriptor 类似, 但是因为它的访问属性是固定的,包含了用户id、用户名称等三个属性, 所以没必要再内置 user 对象, 直接构建 IExternalUserDescriptor 对应属性即可

场景3


Picture2.png
获取token接口

// curl 调用

curl --location --request POST 'http://localhost:51980/connect/token' \

--header 'User-Agent: Apifox/1.0.0 (https://www.apifox.cn)' \

--data-urlencode 'username=<username>' \

--data-urlencode 'password=<password>' \

--data-urlencode 'grant_type=<grant_type>' \

--data-urlencode 'client_id=<client_id>' \

--data-urlencode 'client_secret=<client_secret>' \

--data-urlencode 'tenant_path=<tenant_path>' \

--data-urlencode 'access-token-lifetime=<access-token-lifetime>'

//javascript fetch 代码

var myHeaders = new Headers();

myHeaders.append("Content-type", "application/x-www-form-urlencoded");

var urlencoded = new URLSearchParams();

urlencoded.append("username", "<username>");

urlencoded.append("password", "<password>");

urlencoded.append("grant_type", "<grant_type>");

urlencoded.append("client_id", "<client_id>");

urlencoded.append("client_secret", "<client_secret>");

urlencoded.append("tenant_path", "<tenant_path>");

urlencoded.append("access-token-lifetime", "<access-token-lifetime>");

var requestOptions = {

method: 'POST',

headers: myHeaders,

body: urlencoded,

redirect: 'follow'

};

fetch("http://localhost:51980/connect/token", requestOptions)

.then(response => response.text())

.then(result => console.log(result))

.catch(error => console.log('error', error));

// axios 调用

var axios = require('axios');

var qs = require('qs');

var data = qs.stringify({

'username': '<username>',

'password': '<password>',

'grant_type': '<grant_type>',

'client_id': '<client_id>',

'client_secret': '<client_secret>',

'tenant_path': '<tenant_path>',

'access-token-lifetime': '<access-token-lifetime>'

});

var config = {

method: 'post',

url: 'http://localhost:51980/connect/token',

headers: {

"Content-type" : "application/x-www-form-urlencoded"

},

data : data

};

axios(config)

.then(function (response) {

console.log(JSON.stringify(response.data));

})

.catch(function (error) {

console.log(error);

});

以上是 curl 和 javascript fetch/axios 调用获取token的方法, 接下来对参数内容加以说明:

  • username 用户名

  • password 密码

  • grant_type 定值 "password"

  • client_id 客户端id 访问 客户端管理 可查询 或 查询

  • client_secret 客户端私钥 访问 客户端管理 可查询 或 查询

  • tenant_path 组织机构角色参数,形如 表示 A部门1角色下的B部门2角色下的C部门3角色

  • access-token-lifetime token过期时间,单位秒

这部分实际上只是第三方调用Wyn接口的场景,按场景1,场景2的内容开发完理应可以直接适应场景3的, 但需要有几个额外注意的点:

  1. tenant_path 这个参数表示的是组织机构角色参数, 也就是说这个场景下可能出现用户和 Wyn中的组织机构通过这个参数来绑定, 所以在生成 token的方法中也需要额外增加处理。tenant_path 参数并将其和用户信息进行绑定, 以便在后面的获取组织机构方法 GetUserOrganizationsAsync 中使用。

  2. 额外参数, 除了上面接口中的参数之外, 也可以添加其他自定义参数,以供生成token 方法中使用,具体值可以从customizedParam 中拿到。

var externalParams = customizedParam as Dictionary<string, string>;

string externalParam = externalParams["key"]

在 ISecurityProvider 方法中可能需要查询数据库、调用 API、调用SDK 的方式来获取第三方的用户信息, 这里建议加一层抽象的 service 功能层供 ISecurityProvider 调用使用, 在 service 层下层在添加连接数据库或者调用 API 的基础查询层, 这一层内容与业务代码完全无关, 只专注于实现后台基础的查询功能。

拓展阅读

数据孤岛困境不复,自助式BI(商业智能)来破局

BI系统打包Docker镜像及部署的技术难度和实现

数据可视化分析之新技能——魔数图


Wyn 商业智能软件| 下载试用

Wyn 是西安葡萄城自主研发的嵌入式商业智能软件,能帮助企业用户发现更多的数据潜在价值,为管理者制定决策提供数据支撑。Wyn 具备完整的嵌入式分析能力,能够与其他软件深度集成,也可独立部署使用,快速提升数据展示和分析能力。

Wyn 支持公有云、私有云和本地部署等多种部署方式,并能在Java、.net、PHP等开发平台中使用。您可以将其与ERP、CRM、OA等业务系统,以及钉钉、企业微信等APP进行深度集成,持续交付BI和报表功能,助力您的客户发现数据的价值。

了解更多关于 Wyn 嵌入式商业智能软件的内容,请点击此处访问官网,立即下载体验。