Skip to content

Commit

Permalink
Improve logging for Global Authentication
Browse files Browse the repository at this point in the history
Closes gh-14663
  • Loading branch information
Kehrlann authored and jzheaux committed Apr 25, 2024
1 parent 23dc0ed commit 7ddc005
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@

package org.springframework.security.config.annotation.authentication.configuration;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

Expand Down Expand Up @@ -49,16 +56,33 @@ public void init(AuthenticationManagerBuilder auth) throws Exception {

class InitializeAuthenticationProviderManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

private final Log logger = LogFactory.getLog(getClass());

@Override
public void configure(AuthenticationManagerBuilder auth) {
if (auth.isConfigured()) {
return;
}
AuthenticationProvider authenticationProvider = getBeanOrNull(AuthenticationProvider.class);
if (authenticationProvider == null) {
List<BeanWithName<AuthenticationProvider>> authenticationProviders = getBeansWithName(
AuthenticationProvider.class);
if (authenticationProviders.isEmpty()) {
return;
}
else if (authenticationProviders.size() > 1) {
List<String> beanNames = authenticationProviders.stream().map(BeanWithName::getName).toList();
this.logger.info(LogMessage.format("Found %s AuthenticationProvider beans, with names %s. "
+ "Global Authentication Manager will not be configured with AuthenticationProviders. "
+ "Consider publishing a single AuthenticationProvider bean, or wiring your Providers directly "
+ "using the DSL.", authenticationProviders.size(), beanNames));
return;
}
var authenticationProvider = authenticationProviders.get(0).getBean();
var authenticationProviderBeanName = authenticationProviders.get(0).getName();

auth.authenticationProvider(authenticationProvider);
this.logger.info(LogMessage.format(
"Global AuthenticationManager configured with AuthenticationProvider bean with name %s",
authenticationProviderBeanName));
}

/**
Expand All @@ -74,6 +98,42 @@ private <T> T getBeanOrNull(Class<T> type) {
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
}

/**
* @return a list of beans of the requested class, along with their names. If
* there are no registered beans of that type, the list is empty.
*/
private <T> List<BeanWithName<T>> getBeansWithName(Class<T> type) {
List<BeanWithName<T>> beanWithNames = new ArrayList<>();
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
.getBeanNamesForType(type);
for (String beanName : beanNames) {
T bean = InitializeAuthenticationProviderBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
beanWithNames.add(new BeanWithName<T>(bean, beanName));
}
return beanWithNames;
}

static class BeanWithName<T> {

private final T bean;

private final String name;

BeanWithName(T bean, String name) {
this.bean = bean;
this.name = name;
}

T getBean() {
return this.bean;
}

String getName() {
return this.name;
}

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@

package org.springframework.security.config.annotation.authentication.configuration;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.password.CompromisedPasswordChecker;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
Expand Down Expand Up @@ -55,15 +62,35 @@ public void init(AuthenticationManagerBuilder auth) throws Exception {

class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

private final Log logger = LogFactory.getLog(getClass());

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
List<BeanWithName<UserDetailsService>> userDetailsServices = getBeansWithName(UserDetailsService.class);
if (auth.isConfigured()) {
if (!userDetailsServices.isEmpty()) {
this.logger.warn("Global AuthenticationManager configured with an AuthenticationProvider bean. "
+ "UserDetailsService beans will not be used for username/password login. "
+ "Consider removing the AuthenticationProvider bean. "
+ "Alternatively, consider using the UserDetailsService in a manually instantiated "
+ "DaoAuthenticationProvider.");
}
return;
}

if (userDetailsServices.isEmpty()) {
return;
}
UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
if (userDetailsService == null) {
else if (userDetailsServices.size() > 1) {
List<String> beanNames = userDetailsServices.stream().map(BeanWithName::getName).toList();
this.logger.warn(LogMessage.format("Found %s UserDetailsService beans, with names %s. "
+ "Global Authentication Manager will not use a UserDetailsService for username/password login. "
+ "Consider publishing a single UserDetailsService bean.", userDetailsServices.size(),
beanNames));
return;
}
var userDetailsService = userDetailsServices.get(0).getBean();
var userDetailsServiceBeanName = userDetailsServices.get(0).getName();
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
Expand All @@ -83,6 +110,9 @@ public void configure(AuthenticationManagerBuilder auth) throws Exception {
}
provider.afterPropertiesSet();
auth.authenticationProvider(provider);
this.logger.info(LogMessage.format(
"Global AuthenticationManager configured with UserDetailsService bean with name %s",
userDetailsServiceBeanName));
}

/**
Expand All @@ -97,6 +127,41 @@ private <T> T getBeanOrNull(Class<T> type) {
return InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
}

/**
* @return a list of beans of the requested class, along with their names. If
* there are no registered beans of that type, the list is empty.
*/
private <T> List<BeanWithName<T>> getBeansWithName(Class<T> type) {
List<BeanWithName<T>> beanWithNames = new ArrayList<>();
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
for (String beanName : beanNames) {
T bean = InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
beanWithNames.add(new BeanWithName<T>(bean, beanName));
}
return beanWithNames;
}

static class BeanWithName<T> {

private final T bean;

private final String name;

BeanWithName(T bean, String name) {
this.bean = bean;
this.name = name;
}

T getBean() {
return this.bean;
}

String getName() {
return this.name;
}

}

}

}

0 comments on commit 7ddc005

Please sign in to comment.