简介中关村科技软件公司的《中关企业门户平台CenEP》是一份面向通用行业的企业门户方案文档适合企业信息化负责人、架构师及门户平台评估者阅读主要解决异构系统与数据分散造成的信息孤岛问题。资源为1个doc文档压缩包大小313KB内容结构完整适合快速通读或作为产品选型参考。目前已有105人浏览学习。文档系统梳理了CenEP的核心能力通过统一界面实现一站式访问支持Office、Lotus、Exchange及ERP、CRM、SCM等业务系统整合提供符合JSR 168标准的本地化Portlet构件库以及IFrame、WSRP等多种集成方式同时涵盖单点登录、个性化桌面、协作工具、内容管理CMS、报表展现与安全授权等模块并给出支持的操作系统、应用服务器、数据库、认证系统等运行环境与最低硬件配置有助于读者评估平台适用性并理解门户建设的关键技术要点。1. 企业门户平台 CenEP 能解决什么问题从信息孤岛到统一入口的实战定位负责过跨系统协同的人大概都体会过这种无奈发薪日在 OA 里确认流程再去人事系统里逐条核对工资项最后切到 ERP 里导一张薪酬表中间还要人工同步三次账号状态。这几年低代码平台很火可很多团队折腾一圈后发现真正能把“信息孤岛”重新粘起来的反而是像 CenEP 这种轻量级的企业门户平台。CenEP 是某软件公司在多个企业集成项目里沉淀出来的一套门户实施框架核心只做四件事统一登录、菜单聚合、待办集成和权限下发。它不碰业务系统的核心表结构而是把散落在各系统里的登录状态、导航目录、权限点这些公共逻辑抽离出来形成一个独立的中间层。这个定位让它在不改动存量系统代码的前提下先把“人从哪里进、能看到什么、能点哪些按钮”这几个入口问题解决掉。所以说CenEP 不是又一个业务系统而是给业务系统们修的一条康庄大路。如果你正被多套系统地址搞到头晕或者每次有人入职都要手动开七八个账号那这篇笔记会把它的架构、接线方式和最坑的边界条件讲透照着步骤走三到五天就能在测试环境拉起一套可用的门户雏形。2. CenEP 的架构拆解为什么门户中间件不能只做“页面拼接”把 CenEP 当成一台“页面拼接机”是最常见的误判。很多团队一看到门户首页能挂菜单、能嵌 iframe就开始把业务流程往里面塞最后把轻量门户硬生生用成了重型业务中台。实际上 CenEP 的设计精髓只有三个横向职责会话管理、目录聚合、路由分发。下面从选型边界、权限模型和认证设计三层把它的架构逻辑讲清楚。2.1 门户平台与低代码平台的边界差异先把四个选型原则定下来在选型汇报里我最常被问到的问题是“CenEP 和低代码平台到底什么区别、能不能互相替代”。这里头有个很现实的分工低代码平台擅长把流程变成表单和状态机而 CenEP 这样的门户平台既不建业务字段也不存业务单据。它的数据模型基本固定为人员、组织、应用、菜单、角色、路由。围绕着这个差异我在方案设计时通常先定下四条选型原则。第一门户只做导航和路由绝不在门户内解析复杂的业务表单如果诉求是“要在门户里审批单据”正确做法是接一个流程引擎进来而不是让门户平台去读表单结构。第二业务数据是各系统的私有财产CenEP 只约束主数据同步的接口字段比如用户编号、部门编号、角色编码具体业务逻辑一概不管。第三门户统一了认证入口但各业务系统必须保留自己的按钮级权限CenEP 把用户角色下发给系统系统内部仍然要自己拦截越权访问。第四门户扩展必须走插件化的路由编排而不是在源代码里硬编码跳转地址。为了在立项阶段让干系人都对齐口径我常用下面这张对比表来一锤定音。对比维度CenEP 门户平台常见低代码平台数据模型仅保存组织、菜单、认证等元数据保存业务表单与流程计算表核心能力单点登录、路由跳转、菜单聚合表单设计、流程审批、数据建模对现有系统影响不改现有代码只接管入口可能形成新的数据孤岛实施成本集成多、代码量少、配置多业务建模成本高适合从零起步这四条原则在遇到“门户里建个报表中心”这类需求时特别有用。我见过不少新团队高估了门户的能力把报表数据源、权限角色、字段字典全部塞进门户库最后门户升级一次全链路都要回归测试这就是没有守住边界导致的翻车现场。2.2 CenEP 的目录与权限模型组织信息如何驱动应用可见性CenEP 的权限体系可以看成三层纵切第一层是“用户—组织”关系通常从 HR 系统或 LDAP 目录定时同步第二层是“角色—应用”关系角色决定用户能看见哪几个应用图标第三层是“应用—菜单”关系菜单具体到页面按钮最终由业务系统本地权限和 CenEP 下发的功能权限共同决定。这层模型里CenEP 用两张核心表应用表 菜单表就完成了对多系统的导航控制。新应用接入后的第一步是注册 app_id管理员再把菜单树挂到对应角色下这套菜单 JSON 会同时写到 Redis 服务和客户端本地。每次用户刷新页面优先读本地缓存里的菜单快照性能开销极低这也是门户首屏能做到秒开的关键优化点。拿一个典型的菜单注册 JSON 来举例结构看起来是下面这个样子{ appId: hr_system, appName: 人力资源平台, menus: [ { menuId: m_emp_query, menuName: 员工事务, url: /hr/query, roles: [hr_admin, dept_head], children: [ { menuId: m_emp_edit, menuName: 员工建档, url: /hr/query/edit, roles: [hr_admin] } ] } ] }这个结构的语义很直观同一个应用不同角色登录后打开的菜单完全不一样。需要说明的是menu_id 在全系统内必须唯一URL 这栏我强烈建议只写相对路径把域名和网关前缀统一交给 CenEP 的路由层去拼接。roles 数组是权限下发的关键后续做权限调整也主要改这里。在实施时很容易忽略的一点是菜单变更后要不要立刻对所有用户生效。一般批次同步完菜单后我会要求运维把对应应用下的菜单缓存键全部清理掉否则老用户会抱着旧菜单过完一整天进而产生“系统坏了”的误判。至于缓存清理的细节后面第 5 章会具体展开。2.3 CenEP 的会话与认证设计用统一登录模型代替点对点登录老架构里最大的问题是每个系统各自维护一套登录态用户换个系统就要重新输一次账号管理员也要在每套系统里重复开账号。CenEP 介入后的做法是建一个中心认证节点用户访问业务系统时如果发现本地会话不存在浏览器会被重定向到 CenEP 的统一登录页登录成功后CenEP 生成一个一次性令牌并把这个令牌附在回跳地址里交还给业务系统业务系统拿令牌到 CenEP 后端校验换回用户身份数据后写入自己的会话。这就是标准的单点登录闭环令牌的有效期一般控制在 120 秒最多允许校验 5 次密钥用随机长字符串混淆。这里有个容易被忽略的设计CenEP 并不要求所有应用共享同一份会话 Cookie它允许各应用保留自己的 Session 机制只要能正确执行一次“令牌换身份”的握手即可。这个宽容度给了那些十几年历史的老系统极大的接入空间。不过有利就有弊这种设计对业务系统自身的登录拦截器要求比较高如果拦截器没有正确放行登录回调接口就会出现“无限重定向”的问题。这一块的代码级坑点我会在第 4 章结合具体实现来聊。3. 在本地把 CenEP 跑起来初始化脚本、YAML 配置与接口冒烟测试实施 CenEP 的第一步就是在本地把一套能跑通“登录—发令牌”闭环的基础环境拉起来。不要一上来就模拟生产高可用先把数据库、配置文件、启动顺序这三个基础搞定后面所有对接工作才有落脚点。3.1 执行建库脚本CenEP 初始化 SQL 的字段设计与字符集处理CenEP 的表结构很克制核心就那么几张。最基础的是应用注册表和菜单功能表我给出一份可以直接套用的初始化脚本-- 创建应用注册表用于存放 CenEP 已接入的应用信息 CREATE TABLE cenep_app ( app_id VARCHAR(64) PRIMARY KEY, app_name VARCHAR(128) NOT NULL, app_url VARCHAR(512) NOT NULL, sso_enabled TINYINT NOT NULL DEFAULT 1, icon_path VARCHAR(256) NULL, status TINYINT NOT NULL DEFAULT 1, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin; -- 创建菜单功能表菜单必须归属某一个应用 CREATE TABLE cenep_menu ( menu_id VARCHAR(64) PRIMARY KEY, app_id VARCHAR(64) NOT NULL, parent_id VARCHAR(64) DEFAULT NULL, menu_name VARCHAR(128) NOT NULL, menu_url VARCHAR(512) NOT NULL, sort_order INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, CONSTRAINT fk_menu_app FOREIGN KEY (app_id) REFERENCES cenep_app(app_id), INDEX idx_menu_app (app_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;建库时字符集是第一个血泪教训。必须统一写成 utf8mb4排序规则用 utf8mb4_bin避免使用服务器默认的 latin1 或者 utf8_general_ci。很多门户首页白屏追根到底就是这张表里的中文字段在持久层被截断最后拼出来的 JSON 直接解析失败。另外parent_id 用来做多级菜单树sort_order 决定同级菜单的排列顺序这两个字段在建表时就要想好后期再补会涉及大表重建。菜单树嵌套深度建议不要超过五层因为门户前端在递归渲染菜单时深度太深会增加编译成本和回调复杂度。如果用的是 ORM 工具自动建表记得手动把 idx_menu_app 这个联合索引补上否则菜单表在数据量过千后按应用维度查询会明显变慢。3.2 编写 application.yml连接 CenEP 后端时的防踩坑参数定义以 Java 生态为例CenEP 的主要配置可以收敛到一个 application.yml 文件里server: port: 8080 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/cenep_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai username: cenep_user password: change_me_in_prod cenep: auth: instance-id: cenep-main secret: a-32-char-random-token ticket-timeout-seconds: 120 cache: type: redis ttl-seconds: 3600这份配置里instance-id 是给中心认证节点用的身份标识同一个门户集群里的多个实例必须配成同一个值否则后续登录会被路由到错误的认证节点。secret 是签名用的密钥生产环境一定要用配置中心或环境变量注入别写死在代码仓库里否则一次源码泄露等于把整个门户的认证防线都交了出去。ticket-timeout-seconds 是令牌有效期设置太短会导致业务系统重试时失败太长又会增加令牌被重放的风险120 秒是个比较稳妥的默认值。还有一个我常被问到的点Redis 的 TTL 为什么默认给 3600 秒。这个值决定菜单快照在缓存里的存活时间如果业务上要求“改权限后立即生效”就需要把菜单 TTL 调短或者引入消息队列主动清缓存。调短 TTL 会带来性能和数据库压力需要在两者之间拿捏好度。一般我习惯按角色维度做菜单缓存而不是按用户维度这样权限变更时只需清理该角色对应的几个键。3.3 启动服务与冒烟测试用 curl 验证门户首页取数和统一认证状态服务启动后不要急着打开浏览器看界面。先用 curl 把认证主链路传一遍能更快速、更准确定位问题。以下是三条最基本的冒烟测试命令# 1. 探测 CenEP 首页是否能正常发出版本号-k 用于处理本地自签名证书 curl -k -X GET http://127.0.0.1:8080/cenep/api/version # 2. 尝试用错误的密码登录检验认证拦截是否生效 curl -k -X POST -H Content-Type: application/json \ -d {username:admin,password:bad_password} \ http://127.0.0.1:8080/cenep/api/login # 3. 用正确密码登录抓取返回的令牌 curl -k -X POST -H Content-Type: application/json \ -d {username:admin,password:correct_password} \ http://127.0.0.1:8080/cenep/api/login第一条命令如果返回了版本号说明应用层的数据库连接、配置加载都已经就绪。第二条命令如果返回 HTTP 401 而不是 200说明认证拦截器已经开始正常工作如果返回 200那就要检查是不是过滤器放行了所有请求。第三条命令成功之后会返回一个 ticket 字段下一步对接业务系统时这个 ticket 就是传递身份的凭证。这里给一个验收标准在本地把 CenEP 跑通的标准不是首页画得有多好看而是在请求层能完整完成“登录—颁发令牌”这个闭环。如果 curl 层面通了第 4 章的对接就有了底气一旦 curl 这关过不去浏览器里的问题会更难排查因为所有网络跳转和 Cookie 状态都混在一起。4. 接入真实业务将新应用挂到 CenEP 门户中的三条关键链路门户平台的价值在于接入真实业务。这一章我们模拟把一个“某订单管理系统”挂到 CenEP 门户下围绕单点登录、菜单注册、灰度发布这三条链路逐一演示。4.1 实现单点登录握手CenEP 的令牌校验与本地会话回写逻辑业务系统的后端必须提供一个接口来消费 CenEP 重定向过来的令牌参数。在 Java 后端里典型写法如下// 业务系统接住 CenEP 跳转请求中的 ticket 参数 public void handleSsoCallback(String ticket, HttpSession session, HttpServletResponse resp) { // 1. 调用 CenEP 的校验接口换取用户身份 AuthUser user CenEPClient.validateTicket(ticket); if (user null) { // 校验失败踢回统一登录页 resp.sendRedirect(LOGIN_URL); return; } // 2. 写入本地会话并把角色信息一并存入避免后续重复查询 session.setAttribute(loginUser, user); session.setAttribute(loginRoles, user.getRoles()); // 3. 跳转业务系统首页 resp.sendRedirect(/home); }CenEPClient.validateTicket 这一步必须设置合理的超时时间建议 2 秒因为如果认证中心响应变慢业务系统的登录链路会被拖垮用户会以为业务系统宕机了。写会话时不要只存用户名一定要把 roles 列表也塞进去否则后续页面的按钮权限还要再跨系统调用一次造成不必要的网络延迟。从安全性考虑绝不建议直接把用户编号写进 Cookie 里那样等于随便改个编号就能冒充他人登录安全扫描也一定不会放过这个问题。4.2 通过 REST 接口注册菜单把新系统的页面和按钮权限同步到 CenEP菜单注册可以直接调用 CenEP 的 REST 接口比如把“订单中心”挂到门户导航树里# 将新系统“订单管理”注册为 CenEP 里的顶级目录 curl -k -X POST \ -H Authorization: Bearer admin_token \ -H Content-Type: application/json \ -d { appId: order_system, appName: 订单中心, parentId: 0, menuId: order_home, menuName: 订单综合查询, menuUrl: /order/list, roles: [ops, admins] } \ http://127.0.0.1:8080/cenep/api/menu/register这个接口会做三件事第一从管理员令牌中解析操作者身份第二检查 order_system 这个应用是否存在如果没注册过就自动在应用表里新建第三把菜单写入菜单表并触发 Redis 缓存刷新。这个接口是同步的当菜单表数据量大时响应时间可能接近一秒所以更推荐的做法是把全量菜单整理成一个 JSON 文件然后用脚本循环注册。需要注意的是接口内部要对 appId 和 menuId 做幂等处理防止重复注册导致门户菜单重复出现。重复菜单是交付现场最常见的“小白问题”表现出来就是用户门户左侧一排长得一模一样的图标点进去还是同一个地址。4.3 最小化灰度发布给 CenEP 增加一个“只对指定部门开放”的灰度开关把新系统一上线就向全员开放最容易引发群体性故障。CenEP 的灰度能力通常挂在路由层用一段简单的规则配置就可以实现cenep: gray-rule: - appId: order_system version: v2.0 enabled: true share: 30 department-whitelist: [研发部, 测试部]参数说明share 表示 30% 的用户会看到新系统入口department-whitelist 定义白名单部门只要命中白名单就无条件放行新版本。两套规则建议设计成“或”逻辑这样核心业务部门永远能提前验收其他部门逐步放量。这里有个容易踩的坑门户前端只是控制了“显示新入口”但后端 API 地址如果仍然指向老服务就会出现新首页点到旧系统的现象数据对不上用户还以为是新系统有毛病。灰度切换的本质发生在 CenEP 路由层路由引擎判断当前用户命中了灰度条件就把请求转发到订单系统 v2 集群反之则转发到 v1 集群。灰度规则在实施时要同时作用于路由层和菜单渲染层前后端都验证通过灰度才算真正生效。5. 一线交付避坑CenEP 在真实项目中让我印象深刻的 4 个坑交付一套门户平台百分之八十的调试时间都耗在“登录—跳转—菜单显示”这条闭环上。以下四个坑是不同项目里实打实排过的雷每个都按现象、原因、解决的顺序拆开讲。5.1 坑一门户首页白屏原因竟然是登录使用的字符集没对齐现象CenEP 部署在机房服务器后访问首页一切正常但一旦输入账号登录整个页面直接白屏后端日志里报一堆无法解析的字符异常。原因数据库初始化时沿用了服务器默认的 latin1 字符集而应用配置的 JDBC 连接是 utf8mb4。两边的字符集不一致导致登录返回的消息体里中文字段被截断成乱码前端拿到的 JSON 解析失败页面就白屏了。解决按第 3.1 节给出的建库规范重建数据库并在 JDBC URL 上显式加上 characterEncodingutf8mb4。同时检查应用容器自身的默认字符集比如 Tomcat 的 URIEncoding 和 Linux 服务器的 LANG 环境变量确保全链路都统一到 UTF-8。5.2 坑二单点登录回调地址被防火墙拦截白白折腾三天现象联调时业务系统的登录页面跳转到 CenEP 是正常的但登录成功后回调业务系统时浏览器一直提示“无法访问此网站”。本地用 HTTP 调试一切正常一到测试环境的 HTTPS 就挂掉了。原因CenEP 生成的回调地址里默认用的是内部服务的 HTTP 端口比如 8080而测试环境的云安全组只开放了 443 和 80 两个端口内网 8080 端口被防火墙拦截了。解决在 CenEP 的回跳地址适配器里强制指定外层网关的协议和映射端口让 HTTPS 的外部域名能被正确转发到内部服务。排查时可以用 curl 从客户端直接访问这个回调地址一旦提示连接超时或证书不匹配基本都能锁定到网络链路和证书配置这两个问题根源。5.3 坑三新员工看到的菜单是旧的权限在缓存里没刷新现象管理员给新员工分配了工程师角色但新员工登录 CenEP 后左侧菜单栏依然只有“访客”菜单反复刷新也没有变化。原因CenEP 的菜单权限在用户首次登录时会渲染成一个快照存进 Redis默认过期时间是 3600 秒。管理员在权限变更后没有主动清理缓存查询时仍然命中了旧快照。解决开发管理后台的“权限变更”入口每次变更后主动清掉对应用户的菜单缓存键命令形如 DEL cenep:menu:{appId}:{userId}然后强制该用户重新登录。更优的方案是把缓存粒度做成“角色维度”权限变更时只需清理对应角色的缓存所有属于该角色的用户下次刷新时会自动拉取新菜单既省力又避免缓存爆炸。5.4 坑四并发高峰时数据库连接池被占满整站出现“无法获取连接”错误现象月末集中处理审批时门户页面弹出一堆 Connection is not available 的错误数据库 CPU 并没有跑满可请求就是等不到连接。原因CenEP 默认的数据库连接池 maximum-pool-size 是 20业务侧没有给门户配置独立的线程池导致权限查询和菜单加载请求都挤在这 20 个连接上。连接从池里被占满后后续所有操作只能排队干等。解决第一把最大连接数提高到 100并设置合理的 connection-timeout第二在 CenEP 后端增加一层基础数据缓存把应用、菜单、角色这类低频变更的数据放到 Redis 持久缓存里请求不再直接穿透到数据库第三为数据库和 Redis 配置熔断限流当并发超过阈值时优先保证核心页面的存活而不是让整个门户被拖死。连接池大小要结合服务器内存来看100 个连接在数据库侧不算多但要清楚每个连接的内存开销和防火墙端口占用。6. 让 CenEP 的日常运行更省心的三个习惯日志深挖、缓存巡检与配置备份CenEP 的复杂度集中在初期集成后期的稳定性主要靠持续的运维习惯来保障。这里分享三个我在交付后长期使用的方法都能直接落进监控系统或巡检脚本里。6.1 用一条命令把 CenEP 的接口时效和会话过期日志变成报警依据CenEP 每台实例会输出访问日志重点过滤“会话过期”和“令牌异常”这两类记录tail -f /logs/cenep/access.log | grep -E ticket.*403|session.*expired发现 403 记录在持续增长说明业务系统和 CenEP 之间的域名解析或服务器时钟出现了偏差。第一步先校正服务器时钟到 NTP第二步再检查请求头里有没有正确传递令牌。这个命令挂到监控群里能第一时间捕捉到认证链路的异常苗头。6.2 每月检查一次 CenEP 权限点是否漂移用 SQL 扫出孤儿菜单权限点漂移是指“菜单还挂在导航里但触发这个菜单的本地权限已经不存在了”这是门户系统最常见的数据垃圾。我习惯在每月底执行下面这条 SQLSELECT m.menu_name, r.role_name FROM cenep_menu m LEFT JOIN cenep_role_menu rm ON m.menu_id rm.menu_id LEFT JOIN cenep_role r ON rm.role_id r.role_id WHERE m.status 1 AND r.role_id IS NULL;这条查询能找出那些状态为启用、却没有挂给任何角色的“孤儿菜单”。这类垃圾菜单是用户报“点了没权限”的头号嫌疑对象。新系统接入后及时清掉它们可以给后续接手的人减轻不少排查负担。6.3 把 CenEP 的交付参数做成固化清单减少系统移交成本很多系统上线一年后出问题一半原因在于接手的人根本不知道当初为什么要把某个参数配成那个值。以下是我交付时固定输出的一份交接参数清单参数模块必查项参考默认值月度检查标准认证中心ticket 有效期、密钥轮换周期120 秒 / 90 天检查密钥是否泄漏提醒到期轮换数据库连接池maximum-pool-size超时时间20观察高峰期使用率不得超过 80%菜单缓存TTL 时长清理策略3600 秒手动核对是否存在异常残留缓存灰度开关share 值白名单无确认新版本开放部门是否符合预期我接手新项目时有个习惯就是先把第 6.2 节的孤儿菜单 SQL 跑一遍把 6.1 节的日志挂上报警最后把 6.3 节的清单贴进交接文档。这套流程下来很多历史遗留问题都能在初期暴露出来而不是等到半夜被电话叫醒才去翻日志。做实施的日子久了就会发现门户这类系统本身不难难的是把参数和状态管得明明白白。希望这篇笔记能帮你在 CenEP 这条路上少踩一点坑让交付和运维都多一些从容希望帮到你。本文还有配套的精品资源点击获取