You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security数据库认证失败求助(authenticate未知源错误)

Spring Security数据库认证失败问题

我跟着Teddy Smith的YouTube教程搭建Spring Security认证系统,已经完成注册功能和PasswordEncoder配置,但用数据库里的用户无法完成认证,触发authenticate(Unknown Source)错误,卡着没法继续学习。以下是相关代码和控制台报错:

UserEntity类

package springsecuritylearn.entity;

import java.util.ArrayList;
import java.util.List;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;

@Entity
@Table(name = "users")
public class UserEntity {

@Id
@SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
private long id;

private String username;

private String password;

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "user_roles", 
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<RoleEntity> roles = new ArrayList<>();

public UserEntity() {}

public UserEntity(String username, String password, List<RoleEntity> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public List<RoleEntity> getRoles() {
return roles;
}

public void setRoles(List<RoleEntity> roles) {
this.roles = roles;
}

}

RoleEntity类

package springsecuritylearn.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;

@Entity
@Table(name = "roles")
public class RoleEntity {

@Id
@SequenceGenerator(name = "role_sequence", sequenceName = "role_sequence", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "role_sequence")
private long id;

private String name;

public RoleEntity() {}

public RoleEntity(String name) {
this.name = name;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

AuthController类

package springsecuritylearn.controller;

import java.util.Collections;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import springsecuritylearn.entity.RoleEntity;
import springsecuritylearn.entity.UserEntity;
import springsecuritylearn.repository.RoleRepository;
import springsecuritylearn.repository.UserRepository;

@Controller
@RequestMapping("/")
public class AuthController {

private AuthenticationManager authenticationManager;
private PasswordEncoder passwordEncoder;
private UserRepository userRepository;
private RoleRepository roleRepository;

@Autowired
public AuthController(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder,
UserRepository userRepository, RoleRepository roleRepository) {
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}

@GetMapping("/login")
public String login() {
return "login";
}

@PostMapping("/login")
public String userAuthenticate(
UserEntity userEntity,
@RequestParam String username,
@RequestParam String password,
Map<String, Object> model) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
SecurityContextHolder.getContext().setAuthentication(authentication);
return "redirect:/home";
}

@GetMapping("/register")
public String register() {
return "register";
}

@PostMapping("/register")
private String addNewUser(
@RequestParam String username,
@RequestParam String password,
Map<String, Object> model) {
if (userRepository.existsByUsername(username)) {
return "register";
}
    
    
RoleEntity roles = roleRepository.findByName("USER").orElse(null);
UserEntity user = new UserEntity(username, passwordEncoder.encode(password), Collections.singletonList(roles));
    
    
userRepository.save(user);
    
return "redirect:/login";
}

}

WebController类

package springsecuritylearn.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(path = "/")
public class WebController {

@GetMapping(path = "/home")
public String home() {
return "home";
}

@GetMapping(path = "/user")
public String user() {
return "user";
}

@GetMapping(path = "/admin")
public String admin() {
return "admin";
}

}

UserRepository接口

package springsecuritylearn.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import springsecuritylearn.entity.UserEntity;

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {

Optional<UserEntity> findByUsername(String username);
Boolean existsByUsername(String username);

}

RoleRepository接口

package springsecuritylearn.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import springsecuritylearn.entity.RoleEntity;

@Repository
public interface RoleRepository extends JpaRepository<RoleEntity, Long> {

Optional<RoleEntity> findByName(String name);

}

CustomUserDetailsService类

package springsecuritylearn.security;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 springsecuritylearn.entity.RoleEntity;
import springsecuritylearn.entity.UserEntity;
import springsecuritylearn.repository.UserRepository;

@Service
public class CustomUserDetailsService implements UserDetailsService {

private UserRepository userRepository;

@Autowired
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("Username not found"));
return new User(user.getUsername(), user.getPassword(), mapRolesToAuthorities(user.getRoles()));
}

private Collection<GrantedAuthority> mapRolesToAuthorities(List<RoleEntity> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
}

}

SecurityConfig类

package springsecuritylearn.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
@Configuration
public class SecurityConfig {

private CustomUserDetailsService userDetailsService;

@Autowired
public SecurityConfig(CustomUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/", "/register").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/home")
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.httpBasic()
.disable();
return http.build();
}

@Bean
public UserDetailsService users() {
UserDetails user1 = User.builder()
.username("august")
.password("{noop}august")
.roles("ADMIN")
.build();
UserDetails user2 = User.builder()
.username("maksim")
.password("{noop}maksim")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user1, user2);
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

}

控制台报错信息

at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.7.jar:6.0.7]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:211) ~[spring-aop-6.0.7.jar:6.0.7]
at jdk.proxy7/jdk.proxy7.$Proxy133.authenticate(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:578) ~[na:na]

问题解决步骤

1. 移除内存用户配置

SecurityConfig里同时定义了InMemoryUserDetailsManager和CustomUserDetailsService,Spring会优先使用内存用户源,导致认证时不走数据库。直接删掉users()这个Bean:

// 删掉这段代码
@Bean
public UserDetailsService users() {
UserDetails user1 = User.builder()
.username("august")
.password("{noop}august")
.roles("ADMIN")
.build();
UserDetails user2 = User.builder()
.username("maksim")
.password("{noop}maksim")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user1, user2);
}

2. 配置AuthenticationManager使用自定义UserDetailsService

确保AuthenticationManager知道要使用你的CustomUserDetailsService,在SecurityConfig里添加认证提供者配置:

@Bean
public AuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
    authProvider.setUserDetailsService(userDetailsService);
    authProvider.setPasswordEncoder(passwordEncoder());
    return authProvider;
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
    return authConfig.getAuthenticationManager();
}

然后在SecurityFilterChain里注册这个提供者:

http
    .csrf().disable()
    .authorizeHttpRequests()
    // ... 其他原有配置
    .and()
    .authenticationProvider(authenticationProvider()) // 添加此行
    .formLogin()
    // ... 其他原有配置

3. 角色权限匹配问题

hasRole("ADMIN")会自动给角色名加上ROLE_前缀,但你的CustomUserDetailsService里直接把数据库角色名转成SimpleGrantedAuthority(比如数据库存"USER",权限就是"USER"),这时候hasRole("USER")会找"ROLE_USER",导致权限不匹配。解决方法二选一:

  • 数据库里角色名存成ROLE_USER、ROLE_ADMIN,和hasRole的前缀规则匹配;
  • 把hasRole改成hasAuthority,比如requestMatchers("/admin").hasAuthority("ADMIN"),直接匹配数据库里的角色名。

4. 移除自定义的登录POST处理

Spring Security的formLogin已经自动处理了登录请求,你自己写的@PostMapping("/login")会和框架的处理冲突,直接删掉这个方法,让框架自动处理认证流程,包括失败后的跳转和错误提示。


内容的提问来源于stack exchange,提问作者Maksim1251

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 10:01:59