How can I configure a spring boot config client microservice to fetch its configuration from an OAuth2 configServer which is #EnableResourceServer ?
I have one OAuth2 Authorization server (#EnableAuthorizationServer). There is a configServer (#EnableConfigServer) which I have configured to respond only to valid requests authorized by authorization server containing JWT tokens.
There is also a microservice client APP1 of the config server which needs to fetch its configuration upon startup from the aforementioned config server. Since the server only responds to requests containing valid access tokens (jwt tokens) I tried to inject OAuth2RestTemplate into ConfigServicePropertySourceLocator so that my config client (APP1) could fetch its config.
In order to do that I tried the partial solution which was discussed here.
This is my OAuth2 ready RestTemplate that I want to inject
#Configuration
public class OAuthConfig {
#Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details) {
return new OAuth2RestTemplate(details, oauth2ClientContext);
}}
and this is my custom property locator
public class CustomConfigServicePropertySourceLocator {
#Autowired
private ConfigurableEnvironment environment;
#Autowired
private RestTemplate restTemplate;
#Bean
public ConfigClientProperties configClientProperties() {
ConfigClientProperties client = new ConfigClientProperties(this.environment);
client.setEnabled(false);
return client;
}
#Bean
#Primary
public ConfigServicePropertySourceLocator configServicePropertySourceLocator() {
ConfigClientProperties clientProperties = configClientProperties();
ConfigServicePropertySourceLocator configServicePropertySourceLocator = new ConfigServicePropertySourceLocator(
clientProperties);
configServicePropertySourceLocator.setRestTemplate(restTemplate);
return configServicePropertySourceLocator;
}}
I Followed the instructions in Customizing the Bootstrap Configuration
I created a META_INF > spring.factories file containing
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.company.mcapp.CustomConfigServicePropertySourceLocator
By debugging I can see that this custom locator will get called but my APP1 is failing to contact config server to fetch the configurations.
In initialize method of PropertySourceBootstrapConfiguration (below) I can see that the propertySourceLocators does not contain my CustomConfigServicePropertySourceLocator.
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
CompositePropertySource composite = new CompositePropertySource(
BOOTSTRAP_PROPERTY_SOURCE_NAME);
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
PropertySource<?> source = null;
source = locator.locate(environment);
if (source == null) {
continue;
}
logger.info("Located property source: " + source);
composite.addPropertySource(source);
empty = false;
}
.
.
.
UPDATE: The issue was a silly mistake That I made. Instead of creating META-INF I created META_INF.
Related
I am getting Unable to connect to Redis;nested exception is io.lettuce.core.RedisConnectionException using RedisTempalte error when i try to connect azure redis cache from my spring reactive application.
I have configured below in properties file
spring.redis.host=hostName
spring.redis.port=6379
spring.redis.password=password
also tried java based configuration using LettuceConnectionFactory
I have encountered the same problem when enabling password authentication in AWS ElastiCache because of not using SSL for the Redis client.
Bellow RedisConfig.java uses SSL to connect and my error disappeared. I am using Spring Reactive by the way.
#Configuration
#ConfigurationProperties(prefix = "spring.redis")
#Setter
public class RedisConfig {
private String host;
private String password;
#Bean
#Primary
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory(RedisConfiguration defaultRedisConfig) {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.useSsl().and()
.commandTimeout(Duration.ofMillis(60000)).build();
return new LettuceConnectionFactory(defaultRedisConfig, clientConfig);
}
#Bean
public RedisConfiguration defaultRedisConfig() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPassword(RedisPassword.of(password));
return config;
}
}
I want to get different token per browser tap to get differents roles and user logged against oauth server in the same browser
I have requested oauth login against own oauth server. I develop own client using Spring security Oauth2...
OAUTHCONF.JAVA
Spring security client...
/**
* Called after executed Configuration "addFilterBefore"
*
* #return OAuth2ClientAuthenticationProcessingFilter
*/
private OAuth2ClientAuthenticationProcessingFilter oauthFilter() {
OAuth2ClientAuthenticationProcessingFilter oauthFilter = new OAuth2ClientAuthenticationProcessingFilter("/login");
// OAuth2RestTemplate > Spring Boot does not automatically create such a bean,
OAuth2RestTemplate oauthTemplate = new OAuth2RestTemplate(oauth(), oauth2ClientContext);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(oauthResource().getUserInfoUri(), oauth().getClientId());
tokenServices.setRestTemplate(oauthTemplate);
oauthFilter.setRestTemplate(oauthTemplate);
oauthFilter.setTokenServices(tokenServices);
return oauthFilter;
}
oauth configuration...
#Autowired
private OAuth2ClientContext oauth2ClientContext;
#Bean
#ConfigurationProperties("oauth.resource")
public ResourceServerProperties oauthResource() {
return resourceServerProperties;
}
#Bean
#ConfigurationProperties("oauth.client")
public AuthorizationCodeResourceDetails oauth() {
return authorizationCodeResourceDetails;
}
INDEX.HTML
View...
if (oauth2ClientContext!=null && oauth2ClientContext.getAccessToken()!=null) {
labelInformation.setText("Access token > " + oauth2ClientContext.getAccessToken());
}
In this mode, in the first tap I have requested the token. In the second tab shows me directly token... Is there a way to request different token via Spring security per tab?
Introduction
I would like to be able to have two different spring profiles, and depending on the profile to change to a hardcoded address for our feign builders.
Currently was have the following:
return builder.target(cls, "http://" + serviceName);
But I would actually like to do the following and over-ride the address:
return builder.target(cls, "http://our-server:8009/" + serviceName);
Why
Sometimes we don't want to run all the services within our development environment. Additionally, some of the services are only available through a zuul gateway sometimes.
So we run the same code in different situations and conditions.
Technical Details
We have the following code that we use for building our Feign Clients.
We had been using the #FeignClient annotation in the past, but lately we decided to start building our feignClients manually.
Example below:
#FeignClient(name = "ab-document-store", configuration = MultiPartSupportConfiguration.class, fallback = DocumentStoreFallback.class)
We call the feignRegistrar class with the following command:
return registerFeignClient(DocumentStoreClient.class, true);
#RequiredArgsConstructor
//#Component
#Slf4j
public class FeignRegistrar {
#Autowired
private Decoder decoder;
#Autowired
private Encoder encoder;
#Autowired
private Client client;
#Autowired
private Contract feignContract;
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Autowired
private List<RequestInterceptor> interceptors;
public <T> T register(Class<T> cls, String serviceName, boolean isDocumentStore) {
if(isDocumentStore){
encoder = new MultipartFormEncoder(new SpringEncoder(messageConverters));
}
//Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(encoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
for(RequestInterceptor interceptor : interceptors) {
builder.requestInterceptor(interceptor);
}
log.debug("Registering {} - as feign proxy ", serviceName);
return builder.target(cls, "http://" + serviceName);
}
public static class Slf4Logger extends Logger {
#Override
protected void log(String configKey, String format, Object... args) {
log.info("{} - {}", configKey, args);
}
}
}
Spring Cloud Property Over-ride
We have also been using property files such as application-ENV.property with entries such as the following:
ab-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
ab-document-store.ribbon.listOfServers: localhost:8025
Unfortunately, listOfServers is not enough for us. We would like to be able to assign a directory/path as well. Something like:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
Otherworkaround
I have thought about sneaking in a header into all requests such as X-SERVICE-NAME using a feign interceptor. Then we could point all services to an address (e.g. localhost:9001) , and forward/proxy those requests to localhost:9001/X-SERVICE-NAME.
However, I would prefer a much easier solution such as:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
But this doesn't work :(
Introduction
I found a solution for this using a proxy that detects a header.
So, I have a feign interceptor on the java-side that attaches a header x-service-name to every feign-request.
I also have a NodeJS proxy, that analyzes requests, finds x-service-name, and re-writes the requests to become: x-service-name/originalRequestPath.
This allows me to have all the microservices behind a zuul gateway but also access them using a eureka-over-ride.
Java-Feign-Interceptor
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(usedEncoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
NodeJS proxy
In the example, my zuul gateway ( or another proxy ) is on localhost:9001.
I'm listening on localhost:1200 .
let enableProxyForJava = process.env.ENABLE_PROXY_FOR_JAVA;
if (enableProxyForJava != undefined && enableProxyForJava.toLowerCase() === 'true') {
var httpProxyJava = require('http-proxy');
var proxyJava = httpProxyJava.createProxy();
gutil.log( gutil.colors.green('Enabling Proxy for Java. Set your Eureka overrides to localhost:1200.') );
require('http').createServer(function(req, res) {
console.log("req.headers['x-service-name'] = " + req.headers['x-service-name']);
console.log("Before req.url:"+ req.url);
if( req.headers['x-service-name'] != undefined){
let change = req.headers['x-service-name'] +req.url;
console.log("After req.url:"+ change);
req.url = change;
}
proxyJava.web(req, res, {
target: 'http://localhost:9001/'
});
}).listen(1200);
}
Property file inside Java Application that has feign clients
mbak-microservice1.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice1.ribbon.listOfServers: localhost:1200
mbak-microservice2.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice2.ribbon.listOfServers: localhost:1200
mbak-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-document-store.ribbon.listOfServers: localhost:1200
In the Rest template I was able to to do the following. I'm trying to implement the same using spring webclient and so far I couldn't find any documentation on how to set this up.
#Bean
public OAuth2RestTemplate createRestTemplate() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientID);
resource.setClientSecret(clientSecret);
resource.setGrantType("password");
resource.setScope(Arrays.asList(scope));
resource.setUsername("user");
resource.setPassword("pass");
return new OAuth2RestTemplate(resource);
}
I wrote a simple SOAP endpoint essentially following the Spring tutorial found here: https://spring.io/guides/gs/producing-web-service/
Below is the class which is used to intercept the requests (assume repository object injected):
#Endpoint
public class SampleEndpoint {
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "SampleRequest")
public
#ResponsePayload
JAXBElement<SampleResponseType> sampleQuery(
#RequestPayload JAXBElement<SampleRequestType> request) {
ObjectFactory factory = new ObjectFactory();
SampleResponseType response = repository.query(request.getValue());
JAXBElement<SampleResponseType> jaxbResponse = factory.createSampleResponse(response);
return jaxbResponse;
}
}
The service performs correctly. One issue I'm running into is performance, particularly with unmarshalling the response. On average it's taking seconds to unmarshall the object into an XML response. Is there a way to cache/inject the JaxbContext Spring is using for this process to improve on this time?
Here's the web service configuration file I'm using for this endpoint. I've tried alternating between Saaj and Axiom message factories but didn't see much performance change:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public SaajSoapMessageFactory soap12MessageFactory() {
SaajSoapMessageFactory factory = new SaajSoapMessageFactory();
factory.setSoapVersion(SoapVersion.SOAP_12);
return factory;
}
#Bean
public AxiomSoapMessageFactory axiomSoapMessageFactory() {
AxiomSoapMessageFactory factory = new AxiomSoapMessageFactory();
factory.setSoapVersion(SoapVersion.SOAP_12);
factory.setPayloadCaching(false);
return factory;
}
#Bean
public ServletRegistrationBean dispatcherServlet(
ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
servlet.setMessageFactoryBeanName("soap12MessageFactory");
return new ServletRegistrationBean(servlet, "/ws/*");
}
#Bean(name = "wsdlname")
public DefaultWsdl11Definition xcpdDefaultXcpdWsdl11Definition(XsdSchema
schema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setCreateSoap11Binding(false);
wsdl11Definition.setCreateSoap12Binding(true);
wsdl11Definition.setPortTypeName("xcpdPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition
.setTargetNamespace("http://somenamespace.org/");
wsdl11Definition.setSchema(schema);
return wsdl11Definition;
}
#Bean
public XsdSchema schema() {
return new SimpleXsdSchema(
new ClassPathResource(
"schema.xsd"));
}
}
We obtained a solution to the issue two days ago. The first issue we saw was that the client version of the JVM was installed on the server. I installed the server build of the JVM and changed Tomcat to use that instance. The performance of some of the web services improved dramatically. Previously simple requests were taking three seconds, now they are taking 250 ms. However when testing the Spring service I wrote, I didn't see much of an improvement.
To resolve this last issue, I added the following to the Java options for Tomcat:
-Dcom.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.fastBoot=true
After restarting the Tomcat instance, the response times of the services are now under one second each. Previously requests were taking up to 30 seconds to complete. The server JRE we used is the following:
http://www.oracle.com/technetwork/java/javase/downloads/server-jre8-downloads-2133154.html