无法使用存储在数据库中的凭据登录

Can't login using credentials stored in the database

我创建了一个非常简单的网站,每个人都可以访问/about,经过身份验证的用户可以访问/profile,只有具有“ADMIN”角色的用户才能访问。我在 DBInit.java 中添加了一个“ADMIN”用户,然后我尝试访问 /admin 并获得一个 http 基本登录表单。我输入 adminEmail 作为登录名,输入 admin123 作为密码,但我无法访问 /admin 页面。所以我的代码中某处有错误,我可以看到它。那么错误在哪里以及如何摆脱它呢? 如果我在内存验证中使用一切正常。

// In memory authentication
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
//    auth.authenticationProvider(authenticationProvider());
    
    auth.inMemoryAuthentication()
        .withUser("admin")
        .password(passwordEncoder().encode("admin"))
        .roles("ADMIN");
}

SecurityConfiguration.java

import Onlinestore.service.UserPrincipalDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
    private UserPrincipalDetailsService userPrincipalDetailsService;
    
    public SecurityConfiguration(UserPrincipalDetailsService userPrincipalDetailsService)
    {
        this.userPrincipalDetailsService = userPrincipalDetailsService;
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
    {
        auth.authenticationProvider(authenticationProvider());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http
                .authorizeRequests()
                .antMatchers("/about").permitAll()
                .antMatchers("/profile").authenticated()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .and()
                .httpBasic();
    }
    
    @Bean
    DaoAuthenticationProvider authenticationProvider()
    {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(userPrincipalDetailsService);
        
        return daoAuthenticationProvider;
    }
    
    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
}

User.java

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;

@Entity(name = "user")
@Table(name = "users")
@NoArgsConstructor
public class User
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Getter
    @Setter
    private int id;
    
    @Getter
    @Setter
    @Column(nullable = false)
    private String name;
    
    @Getter
    @Setter
    private String surname;
    
    @Getter
    @Setter
    @Column(nullable = false)
    private String password;
    
    @Getter
    @Setter
    @Column(name = "telephone_number", nullable = false)
    private String telephoneNumber;
    
    @Getter
    @Setter
    @Column(unique = true)
    private String email;
    
    @Getter
    @Setter
    private String country;
    
    @Getter
    @Setter
    private String address;
    
    @Getter
    @Setter
    // delimiter = ";"
    private String roleNames;
    
    public User(String name, String surname, String password, String telephoneNumber, String email, String country, String address, String roleNames)
    {
        this.name = name;
        this.surname = surname;
        this.password = password;
        this.telephoneNumber = telephoneNumber;
        this.email = email;
        this.country = country;
        this.address = address;
        this.roleNames = roleNames;
    }
}

UserPrincipal.java

import Onlinestore.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

public class UserPrincipal implements UserDetails
{
    private User user;
    
    public UserPrincipal(User user)
    {
        this.user = user;
    }
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
        String[] roles = user.getRoleNames().split(";");
        
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : roles)
        {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        
        return authorities;
    }
    
    @Override
    public String getPassword()
    {
        return user.getPassword();
    }
    
    @Override
    public String getUsername()
    {
        return user.getName();
    }
    
    @Override
    public boolean isAccountNonExpired()
    {
        return false;
    }
    
    @Override
    public boolean isAccountNonLocked()
    {
        return false;
    }
    
    @Override
    public boolean isCredentialsNonExpired()
    {
        return false;
    }
    
    @Override
    public boolean isEnabled()
    {
        return false;
    }
}

UserPrincipalDetailsService.java

import Onlinestore.entity.User;
import Onlinestore.repository.UserRepository;
import Onlinestore.security.UserPrincipal;
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;

@Service
public class UserPrincipalDetailsService implements UserDetailsService
{
    private UserRepository userRepository;
    
    public UserPrincipalDetailsService(UserRepository userRepository)
    {
        this.userRepository = userRepository;
    }
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        User user = userRepository.findUserByEmail(username);
        return new UserPrincipal(user);
    }
}

DBInit.java

import Onlinestore.entity.User;
import Onlinestore.repository.UserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;

@Service
public class DBInit implements CommandLineRunner
{
    private UserRepository userRepository;
    private PasswordEncoder passwordEncoder;
    
    public DBInit(UserRepository userRepository, PasswordEncoder passwordEncoder)
    {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }
    
    @Override
    public void run(String[] args)
    {
        User user1 = new User("admin", "admin", passwordEncoder.encode("admin123"),
                "+111111111", "adminEmail", "country1", "address1", "ADMIN");

        List<User> users = Arrays.asList(user1);

        userRepository.saveAll(users);
    }
}

用户存储库

import Onlinestore.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Integer>
{
    User findUserByEmail(String email);
}

内存中身份验证需要配置中的硬编码凭据。它不会从数据库中提取凭据。

如果您想使用数据库中的凭据,您的其余设置乍一看看起来不错。尝试

httpSecurity
  .authorizeRequests()
    .antMatchers(PUBLIC_MATCHERS)
      .permitAll()
    .anyRequest()
      .authenticated();

其中 PUBLIC_MATCHERS 是一组不需要身份验证的端点,如果您有这样的东西。

我发现了所有错误。

  1. 在 class UserPrincipal 中,我不得不覆盖此方法和 return true,但不是 return false。我想当我 return false Spring 安全认为用户被阻止、过期、禁用等并阻止身份验证时。 这但也有一些错误和应用程序尚未按我想要的方式工作。 在 UserPrincipal
  2. 中正确实施方法
@Override
public boolean isAccountNonExpired()
{
    return true;
}
    
@Override
public boolean isAccountNonLocked()
{
    return true;
}
    
@Override
public boolean isCredentialsNonExpired()
{
    return true;
}
    
@Override
public boolean isEnabled()
{
    return true;
}
  1. 在 class UserPrincipal 中,我必须在 getUserName()return user.getEmail(),但不能 return user.getName(),因为我使用电子邮件作为身份验证用户名。
@Override
public String getUsername()
{
    return user.getEmail();
}
  1. UserPrincipalDetailsService class 中 loadUserByUsername() 我不得不抛出异常 UsernameNotFoundException 当没有用户使用这样的用户名时
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
    User user = userRepository.findUserByEmail(username);
        
    if (user == null)
    {
        throw new UsernameNotFoundException("user not found");
    }
        
    return new UserPrincipal(user);
}