一、概述

1. 案例介绍

华为开发者空间是为全球开发者打造的专属云上成长空间,深度整合昇腾AI、鸿蒙、鲲鹏等华为根技术。开发者空间在HDC2025上迎来全面升级,新增AI原生应用引擎、AI Notebook、鸿蒙云手机、FunctionGraph云函数、Astro低代码等核心能力,并在算力、模型、平台、应用层实现全方位优化,助力开发者高效完成从编码到调测的全流程,打造智能AI应用开发新体验。

传统用户登录开发深陷 “高成本、低效率、弱安全” 困局,而华为Astro通过 “预制安全能力+可视化编排+云原生运维” 三位一体,实现:

  • 安全零信任:内建金融级防护,杜绝密码泄露风险;
  • 部署小时级:拖拽式开发释放IT资源;
  • 业务可持续:权限热配置支撑敏捷迭代。

2. 案例流程

说明:

  1. 领取华为开发者空间,登录华为开发者空间-低代码应用开发平台
  2. 新建低代码应用,进入Astro轻应用服务控制台主页,开发应用。

3. 资源总览

本案例预计花费0元。

资源名称 规格 单价(元) 时长(分钟)
华为开发者空间-低代码应用开发平台 系统标配 免费 90

最新案例动态,请查阅《基于华为开发者空间Astro低代码应用平台,构建业务用户登录后台开发》。小伙伴快来领取华为开发者空间,进入Astro低代码应用平台实操吧!

二、华为开发者空间-低代码应用开发平台

1. 登录华为开发者空间-低代码应用开发平台

华为开发者空间-低代码应用开发平台是华为云推出的一款可视化应用开发平台,旨在通过"拖拽式"组件和模板化设计,降低开发门槛,提升企业数字化应用构建效率。平台主要特点包括:

  • 可视化开发:通过图形化界面和预置组件,无需编写复杂代码即可快速搭建应用;
  • 全场景支持:覆盖Web、移动端、大屏等多终端应用开发;
  • 高效集成:内置连接器可快速对接华为云及其他主流企业系统;
  • 智能辅助:提供AI辅助开发能力,如智能表单生成、流程自动化等;
  • 企业级能力:具备权限管理、数据安全、高可用等企业所需特性。

Astro平台特别适合业务人员与开发者协同创新,能大幅缩短应用交付周期,典型适用于OA审批、数据看板、轻量级业务系统等场景。

登录华为开发者空间,在左侧菜单列表选择华为开发者空间 -> 开发平台 -> 低代码应用,进入华为开发者空间-低代码应用开发平台

2. 创建低代码应用

  1. 华为开发者空间-低代码应用开发平台页面点击新建低代码应用,在弹出的新建低代码应用对话框中,选择标准应用,点击确定按钮。
  2. 在右侧弹出的新建空白应用配置页签中,配置应用名称标签均为userLoginPage
  3. 点击右下角确认按钮,平台会自动打开一个新的页面:Astro轻应用服务控制台

    :在点击确认后,在Astro轻应用管理页会同时新增一条刚才创建的名称为pro_man__userLoginPage的应用,点击编辑同样可以进入Astro轻应用服务控制台

三、创建用户注册脚本

1. 修改环境变量

进入Astro轻应用服务控制台主页,点击左上角图标,选择环境配置,进入环境配置页面。

在环境配置页面,顶部选择配置。左侧菜单列表选择系统设置 -> 系统参数。在系统参数页面选择内置系统参数,在其下方的内置系统参数列表中,参考下表修改bingo.permission.resource.default.switchbingo.tenant.security.modebingo.security.sensitive.data几个参数的值。

唯一标识 字段含义
bingo.permission.resource.default.switch 访问资源权限默认开关,包括访问流,脚本,BP,连接器,事件
bingo.tenant.security.mode 租户安全模式开关,开启时为安全模式,控制项包括:1. 软件包和数据导出为txt格式 2.脚本上的SDK 开启权限校验,如获取系统参数接口,具体看各脚本SDK说明 3.编译设置组件加密,Flow和Script默认不可见保护 4.自定义接口必须设置业务权限,否则无法访问; 所有用户(包括管理员)都必须要匹配上业务权限才能访问该接口
bingo.security.sensitive.data 是否允许在服务编排,脚本中操作敏感数据

2. 创建用户注册脚本

在Astro轻应用服务控制台页面,点击选择左侧菜单列表中的逻辑,在弹出的子菜单中,点击脚本后面的按钮,在配置界面选择创建一个新脚本,名称设置为registerPortalUser,单击添加

