无法使用Spring Boot Security登录

3

我有一个使用MySQL的Spring Boot应用程序。我可以将用户保存在数据库中,但是当我尝试登录时,它一直显示用户名或密码不正确。即使我硬编码了一个用户,它仍然不起作用。

我使用JavaBrains教程和他们在GitHub上的代码创建了设置。

这是我正在使用的代码。

安全配置:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorize -> authorize
                        .antMatchers("/static/**", "/index").permitAll()
                        .antMatchers("/premium/**").hasAuthority("PREMIUM")
                        .antMatchers("/user/**").hasAuthority("USER")
                )
                .formLogin(formLogin -> formLogin
                        .loginPage("/login")
                        .failureUrl("/login-error")
                );
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

我的UserDetailsService

public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<User> user = userRepository.findByUsername(username);
        user.orElseThrow(() -> new UsernameNotFoundException("Can't find user " + username));
        return user.map(MyUserDetails::new).get();
    }
}

我的用户详细信息

public class MyUserDetails implements UserDetails {

    private String username;
    private String password;
    private boolean isEnabled;
    private List<GrantedAuthority> authorities;

    public MyUserDetails(User user) {
        this.username = user.getUsername();
        this.password = user.getPassword();
        this.isEnabled = user.isEnabled();
        this.authorities = Arrays.stream(user.getAuthorities().split(","))
                .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return isEnabled;
    }
}

我的用户存储库

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

    Optional<User> findByUsername(String username);

}

用户
@Entity(name="Users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String username;
    private String password;

    @Transient
    private String passwordConfirm;
    private boolean isEnabled;
    private String authorities;
    private String isPremium;
    private LocalDateTime premiumExpiryDate;

    // getters and setters
}

登录页面上的登录表单

<p th:if="${loginError}" class="error">Wrong user or password</p>
<form th:action="@{/login}" method="post" class="login100-form validate-form p-l-55 p-r-55 p-t-178">
        <span class="login100-form-title">
            Sign In
        </span>

    <div class="wrap-input100 validate-input m-b-16" data-validate="Please enter username">
        <input class="input100" type="text" name="username" placeholder="Username">
        <span class="focus-input100"></span>
    </div>

    <div class="wrap-input100 validate-input" data-validate = "Please enter password">
        <input class="input100" type="password" name="password" placeholder="Password">
        <span class="focus-input100"></span>
    </div>

    <div class="text-right p-t-13 p-b-23">
            <span class="txt1">
                Forgot
            </span>

        <a href="#" class="txt2">
            Username / Password?
        </a>
    </div>

    <div class="container-login100-form-btn">
        <button class="login100-form-btn">
            Sign in
        </button>
    </div>
</form>

我没有一个针对登录的POST映射,因为它应该由Spring安全提供。然而,我怀疑问题一定出在那里,因为我在UserDetails和UserDetailsService中都有日志记录语句,但是这些语句都没有被调用。
你能帮我找出问题所在吗?

您提供的示例没有初始数据库,所以可以假设您是手动添加用户,直接在数据库中添加,对吗?在这种情况下,密码必须进行编码(默认情况下为bcrypt)。尝试使用在线生成器获取明文密码的哈希值,然后将此哈希值插入到数据库中:https://bcrypt-generator.com/ - Alex Chernyshev
我正在使用应用程序的另一部分来存储用户,调用userRepository.save(user)。在SecurityConfig中,我正在使用NoOpPasswordEncoder。密码以明文形式保存到数据库中,未加密。您确定我需要使用加密吗?在线教程中,他们总是使用NoOpPasswordEncoder,并且它对他们有效。 - Jan Horčička
不,对于你的情况来说没问题。这个示例是基于旧版Spring Boot的,在最新版本中,“NoOpPasswordEncoder”已被弃用,并且默认情况下密码会被加密。 - Alex Chernyshev
我已经修改了代码,使用了BCryptPasswordEncoder。现在密码已经在数据库中进行了哈希处理,但是日志记录仍然无法正常工作。 - Jan Horčička
1个回答

5
我明白了。有两个问题。
系统正在使用默认值。
inMemoryUserDetailsManager 

我想使用自己的 UserDetailsService 实现而非 inMemoryUserDetailsManager。我不知道如何关闭 inMemoryUserDetailsManager,但我发现了如何使用自己的实现。你需要在服务上加上 @Service("userDetailsService") 注解。

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
...

第二个问题是用户需要将“enabled”值设置为true。如果被设置为false,Spring会将他们视为不活动状态。
public void saveUser(User user) {
    user.setAuthorities("USER");
    user.setEnabled(true);
    ...

现在它对我起作用了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接