Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot security with 3 fields authentication and custom login form

I'm using spring boot and i need to implement spring security with 3 fields authentication process username, password and corporate identifier as a hidden input in a form.

I implemented a custom usernamepasswordauthenticationfilter but it not seems to be enough to setup the security config.

EDIT :

Users don't seem to be authenticated ! because a can access to authenticated request defined in web config

EDIT 2 :

in my custom filter when a enter a valid user it's do execute on succesfulAuthentication. What i'm missing please provide me any help :( Here were i am

@Repository
public class AuthenticationUserDetailsService implements UserDetailsService {

private static final Logger LOGGER = Logger.getLogger(AuthenticationUserDetailsService.class);

@Autowired
private UserRepository users;

private org.springframework.security.core.userdetails.User userdetails;

@Override
public UserDetails loadUserByUsername(String input) throws UsernameNotFoundException {
    // TODO Auto-generated method stub

    System.out.println(input);
    String[] split = input.split(":");
    if (split.length < 2) {
        LOGGER.debug("User did not enter both username and corporate domain.");
        throw new UsernameNotFoundException("no corporate identifier is specified");
    }
    String username = split[0];
    String corporateId = split[1];

    System.out.println("Username = " + username);
    System.out.println("Corporate identifier = " + corporateId);

    boolean enabled = true;
    boolean accountNonExpired = true;
    boolean credentialsNonExpired = true;
    boolean accountNonLocked = true;

    com.ubleam.corporate.server.model.User user;

    user = checkUserDetail(username, corporateId);

    if (user == null)
        throw new NotAuthorizedException("Your are not allowed to access to this resource");

    LOGGER.info("User email : " + user.getEmail() + "#User corporate : " + user.getCorporateId());

    userdetails = new User(user.getEmail(), user.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, getAuthorities("ROLE_USER"));
    return userdetails;
}

/**
 * 
 * @param roles
 *            roles granted for user
 * @return List of granted authorities
 * 
 */

public List<GrantedAuthority> getAuthorities(String roles) {

    List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
    authList.add(new SimpleGrantedAuthority(roles));
    return authList;
}

/**
 * User authentication details from database
 * 
 * @param username
 *            to use for authentication
 * @param coporateId
 *            corporate identifier of user
 * @return found user in database
 */
private com.ubleam.corporate.server.model.User checkUserDetail(String username, String corporateId) {

    com.ubleam.corporate.server.model.User user = users.findByEmailAndCorporateId(username, corporateId);

    return user;
}

My custom filter :

public class PlatformAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private static final Logger LOGGER = Logger.getLogger(PlatformAuthenticationFilter.class);

private static final String LOGIN_SUCCESS_URL = "{0}/bleamcards/{1}/home";
private static final String LOGIN_ERROR_URL = "{0}/bleamcards/{1}/login?error";
private String parameter = "corporateId";
private String delimiter = ":";
private String corporateId;

@Override
protected String obtainUsername(HttpServletRequest request) {
    String username = request.getParameter(getUsernameParameter());
    String extraInput = request.getParameter(getParameter());

    String combinedUsername = username + getDelimiter() + extraInput;

    setCorporateId(extraInput);
    LOGGER.info("Combined username = " + combinedUsername);
    return combinedUsername;
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {

    String contextPath = request.getContextPath();
    String url = MessageFormat.format(LOGIN_SUCCESS_URL, contextPath, corporateId);

    response.sendRedirect(url);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {

    String contextPath = request.getContextPath();
    String url = MessageFormat.format(LOGIN_ERROR_URL, contextPath, corporateId);

    response.sendRedirect(url);
}

public String getParameter() {
    return parameter;
}

public void setParameter(String corporateId) {
    this.parameter = corporateId;
}

public String getDelimiter() {
    return delimiter;
}

public void setDelimiter(String delimiter) {
    this.delimiter = delimiter;
}

public String getCorporateId() {
    return corporateId;
}

public void setCorporateId(String corporateId) {
    this.corporateId = corporateId;
}
}

And finally the web security config :

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Inject
private AuthenticationManagerBuilder auth;
@Inject
private UserDetailsService userDS;

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests().antMatchers("/bleamcards/**/login", "/bleamcards/**/forgetpassword", "/bleamcards/**/register", "/css/**", "/js/**", "/images/**", "/webjars/**")
            .permitAll().anyRequest().authenticated().and().addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class).formLogin().loginPage("/login")
            .defaultSuccessUrl("/").permitAll().and().logout().permitAll();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.eraseCredentials(false);
    auth.userDetailsService(userDS).passwordEncoder(new BCryptPasswordEncoder());

}

@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
    return auth.build();
}