将如下代码导入脚本编辑器中:

import * as buffer from "buffer";
import * as crypto from "crypto";
import * as db from "db";
import * as context from 'context';
import * as http from 'http';
import * as permission from 'permission';
 
//定义入参结构,包括注册账号的用户名、密码和角色,为必填字段
@action.object({ type: "param" })
export class ActionInput {
    @action.param({ type: 'String', required: true, label: 'string' })
    username: string;
    @action.param({ type: 'String', required: true, label: 'string' })
    password: string;
    @action.param({ type: 'String', required: true, label: 'string' })
    role: string;
}
//定义出参结构,出参包含1个参数,portaluser的记录id
@action.object({ type: "param" })
export class ActionOutput {
    @action.param({ type: 'String' })
    msg: string;
}
//使用数据对象PortalUser
@useObject(['PortalUser'])
@action.object({ type: "method" })
export class RegisterPortalUser {    //定义接口类,接口的入参为ActionInput,出参为ActionOutput
    @action.method({ input: 'ActionInput', output: 'ActionOutput' })
    public registerPortalUser(input: ActionInput): ActionOutput {
        let out = new ActionOutput();    //新建出参ActionOutput类型的实例,作pu为返回值
        let error = new Error();    //新建错误类型的实例,用于在发生错误时保存错误信息
        try {
            let s = db.object('PortalUser');
 
            let saltedPassword = salt(input.password);
            let userMsg = {
                "usrName": input.username,
                "name": input.username,
                "userPassword": saltedPassword['saltedPassword'],
                "passwordSalt": saltedPassword['salt'],
                "userType": input.role
            };
 
            let userId = s.insert(userMsg);
            if (userId) {
                out.msg = "注册成功!";
            } else {
                error.name = "USERERROR";
                error.message = "注册失败!";
                throw error;
            }
 
        } catch (error) {
            if (error.name == "405230618") {
                error.message = "该用户名已注册!"
            }
            console.error(error.name, error.message);
            context.setError(error.name, error.message);
        }
        return out;
    }
}
 
function _salt(password: string, saltBuf: buffer.Buffer, encoding: buffer.Encoding = buffer.Encoding.Base64): string {
    const passwordBuf = buffer.from(password)
    const crypt = crypto.pbkdf2(passwordBuf, saltBuf, 1000, 32, crypto.Hashs.SHA1)
    return crypt.toString(encoding)
}
 
function salt(password: string, encoding: buffer.Encoding = buffer.Encoding.Base64): object {
    const saltBuf = crypto.randomBytes(6)
    const saltedPassword = _salt(password, saltBuf, encoding)
    return {
        salt: saltBuf.toString(encoding),
        saltedPassword: saltedPassword
    }
}

在代码编辑器中,插入如下脚本代码。然后单击编辑器上方的,保存脚本。

3. 测试运行脚本

在代码编辑器中点击工具栏按钮,执行脚本程序,

控制台 -> 输入参数,输入如下参数,点击控制台右上角的执行按钮。

{
    "username": "user001",
    "password": "1233",
    "role": "cs"
}

执行成功,会在输出页签返回查询结果。

{
    "msg": "注册成功!"
}

测试成功,单击编辑器上方的,启用发布脚本。

四、创建用户登录脚本

1. 创建用户登录脚本

在Astro轻应用服务控制台页面,点击选择左侧菜单列表中的逻辑,在弹出的子菜单中,点击脚本后面的按钮,在配置界面选择创建一个新脚本,名称设置为login,单击添加

将如下代码导入脚本编辑器中:

