How to provide multiple Authentication providers with java configuration of spring security - java

I store information about users in seperated tables owners,employees,users i am trying to use java configuration in spring security.
I have created three diferent authentication providers for each user type, but only the Users Provider is being triggered. I have read the spring security docs and the only way to do this seems to be is create class with multiple embedded classes extended from WebSecurityConfigurerAdapter but i don't want to do it this way because it requires a lot of duplicating code, is there any other way
I tryed to use the simple userDetailService inside which i send request to all tables in databese but still there is not results, only one query is buing executed and nothing, the only responce i get is:
2016-02-09 23:06:25.976 DEBUG 8780 --- [nio-8080-exec-1]
.s.a.DefaultAuthenticationEventPublisher : No event was found for the
exception
org.springframework.security.authentication.InternalAuthenticationServiceException
2016-02-09 23:06:25.976 DEBUG 8780 --- [nio-8080-exec-1]
o.s.s.w.a.www.BasicAuthenticationFilter : Authentication request for
failed:
org.springframework.security.authentication.InternalAuthenticationServiceException:
No entity found for query; nested exception is
javax.persistence.NoResultException: No entity found for query
But i never throw any exception!! And the most strange is that i can see in the debugger how the execution rapidly stops right after em.createQuery(..).getSingleResult().. and that's it, nothing more! There is no return statement no exception nothing, wtf!!
This is part of my current configuration:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(createAuthenticationProvider(employeeDetailService()))
.authenticationProvider(createAuthenticationProvider(ownerDetailsService()))
.authenticationProvider(createAuthenticationProvider(userDetailsService()));
}
#Bean
public OwnerDetailsService ownerDetailsService() {
return new OwnerDetailsService();
}
#Bean
public EmployeeDetailServiceImpl employeeDetailService() {
return new EmployeeDetailServiceImpl();
}
#Bean
public UserDetailsServiceImpl userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(6);
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new MySimpleUrlAuthenticationSuccessHendler();
}
private AuthenticationProvider createAuthenticationProvider(UserDetailsService service) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(service);
provider.setPasswordEncoder(passwordEncoder());
provider.setHideUserNotFoundExceptions(true);
return provider;
}
User detail services:
#Service
public abstract class CustomUserDetailService implements UserDetailsService{
#Autowired
IDBBean dao;
protected CustomUserDetails getUser(GetUserByNameFunction function, String name) {
return createUser(function.get(name));
}
protected CustomUserDetails createUser(Authenticational user) {
return new CustomUserDetails(user, getAuthorities(user.getAuthority()));
}
protected List<GrantedAuthority> getAuthorities(String authority) {
return Collections.singletonList(new SimpleGrantedAuthority(authority));
}
}
Implementations
public class EmployeeDetailServiceImpl extends CustomUserDetailService {
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return super.getUser(dao::getEmployeeByEmail, email);
}
}
public class OwnerDetailsService extends CustomUserDetailService {
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return super.getUser(dao::getOwnerByEmail, email);
}
}
public class UserDetailsServiceImpl extends CustomUserDetailService {
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
return super.getUser(dao::getUserByEmail, userName);
}
}
Custom user details:
private Long id;
private String userEmail;
public CustomUserDetails(Authenticational user,
Collection<? extends GrantedAuthority> authorities) {
super(
user.getName(),
user.getPassword().toLowerCase(),
user.isEnabled(),
true,
true,
true,
authorities);
upadateValues(user);
}
private void upadateValues(Authenticational user) {
this.id = user.getId();
this.userEmail = user.getEmail();
}

