纲要Spring Security 内建 JDBC 认证的局限自定义数据库认证的整体思路项目依赖与工程结构编写初始化 SQL 脚本schema.sql与data.sql控制脚本加载策略spring.sql.init.modeembedded安全配置基于AuthenticationManagerBuilder自定义查询启动验证与数据库检查深入定制修改表名与字段名总结Spring Security 提供了内建的 JDBC 用户存储支持通过withDefaultSchema()可以自动创建默认的表结构users和authorities。但真实项目中表结构往往更复杂表名、字段名可能都有定制需求直接使用默认结构并不现实。Spring Security 为此提供了非常灵活的扩展点我们只需提供两条 SQL 查询框架就能完全适配任何自定义的用户‑权限表。本文将通过一个完整可运行的 Spring Boot 示例展示如何从零开始实现数据库认证的定制化。项目依赖与工程结构首先创建一个标准的 Spring Boot 项目引入spring-boot-starter-security、spring-boot-starter-web、spring-boot-starter-jdbc以及嵌入式数据库 H2。!-- pom.xml --projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.2.0/version/parentgroupIdcom.example/groupIdartifactIdcustom-jdbc-auth/artifactIdversion1.0.0/versionpropertiesjava.version17/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependencydependencygroupIdcom.h2database/groupIdartifactIdh2/artifactIdscoperuntime/scope/dependency/dependencies/project项目结构如下src └── main ├── java │ └── com │ └── example │ ├── CustomJdbcAuthApplication.java │ └── config │ └── SecurityConfig.java └── resources ├── application.properties ├── schema.sql └── data.sql编写数据库初始化脚本我们需要自定义两张表mock_users存储用户信息mock_authorities存储权限。在resources目录下放置schema.sql和data.sqlSpring Boot 会自动识别并在启动时执行需结合初始化模式配置。-- schema.sqlCREATETABLEIFNOTEXISTSmock_users(usernameVARCHAR(50)NOTNULLPRIMARYKEY,passwordVARCHAR(500)NOTNULL,enabledBOOLEANNOTNULL,nameVARCHAR(100)-- 额外扩展字段允许为空);CREATETABLEIFNOTEXISTSmock_authorities(idBIGINTAUTO_INCREMENTPRIMARYKEY,usernameVARCHAR(50)NOTNULL,authorityVARCHAR(50)NOTNULL,CONSTRAINTfk_authorities_usersFOREIGNKEY(username)REFERENCESmock_users(username));-- data.sqlINSERTINTOmock_users(username,password,enabled,name)VALUES(user,{noop}123456,true,Normal User),(admin,{noop}admin,true,Administrator);INSERTINTOmock_authorities(username,authority)VALUES(user,ROLE_USER),(admin,ROLE_ADMIN);密码前缀{noop}表示使用明文密码编码器仅用于演示生产环境务必使用BCrypt等加密方式。控制初始化脚本的加载策略在生产环境我们通常不希望每次启动都执行初始化脚本以免清空已有数据。Spring Boot 提供了spring.sql.init.mode属性来控制脚本执行时机使用embedded表示只在嵌入式数据库如 H2、Derby时执行连接外部数据库时则跳过。# application.properties spring.sql.init.modeembedded spring.datasource.urljdbc:h2:mem:testdb spring.datasource.driverClassNameorg.h2.Driver spring.datasource.usernamesa spring.datasource.password spring.h2.console.enabledtrue这样一来开发阶段使用内嵌 H2 可自动建表并插入测试数据切换到 MySQL 等外部数据库时脚本不会执行保证数据安全。安全配置基于自定义查询的 JDBC 认证核心配置类SecurityConfig中我们通过AuthenticationManagerBuilder的jdbcAuthentication()方法设置数据源及两条关键查询usersByUsernameQuery根据用户名查询用户信息必须返回username、password、enabled三列顺序及别名必须匹配。authoritiesByUsernameQuery根据用户名查询权限列表必须返回username和authority两列。即使我们使用了与默认不同的表名和字段名只要 SQL 查询的返回列别名正确框架就能完全适配。packagecom.example.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.crypto.factory.PasswordEncoderFactories;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.SecurityFilterChain;importjavax.sql.DataSource;importstaticorg.springframework.security.config.Customizer.withDefaults;ConfigurationEnableWebSecuritypublicclassSecurityConfig{BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp)throwsException{http.authorizeHttpRequests(authz-authz.requestMatchers(/admin/**).hasRole(ADMIN).anyRequest().authenticated()).httpBasic(withDefaults());returnhttp.build();}BeanpublicPasswordEncoderpasswordEncoder(){// 使用委托密码编码器支持 {noop}、{bcrypt} 等前缀returnPasswordEncoderFactories.createDelegatingPasswordEncoder();}// 通过注入 AuthenticationManagerBuilder 并调用 jdbcAuthentication 进行自定义// 更推荐的方式直接在 configure(AuthenticationManagerBuilder) 中配置// 此处采用新的风格通过注入 DataSource 并以 Bean 方式配置// 实际可根据习惯选用BeanpublicvoidconfigureGlobal(AuthenticationManagerBuilderauth,DataSourcedataSource)throwsException{auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery(SELECT username, password, enabled FROM mock_users WHERE username ?).authoritiesByUsernameQuery(SELECT username, authority FROM mock_authorities WHERE username ?).passwordEncoder(passwordEncoder());}}启动类CustomJdbcAuthApplication.java非常简单packagecom.example;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplicationpublicclassCustomJdbcAuthApplication{publicstaticvoidmain(String[]args){SpringApplication.run(CustomJdbcAuthApplication.class,args);}}启动验证启动应用后Spring Boot 会自动执行schema.sql和data.sql在 H2 内存库中创建MOCK_USERS和MOCK_AUTHORITIES表并插入数据。通过浏览器访问http://localhost:8080/h2-console使用 JDBC URLjdbc:h2:mem:testdb连接可以查看到两张表的内容。使用 curl 测试认证# 访问受保护资源使用 user/123456 认证curl-uuser:123456 http://localhost:8080/any-path若配置了/admin路径需要 ADMIN 角色使用admin:admin即可访问。深入定制修改表名与字段名上述配置中SQL 返回列已经使用了别名来匹配框架的预期名称。如果实际业务表中用户名字段为login_name密码字段为pwd状态字段为active只需调整usersByUsernameQuerySELECTlogin_nameASusername,pwdASpassword,activeASenabledFROMmy_usersWHERElogin_name?同理权限表若字段不同也可以通过别名映射。这便是 Spring Security JDBC 认证最灵活的定制方式无需重写UserDetailsService仅靠两条 SQL 即可接入任何遗留系统的用户数据。总结本文从 Spring Security 默认 JDBC 存储的局限出发完整演示了如何通过自定义schema.sql和data.sql初始化表结构结合spring.sql.init.modeembedded控制脚本执行并在安全配置中使用两条查询语句适配任意用户‑权限表。这种方式不仅适用于纯 JDBC 环境当与 MyBatis 等框架配合时也同样简便为后续深度定制如整合 JPA 实现统一风格打下了良好基础。