Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion with Spring Security-based authentication with mongo backed db

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());
}
like image 955
lunatikz Avatar asked Oct 18 '22 04:10

lunatikz


1 Answers

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.

like image 112
lunatikz Avatar answered Oct 21 '22 06:10

lunatikz