尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring Boot集成Spring Security、Apollo与MyBatis-Plus构建安全Web后端

Spring Boot集成Spring Security、Apollo与MyBatis-Plus构建安全Web后端 最近在整理学习笔记时发现很多同学在接触编程或新框架时常常陷入“一看就会一写就废”的困境。明明跟着教程一步步操作却总在环境配置、依赖冲突或参数理解上卡壳网上资料又过于零散不成体系。本文将以一个虚构但典型的“天才琪露诺”学习路径为引系统梳理从零构建一个可运行、可扩展的现代Web应用后端项目的完整闭环。无论你是刚入门的新手还是想巩固工程化实践的开发者都能从中获得一套可直接复用的“避坑”方案和最佳实践。1. 项目背景与核心目标“天才琪露诺今天也有好好学习”这个标题隐喻了一个典型的学习者或开发者项目从零开始、系统化掌握一项复杂技术栈的过程。它不是一个具体的产品需求而是一个学习与工程实践的综合沙箱。核心目标是构建一个具备完整前后端交互、数据持久化、用户认证与授权、以及配置中心化管理的Web应用后端。通过这个项目我们将串联起以下关键技术栈并理解它们如何协同工作Spring Boot: 作为快速构建服务的基石简化初始配置。Spring Security: 处理用户认证登录与授权权限控制保障应用安全。Apollo配置中心: 实现配置的集中管理、动态刷新与多环境隔离提升运维效率。Oracle数据库: 作为关系型数据存储练习复杂的SQL操作、事务管理与性能优化。MyBatis-Plus: 简化数据库操作同时保留手动编写复杂SQL的灵活性。为什么需要这样一个综合项目单独学习每个框架很容易但实际开发中难点往往在于集成。例如如何在Spring Security的过滤器链中整合自定义逻辑如何让Apollo的配置变更实时生效于Security的规则MyBatis-Plus的乐观锁如何与事务配合本项目正是为了打通这些技术点之间的“任督二脉”。2. 环境准备与版本说明在开始编码之前一个稳定、一致的环境是成功的基石。请务必确认你的本地开发环境满足以下要求。2.1 基础软件与版本以下版本为本文撰写时的稳定版本组合已通过兼容性测试。你可以根据实际情况微调但大版本差异可能导致依赖冲突。操作系统: Windows 10/11, macOS 10.15, 或主流的Linux发行版如Ubuntu 20.04。本文命令以Unix-like系统macOS/Linux的bash为例Windows用户可在Git Bash或WSL中运行。Java开发工具包 (JDK):OpenJDK 11或Oracle JDK 11。推荐使用OpenJDK 11。确保java -version命令输出正确。java -version # 预期输出类似openjdk version 11.0.15 2022-04-19构建工具:Apache Maven 3.6。用于管理项目依赖、构建和打包。mvn -v # 预期输出包含Apache Maven 3.8.6集成开发环境 (IDE):IntelliJ IDEA Ultimate/Community Edition或Eclipse with STS插件。IDEA对Spring生态支持更佳推荐使用。数据库:Oracle Database 19c Express Edition (XE)或更高版本。你也可以使用Docker快速启动一个Oracle实例。确保你有数据库的连接信息主机、端口、服务名/SID、用户名、密码。配置中心:Apollo阿波罗。我们将使用其官方提供的Quick Start版本进行本地演示它集成了Portal、ConfigService和AdminService。请从 Apollo GitHub Release页面 下载最新版本的apollo-quick-start.zip。2.2 项目初始化与结构我们使用Spring Initializr来快速生成项目骨架。访问 start.spring.io 或直接在IDEA中创建新项目选择Spring Initializr。生成参数如下Project: Maven ProjectLanguage: JavaSpring Boot: 2.7.x (例如 2.7.18 这是一个长期支持版本)Project Metadata:Group:com.cirno.learn(可自定义)Artifact:genius-cirnoName:genius-cirnoPackage name:com.cirno.learnPackaging: JarJava: 11Dependencies: 暂时只选择Spring Web。其他依赖我们后续手动添加以便更清晰地理解每个依赖的作用。点击“Generate”下载项目压缩包解压后用IDE打开。初始项目结构预览genius-cirno/ ├── pom.xml # Maven项目对象模型管理依赖 ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/cirno/learn/ │ │ │ └── GeniusCirnoApplication.java # Spring Boot主启动类 │ │ └── resources/ │ │ ├── application.properties # 主配置文件 │ │ └── static/ # 静态资源 │ └── test/ # 测试代码目录 └── target/ # 编译输出目录3. 核心依赖引入与配置管理一个清晰、可控的依赖管理是项目健康的标志。我们将分步骤引入核心依赖并立即配置Apollo以实现配置的集中化管理。3.1 添加项目依赖 (pom.xml)打开pom.xml文件在dependencies节点内添加以下依赖。我们按功能模块分组添加并附上简要说明。!-- Spring Boot Starter Web (Initializr已添加) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Boot Starter Test (Initializr已添加) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency !-- 1. Spring Security 依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency !-- 2. Apollo Client 依赖 -- dependency groupIdcom.ctrip.framework.apollo/groupId artifactIdapollo-client/artifactId version2.1.0/version !-- 请检查并使用最新稳定版 -- /dependency !-- 3. MyBatis-Plus 及数据库驱动 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- Oracle JDBC驱动 (需从Oracle官网下载或使用Maven仓库) -- !-- 注意Oracle驱动不在中央仓库需手动安装到本地仓库或使用合法仓库 -- dependency groupIdcom.oracle.database.jdbc/groupId artifactIdojdbc8/artifactId version21.9.0.0/version scoperuntime/scope /dependency !-- 或者使用更易获取的MySQL驱动作为演示替代 -- !-- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency -- !-- 4. 其他工具类依赖 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency添加完成后在IDE中点击Maven刷新按钮下载所有依赖。3.2 配置Apollo客户端Apollo的核心思想是将配置从应用代码中分离。我们需要告诉Spring Boot应用从哪里读取配置。步骤1启动Apollo Quick Start解压下载的apollo-quick-start.zip。根据官方文档执行启动脚本如demo.sh或demo.bat。等待所有服务启动完成访问http://localhost:8070进入Apollo Portal默认账号: apollo, 密码: admin。步骤2在Apollo中创建项目并添加配置登录Portal后点击“创建项目”。输入项目信息部门选择默认部门。AppId:genius-cirno(必须与后续应用配置一致)。应用名天才琪露诺学习项目。应用负责人填写你的信息。在新建的项目中为默认的application命名空间添加一些测试配置例如server.port 8088 cirno.learning.topic Spring Security Apollo Integration security.jwt.secret mySuperSecretKeyForDemoOnly123!点击“发布”。步骤3在Spring Boot应用中启用Apollo修改src/main/resources/application.properties文件将其重命名为application.properties或application.yml但内容只保留最核心的Apollo引导配置其他配置移至Apollo管理。# application.properties (本地引导文件) # 1. 指定应用的AppId必须与Portal中创建的一致 app.idgenius-cirno # 2. 指定Apollo Meta Server的地址 (QuickStart版本地址) apollo.metahttp://localhost:8080 # 3. 启用Apollo配置并指定在应用启动阶段就加载 apollo.bootstrap.enabledtrue apollo.bootstrap.eagerLoad.enabledtrue # 指定要加载的命名空间多个用逗号分隔 apollo.bootstrap.namespacesapplication # 4. (可选) 本地开发环境覆盖如果连接不上Apollo则使用本地配置 apollo.cacheDir./apollo-config apollo.allowOverridetrue apollo.overrideSystemPropertiestrue步骤4验证配置读取在主启动类GeniusCirnoApplication.java中添加一个测试接口。package com.cirno.learn; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; SpringBootApplication RestController public class GeniusCirnoApplication { // 使用Value注解从配置中心注入值 Value(${cirno.learning.topic:Default Topic}) private String learningTopic; Value(${server.port:8080}) private String serverPort; public static void main(String[] args) { SpringApplication.run(GeniusCirnoApplication.class, args); } GetMapping(/config) public String showConfig() { return String.format(当前学习主题: %s, 服务运行在端口: %s, learningTopic, serverPort); } }启动应用访问http://localhost:8088/config端口已在Apollo中改为8088你应该能看到从Apollo读取的配置信息。尝试在Apollo Portal中修改cirno.learning.topic并发布在不重启应用的情况下刷新浏览器观察配置是否动态更新需要确保应用开启了RefreshScope或配置类本身支持刷新简单字符串在Spring Boot 2.7中通常支持。4. 集成Spring Security实现认证与授权现在我们为应用加上安全层。Spring Security功能强大但配置复杂我们由简入繁。4.1 基础安全配置与密码编码首先我们会遇到Spring Security的默认行为启动应用后访问任何接口都会跳转到一个默认的HTTP Basic认证登录页面。我们需要自定义这个行为。创建一个安全配置类SecurityConfigpackage com.cirno.learn.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; Configuration EnableWebSecurity public class SecurityConfig { /** * 密码编码器。 * BCrypt是当前推荐的安全哈希算法每次加密结果都不同且自动加盐。 * 必须将其声明为Bean供UserDetailsService使用。 */ Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 核心安全过滤器链配置。 * 这是Spring Security 5.7推荐的Lambda DSL配置风格更清晰。 */ Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth - auth // 定义请求授权规则 .antMatchers(/, /config, /login, /error).permitAll() // 允许所有人访问 .antMatchers(/admin/**).hasRole(ADMIN) // /admin/ 下的路径需要ADMIN角色 .antMatchers(/user/**).hasAnyRole(USER, ADMIN) // /user/ 下的路径需要USER或ADMIN角色 .anyRequest().authenticated() // 其他所有请求都需要认证 ) .formLogin(form - form .loginPage(/login) // 自定义登录页面路径 .loginProcessingUrl(/api/auth/login) // 处理登录表单提交的API路径 .defaultSuccessUrl(/home, true) // 登录成功后跳转的页面 .permitAll() ) .logout(logout - logout .logoutUrl(/api/auth/logout) // 处理注销的API路径 .logoutSuccessUrl(/login?logout) // 注销成功后跳转的页面 .permitAll() ) .csrf(csrf - csrf.disable()) // 暂时禁用CSRF以方便API测试生产环境必须谨慎处理 .httpBasic(withDefaults()); // 保留HTTP Basic认证可用于简单的API测试 return http.build(); } }4.2 实现用户详情服务与数据源安全规则定义了“谁可以访问什么”接下来需要定义“用户是谁”。我们实现一个从数据库加载用户信息的服务。1. 创建用户实体与表首先在Oracle中创建用户表。CREATE TABLE sys_user ( id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, username VARCHAR2(50) NOT NULL UNIQUE, password VARCHAR2(100) NOT NULL, -- 存储BCrypt加密后的密码 nickname VARCHAR2(50), email VARCHAR2(100), status NUMBER(1) DEFAULT 1 NOT NULL, -- 状态1-启用0-禁用 create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE sys_role ( id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, role_code VARCHAR2(20) NOT NULL UNIQUE, -- 如ROLE_ADMIN, ROLE_USER role_name VARCHAR2(50) NOT NULL ); CREATE TABLE sys_user_role ( user_id NUMBER NOT NULL, role_id NUMBER NOT NULL, PRIMARY KEY (user_id, role_id), FOREIGN KEY (user_id) REFERENCES sys_user(id), FOREIGN KEY (role_id) REFERENCES sys_role(id) ); -- 插入初始数据 INSERT INTO sys_role (role_code, role_name) VALUES (ROLE_ADMIN, 管理员); INSERT INTO sys_role (role_code, role_name) VALUES (ROLE_USER, 普通用户); -- 密码是明文admin123经过BCrypt加密后的结果仅示例你的加密结果会不同 INSERT INTO sys_user (username, password, nickname, status) VALUES (admin, $2a$10$YourBcryptHashHere..., 系统管理员, 1); INSERT INTO sys_user (username, password, nickname, status) VALUES (cirno, $2a$10$AnotherBcryptHash..., 琪露诺, 1); -- 关联用户角色 INSERT INTO sys_user_role (user_id, role_id) VALUES ((SELECT id FROM sys_user WHERE usernameadmin), (SELECT id FROM sys_role WHERE role_codeROLE_ADMIN)); INSERT INTO sys_user_role (user_id, role_id) VALUES ((SELECT id FROM sys_user WHERE usernamecirno), (SELECT id FROM sys_role WHERE role_codeROLE_USER));注意你需要使用BCryptPasswordEncoder生成真实的加密密码替换上面的YourBcryptHashHere...。可以在一个测试类或main方法中运行encoder.encode(admin123)来获取。2. 创建MyBatis-Plus实体类和Mapper// User.java package com.cirno.learn.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; import java.util.List; Data TableName(sys_user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; private String nickname; private String email; private Integer status; private LocalDateTime createTime; // 非数据库字段用于存储用户的角色信息 TableField(exist false) private ListRole roles; } // Role.java package com.cirno.learn.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; Data TableName(sys_role) public class Role { TableId(type IdType.AUTO) private Long id; private String roleCode; // 对应 ROLE_ADMIN private String roleName; } // UserRole.java (关联实体) package com.cirno.learn.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; Data TableName(sys_user_role) public class UserRole { private Long userId; private Long roleId; }// UserMapper.java package com.cirno.learn.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.cirno.learn.entity.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; public interface UserMapper extends BaseMapperUser { // 自定义查询根据用户名查询用户及其角色 Select(SELECT u.* FROM sys_user u WHERE u.username #{username}) User selectByUsername(Param(username) String username); // 自定义查询查询用户的所有角色 Select(SELECT r.* FROM sys_role r INNER JOIN sys_user_role ur ON r.id ur.role_id WHERE ur.user_id #{userId}) ListRole selectRolesByUserId(Param(userId) Long userId); }注意需要在主启动类上添加MapperScan(com.cirno.learn.mapper)注解。3. 实现UserDetailsService这是Spring Security的核心接口用于根据用户名加载用户信息。package com.cirno.learn.service; import com.cirno.learn.entity.Role; import com.cirno.learn.entity.User; import com.cirno.learn.mapper.UserMapper; import lombok.RequiredArgsConstructor; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; Service RequiredArgsConstructor // Lombok注解为final字段生成构造函数 public class CustomUserDetailsService implements UserDetailsService { private final UserMapper userMapper; Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1. 查询用户基本信息 User user userMapper.selectByUsername(username); if (user null) { throw new UsernameNotFoundException(用户不存在: username); } // 2. 查询用户角色 ListRole roles userMapper.selectRolesByUserId(user.getId()); user.setRoles(roles); // 3. 将角色转换为Spring Security需要的GrantedAuthority格式 ListSimpleGrantedAuthority authorities roles.stream() .map(role - new SimpleGrantedAuthority(role.getRoleCode())) // ROLE_ADMIN, ROLE_USER .collect(Collectors.toList()); // 4. 构建并返回Spring Security的UserDetails对象 // 注意这里返回的是org.springframework.security.core.userdetails.User // 它包含了用户名、密码加密后的、权限、账户状态等信息。 return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), // 数据库存储的已经是BCrypt密文 user.getStatus() 1, // enabled 账户是否启用 true, // accountNonExpired 账户是否未过期 true, // credentialsNonExpired 凭证是否未过期 true, // accountNonLocked 账户是否未锁定 authorities ); } }4.3 配置数据库连接与MyBatis-Plus在Apollo配置中心application命名空间添加数据库配置实现配置与代码分离。在Apollo Portal中添加配置# 数据库连接配置 spring.datasource.driver-class-nameoracle.jdbc.OracleDriver spring.datasource.urljdbc:oracle:thin:localhost:1521:XE spring.datasource.usernameyour_username spring.datasource.passwordyour_password # MyBatis-Plus 配置 mybatis-plus.configuration.map-underscore-to-camel-casetrue mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 mybatis-plus.global-config.db-config.id-typeauto在项目中配置数据源和MyBatis-Plus扫描路径由于Apollo已经管理了spring.datasource我们只需确保MyBatis-Plus被正确扫描。在主启动类或配置类上添加注解// GeniusCirnoApplication.java SpringBootApplication MapperScan(com.cirno.learn.mapper) // 添加这行 RestController public class GeniusCirnoApplication { // ... 原有代码 }5. 构建业务模块与API接口安全基础搭建好后我们来创建一些受保护的业务接口。5.1 创建用户信息控制器package com.cirno.learn.controller; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; RestController RequestMapping(/api/user) public class UserController { GetMapping(/info) public MapString, Object getCurrentUserInfo() { // 从SecurityContext中获取当前认证的用户信息 Authentication authentication SecurityContextHolder.getContext().getAuthentication(); String username authentication.getName(); Object authorities authentication.getAuthorities(); MapString, Object info new HashMap(); info.put(username, username); info.put(authorities, authorities); info.put(message, 你好 username 这是你的个人信息。); return info; } GetMapping(/admin/secret) public String adminSecret() { return 这是一个只有ADMIN角色才能访问的秘密信息。; } GetMapping(/user/dashboard) public String userDashboard() { return 欢迎来到用户仪表板。; } }5.2 创建简单的登录页面为了测试表单登录我们在src/main/resources/static/下创建一个简单的login.html生产环境应使用模板引擎如Thymeleaf。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title登录 - 天才琪露诺的学习项目/title /head body h2用户登录/h2 form action/api/auth/login methodpost div label forusername用户名:/label input typetext idusername nameusername required /div div label forpassword密码:/label input typepassword idpassword namepassword required /div div button typesubmit登录/button /div input typehidden name_csrf th:value${_csrf.token} th:if${_csrf} !-- 启用CSRF后需要 -- /form p测试账号: admin/admin123, cirno/cirno123/p /body /html6. 运行、测试与验证现在让我们启动整个项目并进行端到端的测试。启动Apollo Quick Start如果尚未启动。启动Oracle数据库。启动Spring Boot应用。在IDE中运行GeniusCirnoApplication的main方法。观察控制台日志确认Apollo客户端成功连接并拉取配置。数据源连接成功。Spring Security过滤器链初始化完成。测试公开接口访问http://localhost:8088/config应能看到从Apollo读取的配置信息且无需登录。测试登录与权限访问http://localhost:8088/login使用admin/admin123登录。登录成功后应跳转到/home我们未创建此页面可能会404但登录状态已建立。访问http://localhost:8088/api/user/info应返回当前用户admin的信息及其ROLE_ADMIN权限。访问http://localhost:8088/api/user/admin/secret应能成功访问。访问http://localhost:8088/api/user/user/dashboard也应能成功访问因为admin也有USER角色。退出登录访问/api/auth/logout。使用cirno/cirno123登录。再次访问/api/user/admin/secret应返回403 Forbidden错误因为用户cirno只有ROLE_USER角色。访问/api/user/user/dashboard应能成功访问。测试配置动态刷新在Apollo Portal中将cirno.learning.topic的值修改为“动态配置刷新测试”。发布配置。稍等片刻默认5秒再次访问/config接口观察返回的学习主题是否已更新。7. 常见问题与排查思路在集成过程中你可能会遇到以下典型问题。这里提供排查思路。问题现象可能原因排查步骤与解决方案应用启动失败报BeanCreationException1. 依赖冲突。2. 配置错误如数据库连接失败。3. Bean循环依赖。1. 检查pom.xml依赖树 (mvn dependency:tree)排除冲突版本。2. 检查Apollo或本地配置中的数据库连接字符串、用户名密码是否正确。3. 查看完整堆栈信息定位具体是哪个Bean创建失败。访问接口返回Whitelabel Error Page或4041. 请求路径错误。2. 控制器未正确映射。3. 静态资源未放行。1. 检查浏览器地址栏URL与RequestMapping定义是否一致。2. 检查控制器类是否被Spring扫描到是否在启动类同级或子包下。3. 在Security配置中确认对应路径是否已被.permitAll()。登录失败无错误提示或提示Bad credentials1. 用户名/密码错误。2.UserDetailsService加载用户失败。3. 密码编码器不匹配。1. 确认数据库中的用户名和加密密码是否正确。2. 在CustomUserDetailsService的loadUserByUsername方法中打日志或调试看是否查询到用户。3.最关键确保数据库存储的密码是同一个BCryptPasswordEncoder加密的结果。登录时Security会用配置的PasswordEncoder对输入的密码进行编码然后与数据库存储的密文比对。具有权限却返回403 Forbidden1. 角色前缀问题。2. Security配置规则顺序错误。3. 请求方法不匹配。1. Spring Security默认要求角色权限字符串以ROLE_开头。检查数据库role_code字段和GrantedAuthority构造是否正确。2. Security配置中更具体的规则应放在前面。检查antMatchers顺序。3. 检查控制器方法是GetMapping还是PostMapping与请求是否匹配。Apollo配置不刷新1. 客户端未监听命名空间。2. 配置类未使用RefreshScope。3. 客户端长轮询失败。1. 检查apollo.bootstrap.namespaces配置是否包含要刷新的命名空间。2. 对于需要刷写的Bean如ConfigurationProperties绑定的类加上RefreshScope注解。简单Value注入的字符串通常能自动刷新。3. 查看应用日志看是否有Apollo长连接相关的错误。检查网络和Apollo服务状态。MyBatis-Plus查询报错或返回null1. 实体类字段与数据库列名映射失败。2. Mapper接口未被扫描。3. SQL语法错误。1. 确认mybatis-plus.configuration.map-underscore-to-camel-casetrue已配置且实体类属性是驼峰数据库列是下划线。2. 确认启动类上有MapperScan且路径正确。3. 打开SQL日志 (mybatis-plus.configuration.log-impl)查看实际执行的SQL语句是否正确。8. 最佳实践与工程建议将技术跑通只是第一步写出健壮、可维护的代码才是目标。8.1 安全层面密码加密永远不要在数据库或日志中存储明文密码。坚持使用BCryptPasswordEncoder。CSRF防护在SecurityConfig中我们为了方便测试禁用了CSRF。在生产环境中必须为所有状态修改请求POST, PUT, DELETE, PATCH启用CSRF防护。对于前后端分离项目如VueSpring Boot可以考虑将Token放在请求头中。会话管理根据业务需求合理配置会话超时、并发控制防止同一账号多处登录、以及安全的Cookie设置HttpOnly, Secure。权限细化除了角色ROLE_*Spring Security还支持更细粒度的权限表达式如hasAuthority(USER:READ)适合复杂的权限模型。敏感信息像JWT密钥、数据库密码等敏感信息不应硬编码在Apollo或任何配置文件中。应使用环境变量、启动参数或专业的密钥管理服务如Vault注入Apollo可以引用这些外部变量。8.2 配置管理环境隔离在Apollo中为dev开发、test测试、prod生产环境创建不同的集群和命名空间。通过apollo.meta指定不同环境的Meta Server地址。配置分类不要把所有配置都堆在application命名空间。可以按功能创建私有命名空间如spring-security,datasource或按组件创建公共命名空间供多个应用使用。灰度发布利用Apollo的灰度发布功能先将新配置推送给一小部分应用实例验证无误后再全量发布。配置回滚任何发布操作前先记录当前的配置快照。一旦新配置有问题可以快速回滚到上一个稳定版本。8.3 数据持久化事务管理在Service层方法上使用Transactional注解确保数据库操作的原子性。特别注意MyBatis-Plus的Service方法默认已封装在事务中。SQL注入防护MyBatis-Plus的Wrapper条件构造器或Param注解传递的参数默认使用了预编译语句能有效防止SQL注入。绝对避免在MyBatis的XML或注解中使用字符串拼接 (${}) 来构建SQL。连接池监控使用Druid或HikariCP等高性能连接池并配置合理的初始大小、最大连接数、超时时间同时开启监控端点生产环境注意安全。ORM使用规范实体类字段与表结构保持映射清晰。复杂查询多表关联、分组统计优先写在XML文件中或使用Select注解保持可读性。简单CRUD使用MyBatis-Plus的Lambda QueryWrapper兼顾类型安全和便捷。8.4 代码结构与可维护性分层清晰坚持Controller - Service - Mapper的分层架构。Controller负责参数校验和HTTP响应Service负责业务逻辑和事务控制Mapper只负责数据访问。统一响应封装设计一个统一的REST API响应体如包含code, message, data字段便于前端处理。全局异常处理使用ControllerAdvice和ExceptionHandler捕获并处理各类异常业务异常、校验异常、安全异常等返回友好的错误信息而不是暴露堆栈信息。日志规范使用SLF4J接口配合Logback/Log4j2实现。对不同级别ERROR, WARN, INFO, DEBUG的日志进行合理输出。关键业务节点、异常处必须打日志且日志信息要包含可追踪的请求ID或用户标识。接口文档集成Swagger或Spring Doc OpenAPI自动生成并维护API文档减少前后端沟通成本。通过这样一个从零到一的综合项目实践我们不仅学会了如何单独使用Spring Security、Apollo、MyBatis-Plus更重要的是掌握了如何将它们有机地组合在一起构建一个配置灵活、安全可靠、易于维护的后端服务。这正是一个“天才”开发者从“学习”到“实战”的必经之路。接下来你可以尝试在此基础上增加更多功能如JWT令牌认证、API限流、操作审计日志、集成测试等不断丰富你的技术武器库。
返回列表