Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

problem in user authentication getting login screen again and again after entering credentials

Manjeet Kule
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
November 7, 2018

Hello Team ,

I am trying to do ldap authentication but after putting user credentials again same login login page is coming. I really do not have any clue to what to do next. 

Kindly help. Many thanks!

Sharing the code.

package com.wk.gbs.myaccount.application;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages="com.wk.gbs.myaccount.application")
public class AuthenticationApplication {

public static void main(String[] args) {
SpringApplication.run(AuthenticationApplication.class, args);
}
}

---

package com.wk.gbs.myaccount.application;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

 

public class ServletInitializer extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AuthenticationApplication.class);
}

}

---

package com.wk.gbs.myaccount.application.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.WebSecurityConfigurerAdapter;
import com.wk.gbs.myaccount.application.security.CustomActiveDirectoryLdapAuthenticationProvider;

/**
*
* @author TyroMind
* WebSecurityConfig class is created to provide configuration
* extending WebSecurityConfigurerAdapter to allows customization to httpSecurity
* Annotation Configuration to declare this class is configuration class
* Annotation EnableWebSecurity enables required WebSecurity for configuration class
*/

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {



/**
* Annotation Autowire used to create and object of CustomActiveDirectoryAuthProvider in WebSecurityConfig class
* and
* connect with WebSecurityConfig
*/
@Autowired
public CustomActiveDirectoryLdapAuthenticationProvider customActiveDirectoryLdapAuthenticationProvider;


@bean
public CustomActiveDirectoryLdapAuthenticationProvider customActiveDirectoryLdapAuthenticationProvider() {
return new CustomActiveDirectoryLdapAuthenticationProvider();

}

/**
* mentioned method is to override
* .authorizeRequests()
.anyRequest().fullyAuthenticated() authorize all requests coming in it and it
* should redirect to a login page
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin().permitAll()
;



}

/**
* SecurityBuilder used to create an AuthenticationManager.
* Allows for easily building in memory authentication, LDAP authentication, JDBC based authentication,
* adding UserDetailsService, and adding AuthenticationProvider
*/

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customActiveDirectoryLdapAuthenticationProvider);


}

}

---

package com.wk.gbs.myaccount.application.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WKMvcConfigurer implements WebMvcConfigurer {




@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("index").setViewName("index");
}

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}


}

 ---

package com.wk.gbs.myaccount.application.security;

 

import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.OperationNotSupportedException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.InitialLdapContext;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//import org.springframework.security.ldap.authentication.ad.ActiveDirectoryAuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
* Custom class created to override API class ActiveDirectoryLdapAuthenticationProvider, because of the following jiras:
* https://jira.spring.io/browse/SEC-1915
* https://jira.spring.io/browse/SEC-1823
* Changes made to method "searchForUser".
* ============ Diptayan Datta: 03/27/2015 ============
*/
@Component
public class CustomActiveDirectoryLdapAuthenticationProvider extends AbstractLdapAuthenticationProvider {
private static final Pattern SUB_ERROR_CODE = Pattern.compile(".*data\\s([0-9a-f]{3,4}).*");

// Error codes
private static final int USERNAME_NOT_FOUND = 0x525;
private static final int INVALID_PASSWORD = 0x52e;
private static final int NOT_PERMITTED = 0x530;
private static final int PASSWORD_EXPIRED = 0x532;
private static final int ACCOUNT_DISABLED = 0x533;
private static final int ACCOUNT_EXPIRED = 0x701;
private static final int PASSWORD_NEEDS_RESET = 0x773;
private static final int ACCOUNT_LOCKED = 0x775;

private String domain;
@SuppressWarnings("unused")
private String rootDn;
private String url;
private boolean convertSubErrorCodesToExceptions;

// Only used to allow tests to substitute a mock LdapContext
ContextFactory contextFactory = new ContextFactory();

public void setContextFactory(ContextFactory contextFactory) {
this.contextFactory = contextFactory;
}

/**
*
* @Param domain
*/
public void setDomain(String domain){
this.domain = StringUtils.hasText(domain) ? domain.toLowerCase() : null;
rootDn = this.domain == null ? null : rootDnFromDomain(this.domain);
}

/**
*
* @Param url
*/
public void setUrl(String url){
this.url = url;
}


@Override
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth) {
String userName = auth.getName().split("@")[0];
String[] userNameValidation = auth.getName().split("@");
for (int i = 0; i < userNameValidation.length - 1; i++) {
String subDomainName = auth.getName().split("@")[1];
if (subDomainName != null && !subDomainName.toLowerCase().equalsIgnoreCase("wolterskluwer.com")) {
throw badCredentials(auth.getName());
}
}
userName = userName.toLowerCase();
String password = (String)auth.getCredentials();
return performAuthentication(userName,password,auth);
}