Just to clarify something from the other answer:
Your authentication providers are stored in a list inside ProviderManager that iterates your authentication request through them. If your authentication provider throws AuthenticationException (BadCredentialsException extends AuthenticationException), then the ProviderManager will try another provider. If you set the hideUserNotFoundExceptions property, then it will also wrap and ignore UsernameNotFoundException and try another provider in this case too.
If I were you I would start by placing a debugging point inside ProviderManager's authenticate method. From there you can find out why the other authentication providers are not being called for their authenticate method.
Also I would think about having only one authentication provider with one UserDetailsService. It seems to me that you are doing a lot of complex not really needed operations like passing function to your abstract implementation when all you could do would be to have one UserDetailsService that would ask all your DAOs for a user. Which is basically what you're trying to accomplish but minus 2 authentication providers, minus 1 abstract class and minus 2 UserDetailsService implementations.

Spring Security will not try other authentication providers if a provider throws an AccountStatusException or if a UserDetailsService throws a UserNameNotFoundException or any other AuthenticationException
If you want other providers to be tried, then the loadUserByUserName methods of your UserDetailsServiceImpl and OwnerDetailsService should not throw the UserNameNotFound exception.
You should decide if you either want to return a dummy anonymous UserDetails object that will be used exclusively for fallback or some other mechanism to not throw the exception when a user is not available in your UserDetailsService implementation

this is an example I'v done to provide multi-auth for my application which have two diffirent user : admin and client .
ps: the admin and the client are two diffirent model.
public class CustomUserDetails implements UserDetails{
private Admin admin;
private Client client;
public CustomUserDetails(Admin admin) {
this.admin = admin;
}
public CustomUserDetails(Client client) {
this.client = client;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
public String getPassword() {
if((admin != null)&&(client==null)) {
return admin.getPassword();
}
else {
return client.getPassword();
}
}
#Override
public String getUsername() {
if((admin != null)&&(client==null)) {
return admin.getEmail();
}
else {
return client.getMail();
}
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
the CustomUserDetailsService class :
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
AdminRepository adminRepo;
#Autowired
ClientRepository clientRepo;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Admin admin = adminRepo.findByEmail(email);
Client client = clientRepo.findByEmail(email);
if((admin == null)&&(client==null)) {
throw new UsernameNotFoundException("User not found");
}
else if((admin != null)&&(client==null)) {
return new CustomUserDetails(admin);
}
else {
return new CustomUserDetails(client);
}
}
}

Related

Spring Security - Active Directory Get Custom User details

I am having a challenge extracting original user details from the LdapUserDetailsImpl such as the getUsername() returns null
I have the following Java classes
Custom User Class
public class AppUserDetails extends LdapUserDetailsImpl {
public AppUserDetails() {
super();
}
private String mail; //mail
}
Custom User Details Mapper
public class AppUserDetailsContextMapper extends LdapUserDetailsMapper {
public AppUserDetailsContextMapper() {
super();
}
#Override
public AppUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
UserDetails details = super.mapUserFromContext(ctx, username, authorities);
String mail = ctx.getStringAttribute("mail");
AppUserDetails appUserDetails = new AppUserDetails();
appUserDetails.setMail(mail);
return appUserDetails;
}
}
Web security configuration
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Environment env;
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new AppUserDetailsContextMapper();
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider =
new ActiveDirectoryLdapAuthenticationProvider(
env.getRequiredProperty("spring.ldap.domain"),
env.getRequiredProperty("spring.ldap.urls")
);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(
Collections
.singletonList(activeDirectoryLdapAuthenticationProvider())
);
}
}
However I am having a serious challenge trying to getting Custom User Details in the controller:
#Controller
public class HomeController {
#GetMapping(value = {"/home"})
public String home(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println(authentication.getPrincipal());
AppUserDetails appUserDetails = (AppUserDetails) authentication.getPrincipal();
System.out.println(appUserDetails);
return "home";
}
}
I am getting a NullPointer exception if I try to any property from the LdapUserDetailsImpl class. However, I am accurately getting all the properties from the AppUserDetails - which extends the LdapUserDetailsImpl class.
Where might I be missing it?

Password hashing with Salt in Spring Boot Security

