☰
门户网站全套源码解析:Spring Boot + MyBatis 前后台一体化实战
2026/10/21 22:02:53 网站建设 项目流程

简介:这是一套面向 Java Web 开发者的门户网站完整源码,涵盖门户前台与后台管理两个部分,适合需要快速搭建企业官网、资讯门户或后台管理系统的初中级开发者。项目采用前后端分离机制,后台基于 MySQL,内置初始化脚本 tianti_stage.sql 与默认管理员账号,便于本地部署和登录验证;前端为支持多端自适应的静态站点,可部署到 Nginx 或 Web 容器,并可通过 config.js 配置接口地址与后台地址。压缩包约 308.58MB,zip 格式,包内主体包括 Java 后端模块、前端 gateway 静态资源、数据库脚本及配置代码等,结构清晰。目前已有 1337 人学习下载。学习这份源码可以快速理解门户前台展示、后台管理、前后端联调与多端适配的完整实现思路,直接基于它二次开发能节省大量基础环境搭建时间。

1. 门户网站全套源码:一个项目同时拿捏前台展示和后台管理

第一次拆这套门户网站全套源码时,我的第一反应是「这不就是一套 CMS 吗」,但深入看下去才发现,它在权限控制、模板解析和内容发布上做了很多融合的活儿,尤其是把门户展示端和管理后台放在同一个项目里,却又能分开维护,这个设计让我印象很深。这套源码对三类人特别有价值:想快速上线一个企业官网或信息门户的人,正在学完整 Web 项目开发流程的初学者,以及把源码当脚手架改造成自己产品的开发者。对于只想找个软件/插件源码参考的人来说,它也是一份难得的完整工程范例——门户该有的功能、后台该有的能力,全都有。

2. 技术栈与工程结构:前后端一体的门户系统长什么样

2.1 整体架构选型

拿到的这套门户网站源码,我把它归成两个大块:门户前台和后台管理。前台负责内容展示、栏目导航、站内搜索,后台负责内容录入、审核发布、用户权限、栏目配置。整体走的是单体应用里比较典型的分层思路,没有刻意拆微服务,对我来说这反而是好事——它让我在本地就能完整跑通一条业务链路,排查问题也不用在多个服务之间来回跳。

技术栈上,我梳理了几个核心点。后端基于 Java 的 Spring Boot 框架构建,持久层用 MyBatis 配合 MySQL 数据库,权限认证采用 Shiro 框架做登录校验和角色控制,前端页面使用 Thymeleaf 模板引擎渲染。门户页面和后台页面都在同一个 Web 应用里,通过 URL 路径前缀做区分,后台路径统一挂在 /admin 下。这种做法的好处是部署简单,一个 Jar 包就能搞定全部功能,配套资源少,对还没完整上过生产环境的团队特别友好。

有人可能会问,为什么不选 Vue + Spring Boot 的前后端分离方案?原因也很现实:如果只是内容展示和后台维护,服务端渲染能直接输出完整 HTML,对搜索引擎更友好,门户网站要的就是 SEO 可读性。而管理后台虽然是交互密集型页面,用 Thymeleaf 配合 JQuery 也已经足够,没必要为了追求技术热度引入一套 Node 构建链。这套源码选择服务端渲染,说明它是奔着实际落地去设计的。

2.2 工程目录结构

说完了选型,再来看目录。一个合格的源码包,必须能让接手的人一眼看出模块边界。这套源码的目录结构是这样的:

portal-project ├── portal-admin # 后台管理模块 │ ├── controller # 后台接口控制器 │ ├── service # 业务逻辑层 │ ├── mapper # MyBatis 数据访问接口 │ └── resources │ └── mapper # XML 映射文件 ├── portal-core # 公共核心模块 │ ├── entity # 实体类 │ ├── config # 全局配置 │ └── util # 工具类 ├── portal-web # 门户前台模块 │ ├── controller # 前台页面控制器 │ └── resources │ ├── templates # Thymeleaf 模板 │ ├── static # 静态资源 │ └── application.yml └── sql # 数据库初始化脚本

这个拆分逻辑是清晰的:portal-core 放公共代码,portal-web 处理门户页面,portal-admin 处理后端逻辑,sql 目录放所有表结构。我第一次拿到源码时,第一件事就是先看 sql 目录,因为表结构能直接反映这个项目的业务深度。