public DirContextOperations performAuthentication(String userName, String password, UsernamePasswordAuthenticationToken auth) {
DirContext ctx = bindAsUser(userName, password);
try {
return searchForUser(ctx, userName);
} catch (NamingException e) {
logger.error("Failed to locate directory entry for authenticated user: " + userName, e);
throw badCredentials(e,auth.getName());
} finally {
LdapUtils.closeContext(ctx);
}
}

/**
* Creates the user authority list from the values of the {@code memberOf} attribute obtained from the user's
* Active Directory entry.
*/
@Override
protected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username, String password) {
return new ArrayList<GrantedAuthority>();
}

public DirContext bindAsUser(String userName, String password) {
final String bindUrl = url;
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
String bindPrincipal = createBindPrincipal(userName);
env.put(Context.SECURITY_PRINCIPAL, bindPrincipal);
env.put(Context.PROVIDER_URL, bindUrl);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.OBJECT_FACTORIES, DefaultDirObjectFactory.class.getName());
try {
return contextFactory.createContext(env);
} catch (NamingException e) {
if ((e instanceof AuthenticationException) || (e instanceof OperationNotSupportedException)) {
handleBindException(bindPrincipal, e);
throw badCredentials(e,userName);
} else {
throw LdapUtils.convertLdapException(e);
}
}
}

void handleBindException(String bindPrincipal, NamingException exception) {
int subErrorCode = parseSubErrorCode(exception.getMessage());

if (subErrorCode > 0) {
logger.info("Active Directory authentication failed: " + subCodeToLogMessage(subErrorCode));

if (convertSubErrorCodesToExceptions) {
raiseExceptionForErrorCode(subErrorCode, exception, bindPrincipal);
}
} else {
logger.debug("Failed to locate AD-specific sub-error code in message");
}
}

int parseSubErrorCode(String message) {
Matcher m = SUB_ERROR_CODE.matcher(message);

if (m.matches()) {
return Integer.parseInt(m.group(1), 16);
}

return -1;
}

