Spring Boot中JdbcUserDetailsManager无法创建表及插入数据问题排查
整合Spring Security与JdbcUserDetailsManager的Spring Boot应用启动时出现SQL语法错误:
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [insert into users (username, password, enabled) values (?,?,?)]
原以为是缺少users表,但JdbcUserDetailsManager应该自动创建该表,手动创建表后错误依旧。
配置类代码
@Configuration public class UserDetailsConfig { @Autowired private DataSource dataSource; @Bean UserDetailsManager users(DataSource dataSource) { System.out.println(); UserDetails defaultUser = User.builder() .username("admin") .password(this.passwordEncoder().encode("admin")) .roles("ADMIN", "USER") .build(); JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource); try { //users.deleteUser(defaultUser.getUsername()); users.createUser(defaultUser); } catch (Exception e) { System.out.println("database not yet initialized"); e.printStackTrace(); } return users; } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
application配置
spring.datasource.url=jdbc:postgresql://localhost:5432/food_production spring.datasource.username=postgres spring.datasource.password=123 spring.datasource.driver-class-name=org.postgresql.Driver spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true server.port=6969 spring.security.user.name=admin spring.security.user.password=admin spring.security.user.roles=admin
环境信息
- Spring版本:3.2.2
- Java版本:17
- PostgreSQL版本:16.2
可能的原因及解决方法
JdbcUserDetailsManager无自动建表逻辑
别被误导,JdbcUserDetailsManager本身不会自动创建表,它只负责用预设SQL操作表。你需要自行创建符合要求的表结构,或者通过schema.sql初始化表。手动建表结构不符合要求
若手动创建的users表字段类型、约束不对,会触发语法错误。PostgreSQL下,符合JdbcUserDetailsManager要求的表结构如下:-- users表 CREATE TABLE users ( username VARCHAR(50) PRIMARY KEY, password VARCHAR(255) NOT NULL, enabled BOOLEAN NOT NULL ); -- 权限表(必须配套创建,因为用到了roles) CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, PRIMARY KEY (username, authority), FOREIGN KEY (username) REFERENCES users(username) );Bean初始化时机冲突
你在创建UserDetailsManagerBean时直接调用createUser,此时Hibernate的ddl-auto=update可能还未执行完毕,表未创建就执行插入操作。可以把初始化用户的逻辑放到ApplicationRunner中,等应用启动完成后再执行:@Bean ApplicationRunner initUser(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) { return args -> { if (!userDetailsManager.userExists("admin")) { UserDetails defaultUser = User.builder() .username("admin") .password(passwordEncoder.encode("admin")) .roles("ADMIN", "USER") .build(); userDetailsManager.createUser(defaultUser); } }; }配置冲突
你同时配置了spring.security.user.*和自定义UserDetailsManager,会导致Spring Security自动配置与自定义配置冲突。删掉spring.security.user.name、spring.security.user.password、spring.security.user.roles这三行,仅保留自定义配置。表名/字段名大小写问题
PostgreSQL默认对标识符大小写不敏感,但如果创建表时用了双引号(如CREATE TABLE "Users"),就会区分大小写。检查你的表名是否和JdbcUserDetailsManager默认SQL一致,即小写的users和authorities。
内容的提问来源于stack exchange,提问作者Kreuterkeule