2.3 数据库设计要点

数据库脚本里一共包含十几张业务表,核心几张表这里重点说一下:用户表存储后台账号,角色表定义角色,权限表绑定资源,内容表是门户文章的核心,栏目表维护导航结构,这两张表通过栏目 ID 关联。

CREATE TABLE `t_content` ( `id` int(11) NOT NULL AUTO_INCREMENT, `category_id` int(11) NOT NULL COMMENT '所属栏目', `title` varchar(255) NOT NULL COMMENT '文章标题', `summary` varchar(500) DEFAULT NULL COMMENT '摘要', `content` text COMMENT '正文内容', `status` tinyint(4) DEFAULT '0' COMMENT '0草稿 1待审核 2已发布', `author_id` int(11) DEFAULT NULL COMMENT '作者ID', `publish_time` datetime DEFAULT NULL COMMENT '发布时间', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

我把状态字段设计成数字枚举,草稿、待审核、已发布三个状态,后台审核时只做一个 UPDATE 操作,比字符串状态管理起来方便得多。content 字段用 text 类型存 HTML,而不是存 Markdown,这是为了方便非技术编辑直接粘贴排版好的内容,也从侧面说明这套源码更偏向运营管理场景。

3. 核心模块拆解:一个请求从进入到返回的完整链路

3.1 登录与权限控制

后台管理的入口是登录。Shiro 在这套源码里的配置很典型——把登录接口、权限过滤器和 Session 管理器串联起来。我挑权限校验这一段来说,这是整套后台安全机制的基石:

@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); // 配置访问规则,admin 路径下的所有资源都需要登录 Map<String, String> filterChainMap = new LinkedHashMap<>(); filterChainMap.put("/admin/login", "anon"); filterChainMap.put("/admin/**", "authc"); filterChainMap.put("/static/**", "anon"); factoryBean.setFilterChainDefinitionMap(filterChainMap); // 未登录时跳转的地址 factoryBean.setLoginUrl("/admin/login"); return factoryBean; } }