void raiseExceptionForErrorCode(int code, NamingException exception,String userName) {
//String hexString = Integer.toHexString(code); Commented as part of secure Assist fix, Unused snippet
Throwable cause = new AuthenticationException();
//Throwable cause = new ActiveDirectoryAuthenticationException(hexString, exception.getMessage(), exception);
switch (code) {
case PASSWORD_EXPIRED:
try {
throw new Exception();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
case ACCOUNT_DISABLED:
throw new DisabledException(messages.getMessage("LdapAuthenticationProvider.disabled",
"User is disabled"), cause);
case ACCOUNT_EXPIRED:
throw new AccountExpiredException(messages.getMessage("LdapAuthenticationProvider.expired",
"User account has expired"), cause);
case ACCOUNT_LOCKED:
throw new LockedException(messages.getMessage("LdapAuthenticationProvider.locked",
"User account is locked"), cause);
default:
throw badCredentials(cause,userName);
}
}

String subCodeToLogMessage(int code) {
switch (code) {
case USERNAME_NOT_FOUND:
return "User was not found in directory";
case INVALID_PASSWORD:
return "Supplied password was invalid";
case NOT_PERMITTED:
return "User not permitted to logon at this time";
case PASSWORD_EXPIRED:
return "Password has expired";
case ACCOUNT_DISABLED:
return "Account is disabled";
case ACCOUNT_EXPIRED:
return "Account expired";
case PASSWORD_NEEDS_RESET:
return "User must reset password";
case ACCOUNT_LOCKED:
return "Account locked";
}

//return "Unknown (error code " + Integer.toHexString(code) +")"; Commented as part of secure Assist fix
return "Unknown (error code " + code +")";
}

private BadCredentialsException badCredentials(String userName) {
String[] userInfo = userName.split("@");
StringBuilder userNameInfo = new StringBuilder(userInfo[0]);
userNameInfo.append("@wolterskluwer.com");
return new BadCredentialsException(messages.getMessage(
"LdapAuthenticationProvider.badCredentials", "Bad credentials"));
}

//Modifier changed from private to protected to work with com.productportal.productsearch.web.ActiveDirectoryUserValidation
//Diptayan - 9/17/2015
public BadCredentialsException badCredentials(Throwable cause,String userName) {
return (BadCredentialsException) badCredentials(userName).initCause(cause);
}


//Modifier changed from private to protected to work with com.productportal.productsearch.web.ActiveDirectoryUserValidation
//Diptayan - 9/17/2015
public DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

//String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))";
//String searchFilter = "(&(objectClass=user)(SAMAccountName={0})";
String searchFilter = "(SAMAccountName={0})";

//final String bindPrincipal = createBindPrincipal(username);
final String bindPrincipal = username;

//String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal);
String searchRoot = "dc=na,dc=wkglobal,dc=com";
//TODO: Select specific return parameters from LDAP response (like "CN")
try {
return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter,
new Object[]{bindPrincipal});
} catch (IncorrectResultSizeDataAccessException incorrectResults) {
if (incorrectResults.getActualSize() == 0) {
UsernameNotFoundException userNameNotFoundException = new UsernameNotFoundException("User " + username + " not found in directory.");
userNameNotFoundException.initCause(incorrectResults);
throw badCredentials(userNameNotFoundException,username);
}
// Search should never return multiple results if properly configured, so just rethrow
throw incorrectResults;
}
}

protected String searchRootFromPrincipal(String bindPrincipal) {
int atChar = bindPrincipal.lastIndexOf('@');

if (atChar < 0) {
logger.debug("User principal '" + bindPrincipal + "' does not contain the domain, and no domain has been configured");
throw badCredentials(bindPrincipal);
}

return rootDnFromDomain(bindPrincipal.substring(atChar+ 1, bindPrincipal.length()));
}

private String rootDnFromDomain(String domain) {
String[] tokens = StringUtils.tokenizeToStringArray(domain, ".");
StringBuilder root = new StringBuilder();

for (String token : tokens) {
if (root.length() > 0) {
root.append(',');
}
root.append("dc=").append(token);
}

return root.toString();
}

String createBindPrincipal(String username) {
if (domain == null || username.toLowerCase().endsWith(domain)) {
return username;
}

return username + "@" + domain;
}

/**
* By default, a failed authentication (LDAP error 49) will result in a {@code BadCredentialsException}.
* <p>
* If this property is set to {@code true}, the exception message from a failed bind attempt will be parsed
* for the AD-specific error code and a {@link CredentialsExpiredException}, {@link DisabledException},
* {@link AccountExpiredException} or {@link LockedException} will be thrown for the corresponding codes. All
* other codes will result in the default {@code BadCredentialsException}.
*
* @Param convertSubErrorCodesToExceptions {@code true} to raise an exception based on the AD error code.
*/
public void setConvertSubErrorCodesToExceptions(boolean convertSubErrorCodesToExceptions) {
this.convertSubErrorCodesToExceptions = convertSubErrorCodesToExceptions;
}

static class ContextFactory {
DirContext createContext(Hashtable<?,?> env) throws NamingException {
return new InitialLdapContext(env, null);
}
}
}

 

0 answers

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events