@Bean
public PlatformAuthenticationFilter authenticationFilter() throws Exception {
    PlatformAuthenticationFilter authFilter = new PlatformAuthenticationFilter();
    authFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
    authFilter.setAuthenticationManager(authenticationManager());
    authFilter.setUsernameParameter("username");
    authFilter.setPasswordParameter("password");
    authFilter.setParameter("corporateId");
    return authFilter;
}

@Override
protected UserDetailsService userDetailsService() {
    return userDS;
}

I want users to be able to connect only to /login /register /forgetpasswod urls for their respective corporate platforms

like image 651
Hassam Abdelillah Avatar asked Jun 05 '15 09:06

Hassam Abdelillah


2 Answers

Actually i manage to find a solution to my issue.

I added successHandler on successfulAuthentication was missing ! And a failureHandler too on unsuccessfulAuthentication methods.

Here is my new Authentication filter :

public class TwoFactorAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private static final String LOGIN_SUCCESS_URL = "{0}/bleamcards/{1}/home"; 
private static final String LOGIN_ERROR_URL = "{0}/bleamcards/{1}/login?error";
private String parameter = "corporateId";
private String delimiter = ":";
private String corporateId;


@Override
protected String obtainUsername(HttpServletRequest request) {
    String username = request.getParameter(getUsernameParameter());
    String extraInput = request.getParameter(getParameter());
    String combinedUsername = username + getDelimiter() + extraInput;

    setCorporateId(extraInput);
    System.out.println("Combined username = " + combinedUsername);
    return combinedUsername;
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain , Authentication authResult) throws IOException, ServletException {

    String contextPath = request.getContextPath();
    String url = MessageFormat.format(LOGIN_SUCCESS_URL, contextPath, corporateId);
    setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(url));
    super.successfulAuthentication(request, response, chain, authResult);

}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {

    String contextPath = request.getContextPath();
    String url = MessageFormat.format(LOGIN_ERROR_URL, contextPath, corporateId);
    setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(url));

    super.unsuccessfulAuthentication(request, response, failed);
}

public String getParameter() {
    return parameter;
}

public void setParameter(String corporateId) {
    this.parameter = corporateId;
}

public String getDelimiter() {
    return delimiter;
}

public void setDelimiter(String delimiter) {
    this.delimiter = delimiter;
}

public String getCorporateId() {
    return corporateId;
}

public void setCorporateId(String corporateId) {
    this.corporateId = corporateId;
}
}
like image 99
Hassam Abdelillah Avatar answered Nov 14 '22 23:11

Hassam Abdelillah


Did you check that your AuthenticationUserDetailsService code is actually been executed? If the framework is not invoking it this means that your configuration is not properly hooking that UserDetailsService. In your WebSecurityConfig I think you need to have this:

@Bean
public AuthenticationManager getAuthenticationManager() throws Exception {
    return super.authenticationManagerBean(); //not return auth.build();
}

I suggest you to take a look at this branch from Stormpath. There, they are configuring Spring Boot to use a custom AuthenticationProvider (similar to an UserDetailsService). That module uses and depends on this other Spring Security module.

Then, this sample Spring Security Example (note that it is not Spring Boot, but just Spring) will give you a complete example of the way the Spring Security Java Config is done. Please note that this Java Config extends this one which actually hides much of the actual internal configuration.

Disclaimer, I am an active Stormpath contributor.

like image 33
mario Avatar answered Nov 14 '22 23:11

mario