filterChainMap 的 key 是路径模式,value 是过滤器名称,anon 表示匿名可访问,authc 表示需要登录认证。这里有一个容易被忽略的细节:/admin/login 必须放在 /admin/** 前面,Shiro 是按顺序匹配的,一旦先匹配到拦截规则,登录页自己也会被拦下来,形成重定向死循环。这个问题排障的时候特别常见,后面我专门列进了踩坑清单。

3.2 后台内容管理

内容管理是后台的核心业务,典型流程是:列表查询 -> 新增/编辑 -> 审核 -> 发布。以内容发布为例,我贴 Service 层的部分代码:

public boolean publishContent(Integer contentId) { // 查询当前内容状态,只有审核通过的内容才能发布 Content content = contentMapper.selectById(contentId); if (content == null) { throw new BusinessException("内容不存在"); } if (content.getStatus() != ContentStatus.AUDITED) { throw new BusinessException("仅审核通过的内容可以发布"); } // 更新状态为已发布,并记录发布时间 Content update = new Content(); update.setId(contentId); update.setStatus(ContentStatus.PUBLISHED); update.setPublishTime(new Date()); return contentMapper.updateById(update) > 0; }

这种状态机式的写法好处是可控性强。内容从草稿到发布,每一步都在代码里做了状态校验,防止跳步操作。实际开发中,我一般会把状态枚举抽到一个独立的常量类里,避免散落在各处都写魔法数字。参数 contentId 从 Controller 传入,Service 层只接受业务参数,不直接感知 HTTP 层的细节,这个习惯在改造时能省很多事。

3.3 门户页面渲染

门户前台页面的渲染走的是 Thymeleaf 模板,通过 Controller 把数据塞进 Model,再由模板引擎生成静态 HTML 返回给浏览器。我挑了栏目页的入口逻辑来展示:

@Controller @RequestMapping("/category") public class CategoryController { @GetMapping("/{id}") public String categoryPage(@PathVariable Integer id, Model model) { Category category = categoryService.findById(id); List<Content> contentList = contentService.findPublishedByCategoryId(id); model.addAttribute("category", category); model.addAttribute("contents", contentList); return "category"; } }

Model 里的 category 变量在模板中可以直接通过 ${category.name} 拿到,contentList 则用 th:each 循环渲染列表。前端开发只需要关注 templates 目录下的 HTML 模板,不用关心数据的来源,这正是服务端渲染的协作优势——前端改样式,后端改数据,中间通过 Model 对象作为协议,互不干扰。

3.4 站内搜索的实现

门户网站必备的站内搜索,这套源码实现得很务实,用的不是 Elasticsearch,而是 MySQL 的 LIKE 模糊查询。核心在 Service 层:

public List<Content> search(String keyword) { // 使用 LIKE 模糊匹配标题和正文 return contentMapper.searchByKeyword("%" + keyword + "%", "%" + keyword + "%"); }

LIKE 查询在数据量不大时性能完全够用,但一旦内容超过十万条,这种写法就会成为瓶颈。那时候可以考虑引入更专业的搜索引擎,或者改用全文索引。作为入门级的门户源码,这种朴素做法反而是优点,依赖少、容易懂,想在本地把环境跑起来几乎零成本。

4. 从零到一:把门户和后台在本地完整跑起来

4.1 环境准备与初始化

我在本地把这套源码跑起来,只用了三样东西:JDK 8、Maven 3.6 和 MySQL 5.7。数据库的初始化也很直接,用命令行导入 sql 目录里的初始化脚本:

mysql -u root -p < sql/portal_init.sql

portal_init.sql 这个脚本里包含了建库、建表、插入初始管理员账号的语句。我建议你导入完成后,先查一下 t_user 表,确认初始账号的密码是否加密过。如果密码是明文,登录时可能直接用初始密码就进去了,这是一个需要第一时间改掉的隐患——很多拿源码二次开发的人,上线后小半年都没想起改初始管理员口令,血泪教训。

4.2 配置文件修改

初始化完数据库,接下来要改 application.yml 里的数据源配置。这是整个项目跑通的关键,几乎所有的连接问题都出在这一步:

spring: datasource: url: jdbc:mysql://localhost:3306/portal_db?useUnicode=true&characterEncoding=utf8 username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver

这里要特别注意字符集参数 characterEncoding=utf8,很多人在这一步忽略了它,页面中文就变成了乱码。驱动名称也要跟 MySQL 版本匹配,MySQL 5.7 用 com.mysql.jdbc.Driver,MySQL 8.0+ 则必须换成 com.mysql.cj.jdbc.Driver,这个差异我在后面避坑章节里会详说。

4.3 启动与访问路径

一切就绪之后,在项目根目录执行 Maven 打包命令:

mvn clean package -DskipTests java -jar portal-web/target/portal-web.jar

等控制台输出 Spring Boot 的启动 Banner,看到 Tomcat started on port 8080 表示启动成功。浏览器访问本地地址可以看到门户首页,后台入口则通过 /admin/login 访问,用初始账号登录后就能在后台录入内容,回到门户前台刷新页面,内容就会出现在对应栏目下。这个过程走完,你就已经把整套源码的前台和后台都跑通了。

4.4 用 Nginx 做反向代理

本地跑通只是第一步。如果要把这套门户部署到服务器上,我一般会在前面加一层 Nginx 做反向代理,配置大致是这样:

server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /var/www/portal/static/; expires 7d; } }

静态资源单独放在一个目录,由 Nginx 直接返回,这会减轻后端压力,图片和 CSS 的加载速度也会有明显提升。动静分离在单体项目里也能做到这个程度,核心思想就是让专业的人干专业的事——Nginx 擅长处理静态文件,Java 进程专心跑业务逻辑即可。

5. 避坑指南:这套门户源码最常见的五个翻车点

5.1 启动报驱动类找不到

现象:Spring Boot 启动时提示 ClassNotFoundException: com.mysql.jdbc.Driver,应用直接退出。原因:pom.xml 里的 MySQL 依赖版本和本地数据库版本不匹配,旧驱动类在 MySQL 8.0 中已经被移除了。解决:检查依赖版本,MySQL 8.0 数据库必须使用 mysql-connector-java 8.x 版本,并且把驱动类换成 com.mysql.cj.jdbc.Driver,改完重新 clean package 再启动。

5.2 后台登录后页面重定向死循环

现象:输入正确的账号密码之后,浏览器一直跳回登录页,甚至出现"此页面无法正确重定向"的报错。原因:Shiro 的过滤器链中没有配置 /admin/login 为匿名访问,或者配置顺序不对,导致登录请求本身也被拦截,认证失败又跳回登录页,形成死循环。解决:把 anon 规则的路径放在 authc 拦截规则之前,确认 filterChainMap 中使用 LinkedHashMap 保持有序。

5.3 门户页面中文全部变成问号

现象:内容管理后台录入中文后,前台页面显示为"????",数据库里存进去的也是问号。原因:数据库连接 URL 没有指定 characterEncoding=utf8,或者数据库表的字符集不是 utf8mb4,导致中文在写入时就已经损坏。解决:在 JDBC URL 中补上 useUnicode=true&characterEncoding=utf8 参数,SQL 初始化脚本执行前把库的默认字符集设置为 utf8mb4,已经损坏的数据只能删掉重新录入。

5.4 图片上传成功但前台无法显示

现象:后台能选择图片并提示上传成功,但门户页面上图片路径 404,控制台无任何异常。原因:上传的图片保存到了本地磁盘路径,但没有映射为静态资源访问路径,浏览器请求的 URL 在后端找不到对应的文件。解决:在配置类里添加资源映射,把本地目录映射到 /upload/** 路径,同时确认 Nginx 代理没有拦截这个路径。这个坑我在两个项目里都踩过,每一次都是先怀疑路径写错,最后才发现是资源映射缺失。

5.5 修改了模板页面但看不到变化

现象:改了 templates 目录下的 HTML 文件,刷新页面还是老样子,非要重新启动项目才能生效。原因:Thymeleaf 默认开启模板缓存,生产环境下缓存了编译结果,开发时改模板不会立刻生效。解决:在开发阶段把 spring.thymeleaf.cache 设置为 false,改完模板就能实时看到效果;上线前记得改回 true,避免每次请求都重新解析模板带来的性能损耗。

6. 二次开发:如何把这个门户脚手架改成你自己的产品

拿到了源码,不改造就直接上生产是浪费。真实业务里,门户模板的风格、栏目的结构、权限的粒度都要跟着业务走。我建议的改造路径是从模板开始,再到数据模型,最后才动权限逻辑,这个顺序最不容易翻车。

如果想换掉门户首页的风格,最省力的路径是改 templates/index.html 的布局结构。Thymeleaf 模板和普通 HTML 区别不大,你用熟悉的 CSS 框架替换掉原有样式即可。我这次把门户页面从两栏布局改成三栏卡片布局,只改了模板文件和对应的 CSS,没有动任何 Java 代码,这正体现了这套源码的前后端低耦合设计——前台的改版完全可以通过模板和样式完成。

如果要新增一个独立的业务模块,比如招聘管理,我会照葫芦画瓢:先在数据库加两张表——职位表、投递记录表——再在 portal-admin 里增加 Controller、Service、Mapper,然后在后台菜单配置里把新模块的路径挂上去,最后在前台模板中增加入口。整套流程走下来,我一般只需要三个小时,这就是一套结构良好的源码带来的效率。核心经验是:先找最相似的现有模块照抄一遍,跑通之后再优化,不要在第一次就写出标新立异的架构。

权限粒度的扩展也算常见需求。原始的 Shiro 配置只区分了登录和未登录,如果要增加"编辑"和"管理员"两级身份,做法是在角色表里增加对应数据,在 Controller 的方法上加上权限注解,再在 ShiroConfig 里补充对应的过滤规则,三步就能完成一套简单的分级权限。

每一次改完代码,我会强制走一遍完整的验证流程:先 mvn clean package 确认编译无误,再把最新的 Jar 包丢到测试环境跑一遍,登录后台新建一篇内容并发布,最后去前台确认展示效果。数据链路从头到尾是通的,心里才踏实。有一次我就是跳过了中间的部署验证,直接在 IDEA 里跑了个单测就提交了,结果环境差异导致发布后报错,从那次以后我每次改完源码都强制走一遍完整验证。这套源码给我最大的启发是——一个结构清晰的全栈项目,就是最好的老师。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询