Hello I'm facing some problems with the authentification mechanism provided by Spring Boot Security
The main issue is that I have a login form provided by Spring Boot Security and want to check the inputs with an underlying database of users with email, hashed_password, and user_salt
I injected my UserDetailService in the WebSecurityConfigAdapter
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(as); // #Autowired private AuthService as;
}
The AuthService is the following:
#Service
public class AuthService implements UserDetailsService {
#Autowired
MyUserClassRepository ur;
#Override
public UserDetails loadUserByUsername(String username) {
MyUserClass user = ur.findUserByEmail(username);
if(user == null){
throw new UsernameNotFoundException(username);
}
System.out.println("User found but we must check the password");
return new MyUserPrincipal(user);
}
#SessionScope
public class MyUserPrincipal implements UserDetails {
private MyUserClass user;
MyUserClass getUser(){
return user; // I need to retrieve the user later for my Webapplication
}
MyUserPrincipal(MyUserClass user){
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
System.out.println("Call Granted");
return null;
}
#Override
public String getPassword() {
return user.passwordHash;
}
#Override
public String getUsername() {
return user.email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
}
I need a hook or a method where I can get the input of the user so I can encode and salt the password.
A default PassWordEncoder as a component doesn't have access to the inputs or at least I don't know how to get the credentials in the input field:
//#Component
public final class myPwEncoder implements PasswordEncoder {
public String encode(CharSequence rawPassword) {
System.out.println("Encode My Stuff: " + rawPassword);
//MyUserClass user = ((AuthService.MyUserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser(); NullPointerException because the User will be after the verification initialized...
return rawPassword.toString();
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
System.out.println("Macthes call: "+rawPassword + " " + encodedPassword);
return rawPassword.toString().equals(encodedPassword);
}
}
Thank you for your help!
(I want to use sha256-hash with random generated Salt stored in the user DB table. I know BCrypt is probably the best choice but I have to need to use sha256)

Spring boot bcrypt.BCryptPasswordEncoder and Authentication issue

I am configuring Spring Security for the first time, but it seems Spring can't see my client's raw password as I am getting this error.
o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
It seems like an obvious problem, but permit me, I just can't figure it our after many attempts.
My SecurityConfig class is ...
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomUserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
This is my UserServiceDetails Service.
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository repo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Owner> optionalUser = repo.findByUsername(username);
optionalUser
.orElseThrow(() -> new UsernameNotFoundException("Username not
found"));
return optionalUser
.map(CustomUserDetails::new).get();
}
}
I do also have the following bean configured
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
}
This is my userService.
public class CustomUserDetails extends Owner implements UserDetails {
public CustomUserDetails(final Owner owner) {
super();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_"+getRoles()))
.collect(Collectors.toList());
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
I surely must be missing something, but I can't seem to figure it out. From HttpRequest, I know that the password is being posted to the System, as I logged.
I found out that the OptionalUser is not mapping correctly into the UserDetail object, thereby returning a new and empty UserDetail object. the following code is wrong.
return optionalUser
.map(CustomUserDetails::new).get();
}
So I my new UserDetailsService class is ...
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository repo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Optional<Owner> optionalUser = repo.findByUsername(username);
Owner user = optionalUser.get();
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthorities(user));
}
public Collection<? extends GrantedAuthority> getAuthorities(Owner user) {
return user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_"+user.getRoles()))
.collect(Collectors.toList());
}
}

How to authenticate user in spring using AuthenticationManagerBuilder and userDetailsService based on credentials retrived from user entity