import * as buffer from "buffer";
import * as crypto from "crypto";
import * as db from "db";
import * as context from 'context';
//定义入参结构,包括账号的用户名、密码为必填字段,验证码为非必填字段
@action.object({ type: "param" })
export class ActionInput {
    @action.param({ type: 'String', required: true, label: 'string' })
    username: string;
    @action.param({ type: 'String', required: true, label: 'string' })
    password: string;
    @action.param({ type: 'String', required: true, label: 'string' })
    captcha: string;
}
//定义出参结构,出参包含5个参数,登录结果和用户角色
@action.object({ type: "param" })
export class ActionOutput {
    @action.param({ type: 'String' })
    msg: string;
    @action.param({ type: 'String' })
    username: string;
    @action.param({ type: 'String' })
    userId: string;
    @action.param({ type: 'String' })
    captcha: string;
    @action.param({ type: 'String' })
    profile: string;
}
//使用数据对象PortalUser
@useObject(['PortalUser'])
@action.object({ type: "method" })
export class Login {    //定义接口类,接口的入参为ActionInput,出参为ActionOutput
    @action.method({ input: 'ActionInput', output: 'ActionOutput' })
    public login(input: ActionInput): ActionOutput {
        let out = new ActionOutput();    //新建出参ActionOutput类型的实例,作为返回值
        let error = new Error();    //新建错误类型的实例,用于在发生错误时保存错误信息
        try {
            out.captcha = input.captcha;
            let s = db.object('PortalUser');
            let condition = {
                "conjunction": "AND",
                "conditions": [{
                    "field": "usrName",
                    "operator": "eq",
                    "value": input.username
                }]
            };
            let user = s.queryByCondition(condition);
            if (user && user.length == 1) {
                if (validate(user[0].passwordSalt, user[0].userPassword, input.password)) {
                    out.msg = "登录成功!";
                    out.username = user[0].usrName;
                    out.userId = user[0].id;
                    out.profile = user[0].userType;
                } else {
                    out.msg = "密码错误!";
                }
            } else {
                out.msg = "用户不存在!";
            }
        } catch (error) {
            console.error(error.name, error.message);
            context.setError(error.name, error.message);
            out.msg = error.message;
        }
        return out;
    }
}

function _salt(password: string, saltBuf: buffer.Buffer, encoding: buffer.Encoding = buffer.Encoding.Base64): string {
    const passwordBuf = buffer.from(password)
    const crypt = crypto.pbkdf2(passwordBuf, saltBuf, 1000, 32, crypto.Hashs.SHA1)
    return crypt.toString(encoding)
}

function validate(salt: string, userSaltedPassword: string, password: string, encoding: buffer.Encoding = buffer.Encoding.Base64): boolean {
    const saltBuf = buffer.from(salt, encoding);
    const saltedPassword = _salt(password, saltBuf, encoding);
    return saltedPassword === userSaltedPassword
}

在代码编辑器中,插入如下脚本代码。然后单击编辑器上方的,保存脚本。

2. 测试运行脚本

在代码编辑器中点击工具栏按钮,执行脚本程序,

控制台 -> 输入参数,输入如下参数,点击控制台右上角的执行按钮。

{
    "username": "user001",
    "password": "1233",
    "captcha": ""
}

注:测试数据中的usernamepassword参数是在操作步骤“二、创建用户注册脚本”中创建的用户数据。

执行成功,会在输出页签返回查询结果。

{
    "captcha": "",
    "msg": "登录成功!",
    "profile": "cs",
    "userId": "10gg000001NvsTayy0ky",
    "username": "user001"
}

测试成功,单击编辑器上方的,启用发布脚本。

五、创建用户登录服务编排

1. 创建服务编排

用户登录服务编排通过调用脚本、编排图元等操作实现用户登录完整逻辑。

在Astro轻应用服务控制台页面,点击选择左侧菜单列表中的逻辑,在弹出的子菜单中,点击编排后面的按钮,在配置界面选择创建一个新的服务编排,在标签和名称文本框中输入Flow,并设置类型为Autolaunched Flow,单击添加

创建成功后,系统自动进入编排Flow的编辑界面。

用户登录服务编排业务逻辑实现:

  • 通过调用用户登录脚本,查询登录账户密码,然后使用决策图元进行判断,判断当前登录的账号密码是否正确。

  • 如果判断账户密码错误,直接执行账户密码错误,判断账户密码正确,则使用决策图元,继续判断是否有验证码。

  • 如果判断当前登录没有验证码,则直接执行登录,判断当前有验证码,则继续判断验证码是否正确。

  • 如果判断验证码正确,则执行登录操作,判断验证码错误,则执行验证失败。

用户登录服务编排业务逻辑预设图:

2. 定义服务编排变量和公式

  1. 定义服务编排变量

由预设图可知,我们需要定义一些服务编排的变量,预设变量及其数据类型如下:

变量名称(唯一标识) 数据类型
username 文本
password 文本
msg 文本
userId 文本
loginMsg 文本
captcha 文本
profile 文本

操作步骤

在编排Flow的编辑界面,点击右侧的图标展开全局上下文,单击变量后的,设置参数名称。

逐个添加上表中的变量。

  1. 定义服务编排公式

服务编排的公式,预设公式及其表达式如下:

