I am trying to understand the steps to support LDAP with Spring security...replacing our current basic authentication. Recently I made the changes to support the deprecation of the WebSecurityConfigurerAdapter and all is working fine. So now I want to swap out the basic authentication AuthenticationManager or AuthenticationProvider with their LDAP equivalents. There are several websites with partial information and I cant quite put the pieces together.
So I am starting with the spring guide on authenticating ldap (https://github.com/spring-guides/gs-authenticating-ldap) I took the "complete" example, modified for our version of Spring Boot 2.7.5 and the app starts and validates fine. I tried updating the example according to several websites from :
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(new BCryptPasswordEncoder())
.passwordAttribute("userPassword");
To :
#Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
contextSourceFactoryBean.setPort(0);
return contextSourceFactoryBean;
}
#Bean
public AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, new BCryptPasswordEncoder());
factory.setUserDnPatterns("uid={0},ou=people");
factory.setPasswordAttribute("pwd");
return factory.createAuthenticationManager();
}
Thinking this closer to the recommendations and closer to what I would be using in our real project. Now when I try to build ... the test phase keeps failing with the following stack trace
ERROR[m] loginWithValidUserThenAuthenticated Time elapsed: 0 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authenticationManager' defined in class path resource [com/example/authenticatingldap/WebSecurityConfig.class]: Unsatisfied dependency expressed through method 'authenticationManager' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contextSourceFactoryBean': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Unable to load LDIF classpath*:*.ldif
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contextSourceFactoryBean': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Unable to load LDIF classpath*:*.ldif
Caused by: java.lang.IllegalStateException: Unable to load LDIF classpath*:*.ldif
Caused by: com.unboundid.ldap.sdk.LDAPException: An entry with DN 'dc=springframework,dc=org' already exists in the server.
I am using the same ldif file that worked with the other configuration. The code follows the other examples and recommendation but the embedded LDAP server wont load the file. Has anyone seen and solved this problem? Once this is working, do we simply remove the Embedded LDAP bean and somehow point the configuration to the real LDAP server?
Related
Keycloak OAuth 2.0 clientRegistrationRepository cannot be null.
Hello, discovered a problem, when trying to make use of OAuth 2.0 in my application. It fails after the startup. Message:
Exception encountered during context initialization - cancelling
refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration':
Unsatisfied dependency expressed through method
'setSecurityWebFilterChains' parameter 0; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'springSecurityFilterChain' defined in class
path resource
[com/microservices/apigateway/security/SecurityConfig.class]: Bean
instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate
[org.springframework.security.web.server.SecurityWebFilterChain]:
Factory method 'springSecurityFilterChain' threw exception; nested
exception is java.lang.IllegalArgumentException:
clientRegistrationRepository cannot be null
Cannot find any info in the net about the possible nature of this problem.
SecurityConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
#Configuration
#EnableWebFluxSecurity
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.pathMatchers("/actuator/**")
.permitAll()
.and()
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.oauth2Login() // to redirect to oauth2 login page.
;
return http.build();
}
}
application.properties
spring.application.name=api-gateway
server.port=8765
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
#spring.cloud.gateway.discovery.locator.enabled=true
#spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
spring.sleuth.sampler.probability=1.0
spring.security.oauth2.client.provider.myprovider.issuer-uri=http://localhost:8083/realms/MyMicroservicesRealm
spring.security.oauth2.client.provider.myprovider.authorization-uri=http://localhost:8083/realms/MyMicroservicesRealm/protocol/openid-connect/auth
spring.security.oauth2.client.provider.myprovider.token-uri=http://localhost:8083/realms/MyMicroservicesRealm/protocol/openid-connect/token
spring.security.oauth2.client.provider.myprovider.jwk-set-uri=http://localhost:8083/realms/MyMicroservicesRealm/protocol/openid-connect/certs
spring.security.oauth2.client.provider.myprovider.user-info-uri=http://localhost:8083/realms/MyMicroservicesRealm/protocol/openid-connect/userinfo
spring.security.oauth2.client.provider.mywebclient.user-name-attribute = preferred_username
spring.security.oauth2.client.registration.myclient.provider=myprovider
spring.security.oauth2.client.registration.myclient.scope=openid, profile, roles
spring.security.oauth2.client.registration.myclient.client-id=gateway-client
spring.security.oauth2.client.registration.myclient.client-secret=ajKhFIsMi3qJNlXkVTS8AFhYwhv3TUSh
spring.security.oauth2.client.registration.myclient.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.myclient.redirect-uri="{baseUrl}/login/oauth2/code/keycloak"
logging.level.org.springframework.security.web=DEBUG
Resolved by removing spring-boot-starter-web dependency
I have implemented code as specified here to add the multi-tenancy by issuer feature to my Spring Security configuration. However, when my Spring Boot application starts, I encounter the following error:
2021-10-26 | 10:31:37.762 | main | WARN | ConfigServletWebServerApplicationContext | Trace: | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' available
2021-10-26 | 10:31:39.361 | main | ERROR | o.s.b.d.LoggingFailureAnalysisReporter | Trace: |
***************************
APPLICATION FAILED TO START
***************************
Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' in your configuration.
The documentation states:
This is nice because the issuer endpoints are loaded lazily. In fact, the corresponding JwtAuthenticationProvider is instantiated only when the first request with the corresponding issuer is sent.
I wouldn't think that at application startup a JwtDecoder would be expected to be already instantiated according to this documentation. What am I missing in my configuration?
Update
After Steve Riesenberg's help, I have the following code compiling now. You can see in my code snippet what I used to have working (i.e., before we had the multi-tenant requirement) is now commented out:
//.jwt().jwtAuthenticationConverter(jwtAccessTokenConverter);
String[] issuers = new String[] {"https://www.example.com/auth/realms/example"};
JwtIssuerAuthenticationManagerResolver jwtIssuerAuthenticationManagerResolver =
new JwtIssuerAuthenticationManagerResolver(issuers);
...
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer(
oauth2ResourceServerConfigurer ->
oauth2ResourceServerConfigurer
.authenticationManagerResolver(jwtIssuerAuthenticationManagerResolver)
.authenticationEntryPoint(authenticationExceptionHandler));
// .jwt().jwtAuthenticationConverter(jwtAccessTokenConverter);
However, without the ability now to supply my own token converter since I had to remove .jwt(), I'm still unclear on what the default converter provides me.
Also, I'm not clear why I need to use the third constructor of JwtIssuerAuthenticationManagerResolver and provide my own AuthenticationManagerResolver<String>? If my code above is compiling, why do I need to do this?
The JwtDecoder is required if you've configured the resource server with a JwtAuthenticationProvider (because it requires a specific JwtDecoder). This would happen if you do for example:
http
...
.oauth2ResourceServer(oauth2 -> oauth2
.authenticationManagerResolver(authenticationManagerResolver)
.jwt(Customizer.withDefaults())
)
Since the authenticationManagerResolver is an alternative that branches at the AuthenticationManager level, you don't want to use a JwtAuthenticationProvider. It will be used internally by the JwtIssuerAuthenticationManagerResolver.
Remove .jwt() in that case to prevent the configurer from wiring one up.
Update
The section in the docs on Dynamic Tenants gives some more info on various customization options.
In your case, without the use of .jwt() you cannot as easily wire in a JwtAuthenticationConverter that can customize the returned granted authorities.
The JwtIssuerAuthenticationManagerResolver is internally using a TrustedIssuerJwtAuthenticationManagerResolver. This is what performs the multi-tenancy capability, by extracting an issuer claim from the JWT, and creating a JwtDecoder + new JwtAuthenticationProvider(jwtDecoder) based on the matched issuer.
In order to customize the JwtAuthenticationProvider, you will have to re-implement this class so you can inject your JwtAuthenticationConverter into each created instance. You will implement AuthenticationManagerResolver<String> to do this. Call it CustomTrustedIssuerJwtAuthenticationManagerResolver (see this line).
You just need to provide that to the JwtIssuerAuthenticationManagerResolver, like this:
String[] issuers = new String[] {"https://www.example.com/auth/realms/example"};
AuthenticationManagerResolver<String> authenticationManagerResolver =
new CustomTrustedIssuerJwtAuthenticationManagerResolver(issuers);
JwtIssuerAuthenticationManagerResolver jwtIssuerAuthenticationManagerResolver =
new JwtIssuerAuthenticationManagerResolver(authenticationManagerResolver);
...
Below code throw error of unresolvable circular reference. when Spring is trying to create bean of MessageFormatHelper class.This error throw while I run the jar. I try to see but not getting what is reason. Could anyone help.
First class,
#Component
class DbTemplateResolver extends TemplateResolver {
#Autowired
SpringTemplateEngine templateEngine;
....othercode
#PostConstruct
public void extension() {
templateEngine.addTemplateResolver(this);
}
...other code
}
Second class,
#Component
class MessageFormatHelper{
#Autowired
SpringTemplateEngine templateEngine;
... other code
String getMessage()
{
final Context ctx = new Context(locale);
ctx.setVariable("contractMap", model.get(ContractMap.TEMPLATE_MODEL_MAP_KEY));
mergedMessage = templateEngine.process(fileName, ctx);
}
}
Full error:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.flex.eventManagement.handler.helper.MessageFormatHelper com.flex.eventManagement.handler.helper.NotificationPreProcessor.messageFormatHelper; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageFormatHelper': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.thymeleaf.spring4.SpringTemplateEngine com.flex.eventManagement.handler.helper.MessageFormatHelper.templateEngine; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafDefaultConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private final java.util.Collection org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafDefaultConfiguration.templateResolvers; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dbTemplateResolver': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.thymeleaf.spring4.SpringTemplateEngine com.flex.eventManagement.handler.helper.DbTemplateResolver.templateEngine; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'templateEngine': Requested bean is currently in creation: Is there an unresolvable circular reference?
Updated
Remove template engine auto-wire from DbTemplateResolver class. Autowire into MessageFormatHelper.java class like below
class MessageFormatHelper{
#Bean
public DbTemplateResolver dbTemplateResolver() {
DbTemplateResolver resolver = new DbTemplateResolver();
resolver.setOrder(2);
return resolver;
}
#Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolvers(Sets.newHashSet(dbTemplateResolver()));
return engine;
}
}
Also I need to remove SpringTemplateEngine auto wired from MessageFormatHelper right? so how can I call mergedMessage = templateEngine.process(fileName, ctx);?
Update 2.
should followed required in MessageFormatHelper
#Autowired
DbTemplateResolver dbTemplateResolver;
#Autowired
SpringTemplateEngine templateEngine;
#PostConstruct
public void extension() {
templateEngine.addTemplateResolver(dbTemplateResolver);
}
Your code is flawed and it seems you lack basic understanding of how Spring works and how you should use Spring to configure things.
First you are using Spring Boot and want to use Thymeleaf. This is simply done by added the spring-boot-starter-thymeleaf as a dependency for you project. (I assume you have already done this).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf<artifactId>
</dependency>
Spring Boot detects the fact that you have Thymeleaf on your classpath and the ThymeleafAutoConfiguration will kick in and configure the SpringTemplateEngine for you. It will even detect every bean of the type ITemplateResolver, which I assume your DbTemplateResolver implements.
The only thing to have your DbTemplateResolver add is to add a #Bean method to an #Configuration (or your application) class.
#Bean
public ITemplateResolver dbTemplateResolver() {
return new DbTemplateResolver();
}
Spring will detect it and inject it into the automatically configured SpringTemplateEngine.
The only thing you need to do is in classes you need the SpringTemplateEngine you need to auto wire it. Just use the super class not the concrete type.
#Autowired
private TemplateEngine templateEngine;
Don't mess around with trying to configure it later on, use the framework for that.
I want to implement integration test for my spring security kerberos authentication.
There is KerberosRestTemplate (reference) for this purpose. KerberosRestTemplate has got a default constructor with description "Leave keyTabLocation and userPrincipal empty if you want to use cached ticket".
For research i wrote a trivial class:
public static void main(String[] args) {
KerberosRestTemplate krt = new KerberosRestTemplate();
String result = krt.getForObject("http://testserver.testad.local:8080/", String.class);
System.out.println(result);
}
When i run it, exception has thrown:
Exception in thread "main" org.springframework.web.client.RestClientException: Error running rest call; nested exception is java.lang.IllegalArgumentException: Null name not allowed
at org.springframework.security.kerberos.client.KerberosRestTemplate.doExecute(KerberosRestT
emplate.java:196)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:237)
at edu.mezlogo.Application.main(Application.java:9)
Caused by: java.lang.IllegalArgumentException: Null name not allowed
at sun.security.krb5.PrincipalName.<init>(Unknown Source)
at sun.security.krb5.PrincipalName.<init>(Unknown Source)
at javax.security.auth.kerberos.KerberosPrincipal.<init>(Unknown Source)
at javax.security.auth.kerberos.KerberosPrincipal.<init>(Unknown Source)
at org.springframework.security.kerberos.client.KerberosRestTemplate.doExecute(KerberosRestT
emplate.java:182)
... 3 more
My klist contain correct cached ticket, for my service.
#2> Client: deniz # TESTAD.LOCAL
Server: HTTP/testserver.testad.local # TESTAD.LOCAL
KerbTicket Encryption Type: RSADSI RC4-HMAC(NT)
Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
Start Time: 2/5/2016 6:17:39 (local)
End Time: 2/5/2016 16:16:32 (local)
Renew Time: 2/12/2016 6:16:32 (local)
Session Key Type: RSADSI RC4-HMAC(NT)
And my browser (firefox) has successful authenticated with kerberos sso.
I use Windows server 2012. And Windows 7 as client.
How to use cached ticket? (And does ktpass can generate client keytab?)
P.s. sorry for my English.
You are checking the Windows credentials cache - while Java is maintaining it's separate. In order to view the Java's credentials cache you should execute the klist command from your JRE/bin folder
I am using cxf-client grails plugin for SOAP request:
I have added these client configuration into Config:
cxf {
client {
paymentRequestClient {
wsdl = "https://sandbox.demo.com/payments.wsdl"
namespace = "com.demo.soap"
clientInterface = com.demo.soap.PaymentPortType
serviceEndpointAddress = "https://sandbox.demo.com/payments.php"
}
}
}
After running grails wsdl2java grails has generated the required Java classes against the provided WSDL except for PaymentPortType class.
On running the application, I am getting this error:
Web service client paymentRequestClient cannot be created before setting the clientInterface=[:] and serviceEndpointAddress=https://sandbox.demo.com/payments.php properties
[ERROR] 2015-10-27 12:00:11,899 CxfClientGrailsPlugin - Web service client paymentRequestClient cannot be created before setting the clientInterface=[:] and serviceEndpointAddress=https://sandbox.demo.com/payments.php properties
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security REST ...
... finished configuring Spring Security REST
[ERROR] 2015-10-27 12:00:39,401
org.codehaus.groovy.grails.web.context.GrailsContextLoaderListener - Error initializing the application: Error creating bean with name 'paymentRequestClient': Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Class' for property 'clientInterface'; nested exception is java.lang.IllegalArgumentException: Could not load class []!
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentRequestClient': Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Class' for property 'clientInterface'; nested exception is java.lang.IllegalArgumentException: Could not load class []!
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Class' for property 'clientInterface'; nested exception is java.lang.IllegalArgumentException: Could not load class []!
... 4 more
Caused by: java.lang.IllegalArgumentException: Could not load class []!
... 4 more
What could be the possible issue?
You have this issue because Spring can't find the class com.demo.soap.PaymentPortType in the classpath