SpreadJS ReportSheet 与 DataManager 实现 Token 鉴权:全流程详解与代码解析
在 Web 应用开发中,接口安全是核心需求之一,基于 Token 的身份验证是保障接口不被未授权访问的主流方案。SpreadJS 作为专业的前端表格控件,其报表插件(ReportSheet)常需从后端接口拉取数据展示,但默认请求未内置 Token 传递能力。
本文将基于 GrapeCity 官方示例,详细梳理 "后端鉴权服务搭建→前端 Token 配置→报表数据渲染→Token 动态更新" 的全流程,并为关键代码添加逐行解释,帮助开发者快速理解并落地 Token 鉴权功能。
一、前置准备:搭建支持 Token 鉴权的后端服务
首先需要创建一个 Node.js + Express 后端服务,核心目标是:提供需 Token 鉴权的学生数据接口,并模拟鉴权逻辑。
环境与依赖配置
后端服务依赖 3 个核心库:express
(搭建 Web 服务)、cors
(解决跨域,因前端 SpreadJS 与后端端口不同)、body-parser
(解析 JSON 格式请求体)。
步骤 1:初始化项目与依赖
- 新建项目文件夹(如
spreadjs-token-backend
),执行npm init -y
生成默认package.json
; - 替换
package.json
内容(指定入口文件、脚本命令与依赖):
json
JavaScript
{
"name": "spreadjs-token-demo",
"version": "1.0.0",
"main": "student.js", // 后端入口文件
"scripts": {
"start": "node student.js", // 启动服务的命令
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"body-parser": "^1.20.2", // 解析JSON请求体
"cors": "^2.8.5", // 允许跨域请求
"express": "^4.18.1" // 搭建Web服务的核心库
}
}
1.执行 npm install
安装所有依赖。
编写后端服务代码(student.js)
核心功能:定义学生数据源、配置跨域、实现 "无鉴权测试接口" 与 "Token 鉴权接口"。
JavaScript
// 1. 引入依赖库
const express = require("express"); // 引入Express库,用于创建Web服务
const cors = require("cors"); // 引入CORS库,解决跨域问题
const bodyParser = require('body-parser'); // 引入body-parser,解析JSON请求体
// 2. 初始化Express实例(创建服务对象)
const app = express();
// 3. 配置服务中间件
app.use(cors()); // 启用CORS中间件:允许所有跨域请求(生产环境可限制域名)
app.use(bodyParser.json()); // 启用JSON解析中间件:将请求体转为JSON格式
// 4. 定义数据源:学生列表(模拟数据库数据,实际项目需连接真实数据库)
const studentList = [
{ id: 1, name: "张三", age: 18, sex: 1, classId: 1, grade: "高一" },
{ id: 2, name: "李四", age: 17, sex: 1, classId: 1, grade: "高一" },
{ id: 3, name: "王五", age: 18, sex: 0, classId: 2, grade: "高二" },
{ id: 4, name: "赵六", age: 17, sex: 0, classId: 2, grade: "高二" },
{ id: 5, name: "孙七", age: 16, sex: 1, classId: 3, grade: "高三" }
];
// 数据优化说明:年龄调整为16-18岁(符合高中学生身份),新增grade字段(丰富数据维度)
// 5. 接口1:无鉴权接口(用于测试服务是否正常运行)
app.get("/student", (req, res) => {
// req:请求对象(包含请求参数、头信息等);res:响应对象(用于返回数据)
res.json(studentList); // 直接返回所有学生数据(JSON格式)
});
// 6. 接口2:Token鉴权接口(ReportSheet实际调用的接口)
app.get("/studentByToken", (req, res) => {
// 6.1 从请求头中获取Token(前端需将Token放在Authorization头中)
const token = req.headers["authorization"];
console.log("当前请求Token:", token); // 打印Token到控制台(便于调试)
// 6.2 模拟鉴权逻辑(实际项目需用JWT/OAuth2验证Token有效性)
// 逻辑:仅返回"学生id与Token匹配"的数据(模拟"不同Token对应不同用户权限")
const authorizedStudents = studentList.filter(item => item.id == token);
// 6.3 返回鉴权后的结果(若Token无效/不匹配,返回空数组)
res.json(authorizedStudents);
});
// 7. 启动服务(监听3000端口)
app.listen(3000, () => {
console.log("后端服务已启动:http://localhost:3000"); // 服务启动成功的提示
});
测试后端服务
确保服务正常运行,避免前端联调时因后端问题阻塞:
- 执行
npm run start
启动服务; - 通过浏览器 / Postman 测试两个接口:
- 访问
http://localhost:3000/student
:返回所有学生数据(无鉴权,验证服务可用性); - 访问
http://localhost:3000/studentByToken
(需手动添加请求头Authorization: 1
):返回 "张三" 数据(验证鉴权逻辑); - 若不添加
Authorization
头:返回空数组(验证鉴权拦截逻辑)。
- 访问
二、前端实现:用 DataManager 配置 Token 并渲染 ReportSheet
前端核心逻辑:通过 SpreadJS 的 DataManager
配置 Token 请求头,结合 ReportSheet
渲染鉴权后的数据,并支持 Token 动态更新。
引入 SpreadJS 依赖
需在 HTML 中引入 SpreadJS 核心库、ReportSheet 插件(通过 CDN 引入,无需本地下载):
HTML
<!-- SpreadJS 核心CSS -->
<link rel="stylesheet" href="https://cdn.grapecity.com/spreadjs/16.2.4/css/gc.spread.sheets.excel2013white.css">
<!-- SpreadJS 核心JS -->
<script src="https://cdn.grapecity.com/spreadjs/16.2.4/scripts/gc.spread.sheets.all.min.js"></script>
<!-- ReportSheet 插件JS -->
<script src="https://cdn.grapecity.com/spreadjs/16.2.4/scripts/plugins/gc.spread.sheets.reportdesigner.min.js"></script>
<button id="reset">更新token</button>
<div id="gc-designer-container" style="width:100%; height: 100vh"></div>
前端核心逻辑代码
前端代码分 4 个核心步骤:初始化 SpreadJS 工作簿、配置带 Token 的数据源、创建 ReportSheet 报表、实现 Token 动态更新。
JavaScript
// 等待页面DOM加载完成后执行(避免操作未渲染的元素)
document.addEventListener("DOMContentLoaded", async function () {
// ===================== 步骤1:初始化SpreadJS设计器与工作簿 =====================
// 1.1 创建SpreadJS设计器(Designer):包含工作簿(Workbook),支持可视化操作
const designer = new GC.Spread.Sheets.Designer.Designer(
document.getElementById("gc-designer-container") // 绑定容器元素
);
// 1.2 从设计器中获取工作簿(Workbook是SpreadJS的核心对象,管理所有工作表)
const spread = designer.getWorkbook();
// ===================== 步骤2:用DataManager配置带Token的数据源 =====================
// 2.1 创建DataManager实例(用于管理数据源,处理数据请求/同步)
const dataManager = spread.dataManager();
// 2.2 定义初始Token(实际项目中可从LocalStorage/Cookie读取,如用户登录后存储的Token)
const initialToken = "1";
// 2.3 向DataManager添加"student"数据表(关联后端鉴权接口)
// await:等待数据加载完成(因fetch(true)是异步操作)
await dataManager.addTable("student", {
remote: { // 远程数据源配置(表示数据来自后端接口)
read: { // read:配置"读取数据"的请求参数
url: 'http://localhost:3000/studentByToken', // 后端鉴权接口地址
// options:类似Fetch API的RequestInit配置,用于设置请求头/方法等
options: {
headers: {
"Authorization": initialToken // 关键:将Token添加到请求头
}
}
}
}
}).fetch(true); // fetch(true):添加表后立即拉取数据(true表示强制刷新)
// ===================== 步骤3:创建ReportSheet报表并绑定数据 =====================
// 3.1 添加ReportSheet工作表(类型为reportSheet,区别于普通表格)
const reportSheet = spread.addSheetTab(
0, // 工作表索引(0表示第一个工作表)
'学生报表', // 工作表名称
GC.Spread.Sheets.SheetType.reportSheet // 工作表类型(报表类型)
);
// 3.2 获取报表的"模板工作表"(TemplateSheet):用于配置报表结构(表头、数据绑定)
const templateSheet = reportSheet.getTemplate();
// 3.3 定义报表列:对应学生数据的字段(与后端返回字段一致)
const columns = ['id', 'name', 'age', 'sex', 'classId', 'grade'];
// 3.4 渲染报表模板(循环生成表头与数据绑定单元格)
columns.forEach((columnName, index) => {
// 3.4.1 设置表头(第0行,第index列):显示字段的中文名称
const headerMap = { // 字段名与中文表头的映射(提升报表可读性)
id: "学生ID",
name: "姓名",
age: "年龄",
sex: "性别(1=男/0=女)",
classId: "班级ID",
grade: "年级"
};
templateSheet.setValue(0, index, headerMap[columnName] || columnName);
// 3.4.2 设置数据绑定(第1行,第index列):将单元格与"student"表的字段关联
templateSheet.setTemplateCell(1, index, {
type: "List", // 绑定类型:List(表示列表数据,适合报表展示)
binding: `student[${columnName}]` // 绑定规则:"表名[字段名]"
});
});
// 3.5 刷新报表:确保数据加载后立即渲染到页面
reportSheet.refresh();
// ===================== 步骤4:实现Token动态更新与数据刷新 =====================
// 4.1 给"更新Token"按钮绑定点击事件
document.getElementById('reset').addEventListener('click', async function () {
// 4.2 获取已创建的"student"数据表(从DataManager中获取)
const myTable = spread.dataManager().tables["student"];
// 4.3 修改数据表的请求配置:更新Token为2(实际项目可从用户输入/状态管理中获取新Token)
const newToken = "2";
const options = myTable.options; // 获取当前表的配置
options.remote.read = { // 重写read配置(替换新Token)
url: 'http://localhost:3000/studentByToken', // 保持接口地址不变
options: {
headers: { "Authorization": newToken } // 新Token
}
};
myTable.options = options; // 重新赋值配置(触发DataManager更新)
// 4.4 重新拉取数据(强制刷新,使用新Token)
await myTable.fetch(true);
// 4.5 刷新当前报表:展示新Token对应的鉴权数据
spread.getActiveSheetTab().refresh();
});
});
三、功能验证与结果
初始化状态
页面加载完成后,DataManager
用初始 Token=1 请求接口,报表仅展示 "张三"(id=1)的数据:
学生 ID | 姓名 | 年龄 | 性别(1 = 男 / 0 = 女) | 班级 ID | 年级 |
---|---|---|---|---|---|
1 | 张三 | 18 | 男 | 1 | 高一 |
更新 Token 后
点击 "更新 Token 为 2(获取李四数据)" 按钮,DataManager
用新 Token=2 重新请求,报表刷新后展示 "李四"(id=2)的数据:
学生 ID | 姓名 | 年龄 | 性别(1 = 男 / 0 = 女) | 班级 ID | 年级 |
---|---|---|---|---|---|
2 | 李四 | 17 | 男 | 1 | 高一 |
四、关键说明与扩展建议
1.RequestInit
配置的灵活性
DataManager
的 remote.read.options
与 Fetch API 的 RequestInit
完全兼容,除了 headers
,还可配置:
method: "POST"
:若后端接口为 POST 类型,可添加该配置;body: JSON.stringify({ param: "value" })
:若需传递请求体(如筛选参数),可通过body
配置;credentials: "include"
:若需携带 Cookie(如单点登录场景),可添加该配置。
2.实际项目的 Token 优化
示例中 Token 为固定值,实际项目需优化为:
- Token 存储:将用户登录后的 Token 存储在
LocalStorage
或SessionStorage
(如localStorage.setItem("token", "xxx")
); - Token 读取:初始化 / 更新 Token 时,从存储中读取(如
const token = localStorage.getItem("token")
); - Token 过期处理:在
DataManager
请求失败回调中捕获 401 状态码,跳转至登录页。
3.全局 Token 管理
若项目中有多个 DataManager
请求,可封装全局请求拦截器,避免重复配置 Token:
JavaScript
// 封装全局请求配置函数function getRequestOptions(token) {return {headers: {"Authorization": token,"Content-Type": "application/json"}};}// 多个表复用该函数
dataManager.addTable("student", {remote: {read: { url: "xxx", options: getRequestOptions(token) }}});
dataManager.addTable("teacher", {remote: {read: { url: "xxx", options: getRequestOptions(token) }}});
通过以上步骤,可完整实现 SpreadJS ReportSheet 与 DataManager 的 Token 鉴权功能,兼顾安全性与灵活性,适配实际项目的接口访问需求。