名称 表达式 数据类型
portalUserLogin PORTALUSERLOGIN({!username}) 文本
verifyCode VERIFYCODEWITHTYPE({!captcha},“login”) 文本

操作步骤

在编排Flow的编辑界面,点击右侧的图标展开全局上下文,单击公式后的,设置公式名称、数据类型和表达式,设置完成后点击保存

逐个添加上表中的变量。

3. 创建并配置图元

  1. 创建图元

由用户登录服务编排业务逻辑预设图可知,我们需要1个脚本图元、3个决策图元、3个赋值图元。

在编排Flow的编辑界面操作台,从图元区分别拖拽脚本(1个)、决策(3个)、赋值(3个)图元到画布中。

点击操作台中对应的图元,修改图元标签。

名称(变量唯一标识,不需要修改) 标签
Script0 查询用户
Decision0 判断账号密码
Decision1 判断是否包含验证码
Decision2 校验验证码
Assignment0 账号密码错误
Assignment1 执行登录
Assignment2 验证失败

修改后的图元状态

  1. 配置查询用户脚本图元

在编排Flow的编辑界面操作台,选中查询用户图元,单击右侧的图标,在脚本编辑界面,脚本一栏下拉选项中选择脚本pro_man_login(在操作步骤“三、创建用户登录脚本”中创建)。

单击右上角的全局上下文,显示变量列表,从变量中,拖拽usernamepasswordcaptcha输入参数下对应的源输入框中。

输出参数下,单击5次新增行,依次添加下拉选项中的输出参数字段,并从变量中拖拽相应的字段到目标输入框下,字段与变量对应关系如下图所示。

  1. 配置判断账号密码决策图元

在编排Flow的编辑界面操作台,选中判断账号密码决策图元,单击右侧的决策图标。选中默认,修改默认的名称为loginFail

单击新增,增加一个可编辑的结果,修改结果为loginSuccess,在可视下单击新增行,并从全局上下文拖拽变量中的msg到资源下,设置比较符为==,值为"登录成功!"

  • 请直接从全局上下文拖拽变量msg到资源下,请勿手动输入,手动输入的值系统可能不识别。
  • "登录成功!"需要与login脚本中的输出参数一致。
  1. 配置判断是否包含验证码决策图元

在编排Flow的编辑界面操作台,选中判断是否包含验证码决策图元,单击右侧的决策图标。选中默认,修改默认的名称为hasVerifyCode

单击新增,增加一个可编辑的结果,修改结果为noVerifyCode,在可视下单击新增行,并从全局上下文拖拽变量中的captcha到资源下,设置比较符为==,值为""

  1. 配置校验验证码决策图元

在编排Flow的编辑界面操作台,选中校验验证码决策图元,单击右侧的决策图标。选中默认,修改默认的名称为verifyCodeFail

单击新增,增加一个可编辑的结果,修改结果为verifyCodeSuccess,在右侧选择公式,并从全局上下文拖拽变量中的verifyCode到公式下。

  1. 配置账号密码错误赋值图元

在编排Flow的编辑界面操作台,选中账号密码错误赋值图元,单击右侧的赋值图标,配置赋值。

  • 单击新增行,从全局上下文的系统变量中,拖拽$Flow.ResMsg到赋值下,并设置操作符为=,拖拽msg到值;
  • 单击新增行,从全局上下文的系统变量中,拖拽$Flow.ResCode到赋值下,并设置操作符为=,设置值为"1"
  1. 配置执行登录赋值图元

在编排Flow的编辑界面操作台,选中执行登录赋值图元,单击右侧的赋值图标,配置赋值。

  • 单击新增行,修改变量名称为loginMsg,并设置操作符为=,从全局上下文的公式中拖拽portalUserLogin到值;
  • 单击新增行,修改变量名称为msg,并设置操作符为=,从全局上下文的变量中拖拽msg到值;
  • 单击新增行,修改变量名称为profile,并设置操作符为=,从全局上下文的变量中拖拽profile到值;
  • 单击新增行,修改变量名称为username,并设置操作符为=,从全局上下文的变量中拖拽username到值;
  • 单击新增行,修改变量名称为userId,并设置操作符为=,从全局上下文的变量中拖拽userId到值;
  1. 配置验证失败赋值图元

在编排Flow的编辑界面操作台,选中验证失败赋值图元,单击右侧的赋值图标,配置赋值。

  • 单击新增行,从全局上下文的系统变量中,拖拽$Flow.ResMsg到赋值下,并设置操作符为=,值设置为"验证码错误!"
  • 单击新增行,从全局上下文的系统变量中,拖拽$Flow.ResCode到赋值下,并设置操作符为=,值设置为"1"

