Spring Security认证后登录页持续重载问题排查
问题描述
Spring Boot Web项目集成Spring Security后,访问允许页面、受限页面均正常,但提交登录后登录页持续重载。怀疑认证过程存在问题,查看日志未获取有效线索,failureUrl("/login?error=true")也未触发报错。代码哪里出现问题了?
pom依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity6</artifactId> </dependency>
Security配置文件
@Configuration @EnableWebSecurity public class WebSecurity { @Autowired UserDetailsService userDetailsService; @Autowired BCryptPasswordEncoder passwordEncoder; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeHttpRequests((authorize) -> authorize.requestMatchers("/register/**").permitAll() .requestMatchers("/index").permitAll() .requestMatchers("/brand","/shop").hasRole("ADMIN") ).formLogin( form -> form .loginPage("/login") // .loginProcessingUrl("/login") .defaultSuccessUrl("/index") .failureUrl("/login?error=true") .permitAll() ).logout( logout -> logout .logoutRequestMatcher(new AntPathRequestMatcher("/checkout")) .permitAll() ); return http.build(); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().requestMatchers("/image/**", "/js/**","/css/**"); } }
Users实体类
public class Users { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int user_id; private String user_name ="anonymous"; private String password; private Long phone = 12345L; private String gender = "male"; private String email; public String main_image = "1.jpeg"; private Date created_date; private String main_address; private boolean enabled; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable( name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private List<Role> roles = new ArrayList<>(); }
Role实体类
@Entity @Table(name = "role") @Data @NoArgsConstructor @AllArgsConstructor public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int role_id; private String name; @ManyToMany(mappedBy="roles") private List<Users> users; }
UserDetailServiceImpl实现类
public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UsersRepository UsersRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Users user = UsersRepository.getUserByUsername(username); if (user == null) { throw new UsernameNotFoundException("Could not find user"); } else{ return new org.springframework.security.core.userdetails.User(user.getUser_name(), user.getPassword(), mapRolesToAuthorities(user.getRoles())); } } private Collection <? extends GrantedAuthority> mapRolesToAuthorities(Collection <Role> roles) { Collection <? extends GrantedAuthority> mapRoles = roles.stream() .map(role -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList()); return mapRoles; } }
日志文件内容
Securing POST /shop2023-03-31T19:38:50.289+07:00 DEBUG 12560 --- [nio-8080-exec-5]o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymousSecurityContext 2023-03-31T19:38:50.294+07:00 DEBUG 12560 --- [nio-8080-exec-5] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/shop?continue to session2023-03-31T19:38:50.298+07:00 DEBUG 12560 --- [nio-8080-exec-5] o.s.s.web.DefaultRedirectStrategy : Redirecting tohttp://localhost:8080/login 2023-03-31T19:38:50.459+07:00 DEBUG 12560 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy : Securing GET /login 2023-03-31T19:38:50.608+07:00 DEBUG 12560 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy : Secured GET /login 2023-03-31T19:38:51.094+07:00 DEBUG 12560 --- [nio-8080-exec-6] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
问题排查与解决方案
1. 登录请求处理路径缺失
注释掉loginProcessingUrl("/login")后,自定义登录页的表单提交请求无法触发Spring Security的认证逻辑,会被当作普通页面请求,导致认证失败后重定向回登录页。恢复该配置:
.formLogin( form -> form .loginPage("/login") .loginProcessingUrl("/login") // 恢复此行 .defaultSuccessUrl("/index") .failureUrl("/login?error=true") .permitAll() )
2. 角色权限前缀不匹配
hasRole("ADMIN")会自动为角色名添加ROLE_前缀,若数据库中角色名存储为ADMIN而非ROLE_ADMIN,会导致权限验证失败。可二选一修改:
- 数据库中角色
name字段改为ROLE_ADMIN; - 在
mapRolesToAuthorities方法中添加前缀:
private Collection <? extends GrantedAuthority> mapRolesToAuthorities(Collection <Role> roles) { return roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())) .collect(Collectors.toList()); }
3. 用户启用状态未传入
Users实体的enabled字段未在构建UserDetails时传入,默认会被视为禁用状态。修改loadUserByUsername方法:
return new org.springframework.security.core.userdetails.User( user.getUser_name(), user.getPassword(), user.isEnabled(), // 添加启用状态 true, // 账户未过期 true, // 凭证未过期 true, // 账户未锁定 mapRolesToAuthorities(user.getRoles()) );
4. Users实体缺少JPA注解
Users类未添加@Entity和@Table注解,JPA无法识别为实体类,导致查询不到用户数据。补充注解:
@Entity @Table(name = "users") public class Users { // 原有代码 }
5. UserDetailsServiceImpl未被Spring管理
该实现类缺少@Component注解,Spring无法注入UsersRepository,认证时无法查询用户。添加注解:
@Component public class UserDetailsServiceImpl implements UserDetailsService { // 原有代码 }
6. BCryptPasswordEncoder Bean缺失
直接@Autowired但未定义Bean,导致密码编码器无法注入。添加Bean定义:
@Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
内容的提问来源于stack exchange,提问作者Ni No
相关产品推荐
相关产品推荐

