I am trying to write an OAuth2 Client SpringBoot app that :
Does NOT require a web container ( no Tomcat nor Jetty ) ...
To basically send an Authorization bearer header ( either opaque or JWT bearer token ) in an HTTP request to another SpringBoot app that acts an OAuth2 resource server.
Now looking at the documentation here :
https://docs.spring.io/spring-security/reference/5.7/servlet/oauth2/client/core.html#oauth2Client-client-registration-repo
.. it says that both OAuth2AuthorizedClientService and ClientRegistrationRepository should be auto-wired automatically:
#Autowired
private OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
#Autowired
private ClientRegistrationRepository clientRegistrationRepository;
presumably by just having :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
But it turns out that I also need :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
for the auto-wiring to work. Otherwise, I get :
Field oAuth2AuthorizedClientService in org.example.oauth2client.FeignConfiguration required a bean of type 'org.springframework.security.oauth2.client.OAuth2AuthorizedClientService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.client.OAuth2AuthorizedClientService' in your configuration.
So question is, why is spring-boot-starter-web needed to have the auto-wiring to work ?
I don't want to add a web container as the OAuth2 client SpringBoot app does not require it ( e.g. command-line app ) but needs to consume a REST service from another SpringBoot app running as an OAuth2 resource server.
OK ... I found the answer, though not what I was expecting.
DefaultOAuth2AuthorizedClientManager is asserting and expecting a HttpServletRequest:
https://github.com/spring-projects/spring-security/blob/5.7.x/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/DefaultOAuth2AuthorizedClientManager.java#L144
So the spring security's OAuth2 client only works in the context of where the client app is running in a web server container.
It looks like I then cannot use spring-boot-starter-oauth2-client for apps that are not running in a web server container. ( e.g. command line or batch application ), but why ???
In this case, what options do we have ?
Related
My SpringBoot application has following keycloak dependency to connect to the Keycloak server. I used this tutorial for the setup.
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
The application works fine, the problem is however with e2e Tests. I use following code for e2e tests
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
class ServerIntegrationTests {
#Autowired
TestRestTemplate restTemplate;
// ...
}
For authorization-server mocking I use following lib:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-keycloak</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
This lib however seems to work only with #MockMvc, but not with real HTTP-Calls, i.e. #TestRestTemplate.
So my questions are:
Does com.c4-soft.springaddons only support #MockMvc context?
If so, what are the other possibilities to test whole application (without mocking servlet container) with mocked authorization-server (keycloak)?
I have tried following lib, but it does not work with keycloak-spring-boot-starter:
<dependency>
<groupId>no.nav.security</groupId>
<artifactId>mock-oauth2-server</artifactId>
<version>0.5.1</version>
<scope>test</scope>
</dependency>
Does com.c4-soft.springaddons only support #MockMvc context?
No it works without MockMvc. It supports WebTestClient too or just testing secured #Component outside of any HTTP request (#Repository and #Service for instance).
But its usage is limited to unit-testing only. Here is why
I'm not sure you can mock authorization-server: spring-security validates the JWT bearer against its issuer and for that the token must be valid and signing key accessible from configured issuer (unless you modify security filters, but then your test wont cover production security anymore).
You might have to use a dockerised Keycloak or something. After all, aren't you writing end-to-end test for a system which includes an authorization-server?
Your tests will first issue a POST request to Keycloak to be delivered a valid access-token and then add it as authorization header to the test requests sent to your resource-server(s).
P.S.
keycloak-spring-boot-starter is deprecated: https://github.com/keycloak/keycloak/discussions/10187
You can have a look at those tutorials for an alternative
I m working on a customized health check for an API. Where I m using :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.5.5</version>
</dependency>
and implementing the HealthIndicator interface.
I m wondering if there is a way to get access to the payload returned by /actuator/health ?
so I can desterilize it, and base on the values on the object (payload), I will build the implementation for the heath() method from HealthIndicator Interface.
I have tried to use:
- HealthEndpoint but it needs an implementation for HealtContributorRegistary , not sure how to do that.
- SystemHealth (im getting the error : class Cannot be accessed from outside package).
any help is very welcomed.
Thank you.
I was able to get access to all my API components status by
#Autowired HealthContributor[] healthContributors;
in a service.
I have followed this answer : Direct access to Spring Boot Actuator Health beans/data?
I have been trying to collect micrometer metrics in a non springboot application and expose them to prometheus.I have added the following dependency and the test method for the same.I would like to know how to proceed and expose the collected metrics to prometheus from my non spring boot application(traditional spring application).
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.2.0</version>
</dependency>
public string testmetrics(){
private PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
registry.counter("ws metric collection","tktdoc metrics");
String metricsInfo = registry.scrape();
return metricsInfo;
}
You practically have to expose an HTTP endpoint and configure Prometheus with it; the HTTP endpoint will supply the data for the scrapes.
An example showing how to add the HTTP endpoint by starting up an HTTP Server (your application may already be using one) is here.
I have a Camel Project which runs in 8080 port to consume external restful web service which is a SpringBoot project which runs in port 8082, toproduce employee information based on the end point call. Here I'm trying to consume ResetFul webservice using Apache Camel Restlet. While consuming the webservice every alternative call is failing.
Restlet operation failed invoking http://localhost:8082/employeeController/getEmployeeDetails/12?wsdl with statusCode: 400 /n responseBody:<html><body><h1>400 Bad request</h1>Your browser sent an invalid request.</body></html>
This is the error i'm getting on every alternative call.
Restlet code to consume which is written inside Camel Context.
<to id="getEmployeeDetails" pattern="InOut" uri="restlet:http://localhost:8082/employeeManager/getEmployeeDetails/{employeeId}?restletMethod=GET"/>
SpringBoot code which produces webservice,
#Controller
#RequestMapping(value="employeeController")
public class EmployeeController{
#RequestMapping(value="/getEmployeeDetails/{employeeId}", method=RequestMethod.GET,produces=MediaType.APPLICATION_XML_VALUE)
#ResponseBody
public String getEmployeeDetails(#PathVariable("employeeId") int employeeId) {
//getting the employee information from DB
}
}
The Camel Resetlet dependency added in pom.xml is
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-restlet</artifactId>
<version>2.24.1</version>
</dependency>>
Do i need add any other maven dependency in order to work for every endpoint call? Could you please help me here.
The easiest way to consume Restlet Resources is to use the Restlet HTTP Client:
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet.ext.httpclient</artifactId>
<version>2.4.0</version>
</dependency>
With a code similar to this:
Component c = new Component();
Client client = c.getClients().add(Protocol.HTTP);
client.getContext().getParameters().add ( "socketTimeout", "1000" );
Response resp = client.handle(new Request(Method.GET, "https://swapi.co/api/people/1/"));
System.out.println("Output: " + resp.getEntity().getText());
I am having a problem with integrating keycloak into dropwizard. Keycloak requires the RestEasy client so i had to use the dependency :
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.0.26.Final</version>
</dependency>
then I create my httpClient :
RxClient<RxCompletionStageInvoker> httpClient = new JerseyClientBuilder(environment)
.using(configuration.getJerseyClientConfiguration())
.buildRx(getName(), RxCompletionStageInvoker.class);
then i try to use the client, for example :
httpClient
.target(path)
.request()
.get();
and i get the error:
java.lang.ClassCastException: org.jboss.resteasy.client.jaxrs.internal.ClientRequestContextImpl cannot be cast to org.glassfish.jersey.client.ClientRequest
when i remove the dependency I get the JercyClient and all httpRequests works find but Keycloak builder fails, when I use RestEasy dependency keyCloak succeeds but all other http requests fails
Have anyone faced this problem before? is there a way to control when to get the resteasy client and when to get the jersey client?
The solution was to use RestEasy dependency but not using the JersyClientBuilder:
Client httpClient = new ResteasyClientBuilder().build();