I'm trying to login a user to a webpage, where credentials are stored in mongodb. I'm using spring boot and wasn't aware of spring security features.
That's why I ended up, using the following controller code, which works:
@RequestMapping("/auth")
String auth(@ModelAttribute("credential") Credential credential) throws AuthenticationFailedException {
handler.authenticateUser(credential);
log.debug("user: " + credential.getUsername() + " authenticated successfully via /auth");
return "main";
}
handler:
@Autowired
private UserRepository repository;
public boolean authenticateUser(Credential credential) throws AuthenticationFailedException {
User authenticatedUser = repository.findByCredentialUsername(credential.getUsername());
if (authenticatedUser == null)
throw new AuthenticationFailedException(
"cant find any user with name: " + credential.getUsername());
boolean matches = EncryptionUtils.matchEncryptedPassword(credential.getPassword(),
authenticatedUser);
if (!matches)
throw new AuthenticationFailedException(
"provided password is not matching password stored in database");
return matches;
}
After realizing that that servlet filters etc. are relatively easy to setup with spring-security, I had changed my code, so I ended up with this:
Config:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserAuthenticationService userAuthService;
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userAuthService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.userDetailsService(userAuthService).authorizeRequests()
.antMatchers("/register", "/", "/css/**", "/images/**", "/js/**", "/login")
.permitAll().anyRequest().authenticated().and().
formLogin().loginPage("/").and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
SecurityWebInit:
public class SecurityWebInit extends AbstractSecurityWebApplicationInitializer {
public SecurityWebInit() {
super(SecurityConfig.class);
}
}
UserAuthenticationService:
@Component
public class UserAuthenticationService implements UserDetailsService {
private static final Logger log = Logger.getLogger(UserAuthenticationService.class);
@Autowired
private UserRepository repository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = repository.findByCredentialUsername(username);
log.debug("called user auth service..");
if (user == null)
throw new UsernameNotFoundException("username :"+username+" not found.");
else {
AuthenticatedUser authUser = new AuthenticatedUser(user);
return authUser;
}
}
}
AuthenticatedUser - model:
public class AuthenticatedUser implements UserDetails {
private User user; // user is my own model, not of spring-framework
private static final Logger log = Logger.getLogger(AuthenticatedUser.class);
public AuthenticatedUser(User user) {
this.user = user;
}
.. //rest of interface impl. (any method returns true)
//no roles or authorities modeled, yet
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
Modified controller:
@RequestMapping(method=RequestMethod.POST, value="/login")
String auth2(@RequestParam("username") String username, @RequestParam("password") String password) throws AuthenticationFailedException {
Credential cred = new Credential();
cred.setUsername(username);
cred.setPassword(password);
handler.authenticateUser(cred);
log.debug("user: " + cred.getUsername() + " authenticated successfully via /login");
return "main";
}
Template:
<form method="post" action="/login" th:object="${credential}">
<div class="form-group">
<input type="email" th:field="*{username}" class="form-control"
id="username" placeholder="Username/Email" />
</div>
<div class="form-group">
<input type="password" th:field="*{password}"
class="form-control" id="password" placeholder="Password" />
</div>
<button type="submit" class="btn btn-default login-button">Submit</button>
</form>
All of the provided code, is more less clustered together from another tutorials and examples.
When I authenticate via form-based login, it actually works. But after I have been logged in, when I navigate to localhost:8080/<anyRestrictedUrl>
it still redirects me to login form.
I also placed some log.debug(..)
inside of UserAuthenticationService
, but I can't see any of them at all..
In most other examples, I can't even see the basic controller method mapped to \login
, but I think this is because of spring doing this 4 me behind the scenes?!
Please note, that I not using any authorities and / or roles, as that isn't modeled in db, yet. Is this a must have, when using spring security, as most of the examples include roles and authorities.
Edit: (Relevant parts of pom.xml):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
<relativePath />
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
Update: (Things I've tried on above mentioned code):
1) Setting other session policy: (not working)
http.userDetailsService(userAuthService).authorizeRequests()
.antMatchers("/register", "/", "/css/**", "/images/**", "/js/**", "/login")
.permitAll().anyRequest().authenticated().and().
formLogin().loginPage("/").and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS).and().csrf().disable();
2) Replacing @Override with @Autowired annotation (got that from another SO post - not working):
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
I could resolve this..
With reading of this blog post it turned out, that its needed! to specify the usernameParameter(..), passwordParameter(..)
in config(HttpSecurity http)
, unlike they do in many basic examples or tutorials. So relying on default values was my pity. I had to append:
formLogin().loginPage("/")
.loginProcessingUrl("/login")
.failureUrl("/")
.defaultSuccessUrl("/main")
.usernameParameter("username") //needed, if custom login page
.passwordParameter("password") //needed, if custom login page
Without those param-settings the UserDetailsService
implementation gets not triggered.
Hope it helps anyone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With