i am a newbie to spring
was not able to implement login to the application, actually could not figure out where its wrong.
followed a youtube video to do all this.
Help would be very much appreciated.
When i try to log in the application wont allow to log in.
console logs shows querys are being executed but cant log into the system.
also the password are saved in plain ASCII.
WebSecurityConfig class
#Configuration
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
// roles admin allow to access /actuator/**
// roles user allow to access /Application/**
// custom 403 access denied handler
#Override
protected void configure(HttpSecurity http) throws Exception {
// some antMatchers permit all
}
}
customUserDetailsService class
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
private final UserRepo userRepo;
#Autowired
public CustomUserDetailsService(UserRepo userRepo) {
this.userRepo = userRepo;
}
#Override
// userId is reffered as username
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user=userRepo.findByUsername(userName);
if(null == user){
System.out.println("\n\n\n No user present with username: "+userName);
throw new UsernameNotFoundException("No user present with username: "+userName);
}else{
CustomUserDetails c =new CustomUserDetails(user);
//System.out.println(c.getAuthorities());
return c;
}
}
}
CustomUserDetails class
public class CustomUserDetails extends User implements UserDetails{
private static final long serialVersionUID = 1L;
public CustomUserDetails(User user){
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<UserRole> roles = super.getUserRole();
List<String> userRoles= new ArrayList<String>();
for(UserRole r : roles) {
userRoles.add(r.getRole().toString());
}
String strRoles=StringUtils.collectionToCommaDelimitedString(userRoles);
return AuthorityUtils.commaSeparatedStringToAuthorityList(strRoles);
}
#Override
public boolean isAccountNonExpired() {
return super.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return super.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return super.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return super.isEnabled();
}
#Override
public String getUsername() {
return super.getUsername();
}
#Override
public String getPassword() {
return super.getPassword();
}
}

Can Spring Security accept multiple passwords for the same user?

I have Spring Security working within my application to authenticate a user with one password. I'm trying to meet a requirement that an override password will also authenticate that same user.
How can I do this with Spring Security?
It is possible, you will have to implement your own AuthenticationProvider possibly by extending the existing DaoAuthenticationProvider (see additionalAuthenticationChecks() in there).
Also the user is only associated with a single password by default (UserDetails.getPassword()), so you will need to have an extension of that class holding multiple passwords, and a corresponding implementation of UserDetailsService that knows how to load the user along with its passwords.
It is easy to do by providing multiple 'AuthenticationProvider' with 'UserDetailsService'.
private DaoAuthenticationProvider userAuthProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setHideUserNotFoundExceptions(false);
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(userDetailsService);
return provider;
}
private DaoAuthenticationProvider superVisorAuthProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setHideUserNotFoundExceptions(false);
provider.setUserDetailsService(supervisorDetailService);
return provider;
}
then
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(userAuthProvider());
auth.authenticationProvider(superVisorAuthProvider());
}
As has been mentioned - you can overwrite 'additionalAuthenticationChecks'
Hope this helps somebody.
#Slf4j
#Service
class FlexibleAuthenticationProvider extends DaoAuthenticationProvider implements AuthenticationProvider {
#Autowired
UserDetailsService userDetailsService
#Autowired
PasswordEncoder passwordEncoder
#PostConstruct
def init() {
super.setPasswordEncoder(passwordEncoder)
super.setUserDetailsService(userDetailsService)
}
#Override
protected void additionalAuthenticationChecks(
UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
try {
super.additionalAuthenticationChecks(userDetails, authentication)
} catch (AuthenticationException e) {
log.error('Unable to authenticate with regular credentials')
try {
def mutableUserDetails = new MutableUser(userDetails)
mutableUserDetails.password = 'alternatepassword'
return super.additionalAuthenticationChecks(mutableUserDetails, authentication)
} catch (AuthenticationException err) {
log.error('Token based authentication failed')
}
throw e
}
}
static class MutableUser implements UserDetails {
private String password
private final UserDetails delegate
MutableUser(UserDetails user) {
this.delegate = user
this.password = user.password
}
String getPassword() {
return password
}
void setPassword(String password) {
this.password = password
}
....
}
}
#Configuration
class AuthWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
FlexibleAuthenticationProvider flexibleAuthenticationProvider
....
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(flexibleAuthenticationProvider)
}
....
}

Categories

Resources