4. 拖拽图元连线,并配置连线属性

  1. 创建起始节点
    在编排Flow的编辑界面操作台,把鼠标放在起点图元上,从图元上的“+”拖动鼠标,在起点图元和查询用户图元间增加连线。将当前查询用户脚本图元设置为服务编排的起始节点。

  2. 创建连接主线

依次连接查询用户 -> 判断账号密码 -> 判断是否包含验证码 -> 执行登录图元。

  • 单击判断账号密码 -> 判断是否包含验证码图元之间的连线,在右侧属性单击,在连线中修改连线类型为loginSuccess
  • 单击判断是否包含验证码 -> 执行登录图元之间的连线,在右侧属性单击,在连线中修改连线类型为noVerifyCode
  1. 创建分支连线

1)连接判断账号密码 -> 账号密码错误图元。

2)连接判断是否包含验证码 -> 校验验证码图元。

3)连接校验验证码 -> 验证失败图元。

4)连接校验验证码 -> 执行登录图元。

  • 单击校验验证码 -> 执行登录图元之间的连线,在右侧属性单击,在连线中修改连线类型为verifyCodeSuccess

5. 定义服务编排的输入、输出参数,并保存服务编排

  1. 定义服务编排的输入、输出参数

鼠标在编排Flow的编辑界面操作台画布空白处点一下,单击右侧图标,设置服务编排的输入、输出参数。
输入参数:usernamepasswordcaptcha
输出参数:usernamemsguserIdloginMsgprofile

  1. 保存服务编排

单击服务编排页面上方的,保存服务编排。

6. 测试与发布服务编排执行

  1. 在编排Flow的编辑界面操作台,单击服务编排编辑器上方的,执行服务编排,系统自动跳转至Flow Run界面。

  2. 在Flow Run界面中输入如下测试数据,单击运行按钮。

{
    "username": "user001",
    "password": "1233",
    "captcha": ""
}

注:测试数据中的usernamepassword参数是在操作步骤“二、创建用户注册脚本”中创建的用户数据。

执行成功,界面上会返回设备对象中的全部信息,样例如下:

{
  "interviewId": "002N000001NwLsx1Roci",
  "outputs": {
    "loginMsg": "0000000001Nj8VJuKe0W:i0ZZ83vCb8VpVwX29cQorvn79R2atNJvW6SQpz35nME=",
    "msg": "登录成功!",
    "profile": "cs",
    "userId": "10gg000001NvsTayy0ky",
    "username": "user001"
  }
}
  1. 发布服务编排

测试成功,单击服务编排编辑器上方的,启用发布服务编排。

六、创建公共接口

在Astro轻应用服务控制台页面,点击选择左侧菜单列表中的集成,在弹出的子菜单中,点击开发接口后面的按钮,在右侧新建开放接口界面,编辑配置项。

  • 标签和操作名称:login
  • 版本号:1.0.0
  • URL:/Flow_login
  • API类型:REST
  • 内容类型:application/json
  • 类型:服务编排
  • 资源:pro_man__Flow
  • 方法:POST

将上述配置项配置完成后,点击右下角保存按钮。

开放接口参数说明:

参数 说明 示例
标签 新建API接口的标签,长度不能超过64个字符。 login
操作名称 新建API接口的操作名称,必须以英文字母开头,由英文字母,数字和单下划线组成,不能以单下划线结尾,且长度不能超过40个字符。 login
版本 API接口的版本,格式为“x.y.z”。 1.0.0
URL API接口路径, 固定以 /service/{命名空间}__{应用名}/{版本} 开头, 后面接API的具体路径。此处设置的内容是新建开放接口提供给外部访问的URL。 /service/命名空间__A/版本/Flow_login
类型 选择资源类型,只有服务编排类型的接口能够在服务编排中被调用,其他类型接口只能通过API的形式进行调用。 服务编排
资源 根据类型选择需要绑定的资源。 选择操作步骤“四、创建用户登录服务编排”中创建的服务编排,请确保服务编排已启用,否则此处选择不到。
方法 API接口的HTTP方法。本示例选择“POST”,即请求服务器新增资源或执行特殊操作。 POST

至此,基于华为开发者空间Astro低代码应用平台,构建业务用户登录后台开发案例结束。

关注“华为云开发者联盟”,了解更多技术动态。 更多开发者空间技术干货请关注: 开发者空间案例中心

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