Morning!
Just started learning Spring Security with the help of the Baeldung tutorial at https://www.baeldung.com/spring-security-authentication-with-a-database.
However, it's not quite working. What I'm trying to do is connecting my simple H2 database, containing a User table with id, username and password (in plaintext for simplicity), with my secured web application.
I created WebSecurityConfig (extending WebSecurityConfigurerAdapter, see below), MyUserDetailsService (implementing UserDetailsService, see below) and LoggedInUser (implementing UserDetails, see below) classes.
WebSecurityConfig: This should secure all pages except home, login and register pages, which is working. Also, globalSecurityConfiguration
should enable the login function by linking to the userDetailsService
.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
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;
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = MyUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void globalSecurityConfiguration(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
MyUserDetailsService: This gets the Repository injection to access my database. I check the database for the username, and if it's present, I return a new LoggedInUser.
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.List;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
List<User> users = userRepository.findByUsername(username);
if (users.size() == 0) {
throw new UsernameNotFoundException(username);
}
return new LoggedInUser(users.get(0));
}
}
And finally, the LoggedInUser
class:
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class LoggedInUser implements UserDetails {
private User user;
public LoggedInUser(User user) {
this.user= user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getNickname();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
When I try to log in with some nonexistent username, the error message pops as it should. However, when I'm trying to log in with an existing username (with any password, right or wrong), it's not giving any error message but isn't logging me in either (at least I still can't access other secured pages of the app).
I'm omitting User
and UserRepository
classes since they're just pretty straightforward and well tested. My login page looks like that:
<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://www.thymeleaf.org">
<head>
<title>Spring Security Example</title>
</head>
<body>
<div class="container">
<form name="f" th:action="@{/login}" method="post">
<fieldset>
<legend>Please Login</legend>
<div th:if="${param.error}" class="alert alert-error">
Invalid username and password.
</div>
<div th:if="${param.logout}" class="alert alert-success">
You have been logged out.
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</fieldset>
</form>
</div>
</body>
</html>
I know that the loadUserByUsername
method isn't touching the password, but from what I've read, checking if the right password was entered happens automatically within the Security framework.
I also tried to implement my own AuthenticationProvider
to use instead of the UserDetailsService
to check if both username and password inputs match the database entries within the authenticate
method, but then I encountered another problem - wrong credentials now get flagged right, but right credentials produced an error Cannot invoke "Object.toString()" because the return value of "org.springframework.security.core.Authentication.getCredentials()" is null
. However the line the error mentioned was the one that reads the password from the user input - and since this only happens for passwords matching the correct one, this shouldn't be null
. I'm not posting code here since probably this is a different issue though.
Thanks for any help! Remember, this is like the first time I touched any security framework, so better ELI5 :)
Read more here: https://stackoverflow.com/questions/65717868/simple-spring-security-example-not-logging-in
Content Attribution
This content was originally published by Noel93 at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.