I am using OAuth2RestTemplate in order to pass an oauth token with REST requests. However, I now need to hardcode my urls, such as
restTemplate.postForLocation("http://localhost:5555/other-service2/message", "Message")
whereas when I was using a self created Ribbon Annotated(using #LoadBalanced) RestTemplate bean, I can do something like
restTemplate.postForLocation("http://service1/other-service2/message", "Message")
This is because when you use LoadBalanced, it will automatically make it a Ribbon Rest Template which lets you use the Service Discovery features or Eureka, but when you annotate a OAuth2RestTemplate bean with #Loadbalanced, it would throw some sort of error at runtime when trying to use the OAuth2RestTemplate, which says
o.s.b.a.s.o.r.UserInfoTokenServices : Could not fetch user details: class java.lang.IllegalStateException, No instances available for localhost
My OAuth2RestTemplate creation looks like
#LoadBalanced
#Bean
public OAuth2RestTemplate restTemplate(final UserInfoRestTemplateFactory factory) {
final OAuth2RestTemplate userInfoRestTemplate = factory.getUserInfoRestTemplate();
return userInfoRestTemplate;
}
How can I use the Service Discovery features as well as load balancing features of Eureka's Ribbon, on an OAuth2RestTemplate?
I think this is something you might try.
In my project we also use OAuth2, Eureka, Ribbon for microservices to communicate each other. In order to use Ribbon with OAuth2, the approach we took was bit different.
First we leave the restTemplate untouched.
#LoadBalanced
#Bean
public RestTemplate restTemplate() {
However, we created FeignClientIntercepter implementing RequestIntercepter which sets authorization tokens for OAuth when making a request via restTemplate.
#Component
public class UserFeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Jwt";
#Override
public void apply(RequestTemplate template) {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null && authentication
.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication
.getDetails();
template.header(AUTHORIZATION_HEADER,
String.format("%s %s", BEARER_TOKEN_TYPE, details.getTokenValue()));
}
}
}
And If you try to create spring msa project, I would prefer using Feign-client rather than restTemplate.
#FeignClient("your-project-name")
public interface YourProjectClient {
#GetMapping("your-endpoint")
JsonObject getSomething();
Related
I am using Spring Boot 1.5.x (Spring 4.2.x), and I created a RestClientSdk spring component class as shown here:
#Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
#Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//other methods kept out for brevity
}
I have also defined a DefaultRestTemplateCustomizer spring component as shown here:
#Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
#Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}
With that, I've defined a test class as shown below that uses the #RestClientTest annotation as shown below.
#RunWith(SpringRunner.class)
#RestClientTest(RestClientSdk.class)
#ActiveProfiles("test")
/*
* The RestClientTest only includes the most minimal configuration to include a rest template builder,
* so we include the rest sdk auto config within the scope of the test
*/
#ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
#Autowired
private RestClientSdk restClientSdk;
#Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* A simple Http Get that retrieves a JSON document from a rest server and
* produces a plain old java object as a response.
*/
#Test
public void testPlainHttpGet() throws IOException{
//ARRANGE
RestClientDto<?> simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
.andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"),MediaType.APPLICATION_JSON));
//ACT
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//ASSERT
mockRestServiceServer.verify();
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//not including other methods for brevity
}
PROBLEM
Because the MockRestServiceServer builder overrides the BufferingClientHttpRequestFactory in my rest template with a MockClientHttpRequestFactory, I am getting a null response from my body. This is because the logging interceptor is reading the input stream coming from the response and as such the stream no longer has content to read. The BufferingClientHttpRequestFactory would prevent that from happening. Now, I know that as of Spring 5.0.5, there is an extra option in the MockRestServiceServer builder called bufferContent, but I don't have the option of moving to Spring 5.x (Spring Boot 2.x), so I was wondering if there is a way to get this configured using Spring Boot 1.5.x / Spring 4.2.x.
I thank you in advance!
Juan
In order to workaround the issue, I needed to define a test configuration, that would allow me to override the client request factory. Please see the code below. It is a bit hacky, but I suppose the real solution here would be to upgrade to Spring 5.x / Spring Boot 2.x.
#Configuration
#Profile("test")
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.
* #param restClientSdk The rest client SDK containing the rest template to use.
* #return The mock rest service server to use.
*/
#Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}
I'm developing a generic web client wrapper which should send requests to a OAuth2 protected resource. My idea was to implement a ClientWrapper which holds a WebClient. The ClientWrapper offers methods to access the resource like getFile(String registrationId). I thought I could register different ClientRegistration at runtime and obtain them with the WebClient.
So far I've registered a WebClient as follows:
ClienRegistration Bean:
#Bean
public ClientRegistration clientRegistration() {
ClientRegistration.Builder clientRegestrationBuilder = ClientRegistration
.withRegistrationId(CLIENT_REGISTRATION_ID);
clientRegestrationBuilder.clientId(CLIENT_ID);
clientRegestrationBuilder.clientSecret(CLIENT_SECRET);
clientRegestrationBuilder.tokenUri(TOKEN_ENDPOINT);
clientRegestrationBuilder.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS);
return clientRegestrationBuilder.build();
}
In memory holder:
#Bean
public ReactiveClientRegistrationRepository clientRegistrations(List<ClientRegistration> clientRegistrations) {
return new InMemoryReactiveClientRegistrationRepository(clientRegistrations);
}
Web Client Builder
#Bean(OAUTH2_WEB_CLIENT)
public WebClient oauthFilteredWebClient(ReactiveClientRegistrationRepository clientRegistrationRepository) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
return WebClient.builder().filter(oauth).build();
}
I've injected it into my ClientWrapper:
public ClientWrapper(WebClient webClient) {
this.webClient = webClient;
}
#Override
public File getFile(String regId) {
Mono<File> result = webClient.get().uri("https://somepath") //
.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction
.clientRegistrationId(regId))
.retrieve() //
.bodyToMono(File.class);
return result.block();
}
Doing it this way it is very static and I'm only able to use exactly one OAuth2 client (clientid/client-secret and token endpoint). With the idea providing a registration id it would be needed to register n ClientRegistration objects at runtime which I think is not possible and doesn't feel right... Is there a nice way to pass a "OAuthCredentials" object to a WebClient dynamically which is used for fetching a token?
I have a spring boot app that uses spring security.I implemented form based auth and it works well. I want the app to serve as the backend of an angular app i've built.I know about CORS but how can i add JWT auth to the existing spring boot app,is it recommended.
I would recommend AOP concept to authenticate/validate your jwt token.
First thing you need to create a custom annotation. Named it as JWTsecured
#Component
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface JwtSecured {
}
Now You have a controller in which your api need to be JWT secured.
#RestController
public class YourController {
#GetMapping(value="/testApi")
#JWTsecured
public void isTestApi() {
}
}
Now you have to write an aspect to validate your token...
#Component
#Aspect
public class JWTsecuredAspect {
#Around(value =" #within(com.JWTSecured) || #annotation(com.JWTSecured)")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
String token = request.getHeader("Authorization");
if(!isTokenValidated(token)){
throw CustomException("Invalid Token.")
}
}
}
This is how you can use it along with auth.
There are several other ways.
Feel free to contact
I have a bunch of intermediate and core services within my application. All services are Spring Boot and using Netflix Library. When a user requests information, the request will/might pass other services in the chain eg:
Client <-> Zuul <-> Service B <-> Service A
I have configured all services (A and B) to be ResourceServer so that every access needs to be authenticated. When requesting an access token (From a Spring Security Server) and use it to request information directly from Service A, everything works fine. When I use the same token to access information from Service B (which needs Service A down the line) I get an "HTTP 401: Full authentification is required" error. Service B uses a FeignClient to call Service A.
After some debugging, I found out, that the Authorization-Header is not passed from Service B to Service A. Service B checks the token itself correctly, grants access to the method and tries to perform the request of Service A.
I tried a RequestInterceptor but without any success (Error "Scope 'request' is not active for the current thread")
#Component
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Bearer";
private final OAuth2ClientContext oauth2ClientContext;
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext) {
Assert.notNull(oauth2ClientContext, "Context can not be null");
this.oauth2ClientContext = oauth2ClientContext;
}
#Override
public void apply(RequestTemplate template) {
if (template.headers().containsKey(AUTHORIZATION_HEADER)) {
...
} else if (oauth2ClientContext.getAccessTokenRequest().getExistingToken() == null) {
...
} else {
template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE,
oauth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));
}
}
}
This is an example proxy function that uses the FeignClient:
#Autowired
private CategoryClient cat;
#HystrixCommand(fallbackMethod = "getAllFallback", commandProperties = {#HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2") })
#GetMapping("/category")
public ResponseEntity<List<Category>> getAll() {
try {
ResponseEntity<List<Category>> categories = this.cat.getAll();
...
return categories;
} catch(Exception e) {
...
}
}
Is there any working solution to pass the Authorization-Header from the proxy function to the FeignClient so that Service A will receive the header and can do its own auth check with it?
Found a working solution. I still don't know if this is the "best" way to do it and if anyone got a better solution I'd be happy if you share it. But for now, this is working as expected:
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
requestTemplate.header("Authorization", "Bearer " + details.getTokenValue());
}
};
}
Right now, I'm learning about implementing REST API with a Spring Security Framework.
My question is, after success login with spring security, how can i send the request to server and make sure the server know that i am have been authorized (already login with success)?
I have a some experiment code to do testing
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { WebAppConfig.class, SecurityConfig.class })
public class TheTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private FilterChainProxy filterChainProxy;
protected MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders//
.webAppContextSetup(wac)//
.addFilter(filterChainProxy)//
.build()//
;
}
#Test
public void testDoingArequest() throws Exception {
// login here
HttpSession session = mockMvc.perform(//
//
post("/login-process")//
.param("username", "theusername")//
.param("password", "thepassword")//
)//
.andDo(print())//
.andExpect(status().isFound())//
.andReturn().getRequest().getSession()//
;
// login is success and now trying to call request
this.mockMvc.perform(//
get("/doingRequest")//
.session((MockHttpSession) session)// <-- where this part must added to?
)//
.andExpect(status().isOk())//
.andDo(print())//
;
}
}
-
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()//
.antMatchers("/doingRequest").authenticated()//
.anyRequest().permitAll()//
.and()//
.csrf().disable()//
.formLogin()//
.loginPage("/")//
.loginProcessingUrl("/login-process")//
.defaultSuccessUrl("/");
}
-
#Controller
public class TheController {
#RequestMapping(value = "doingRequest", method = RequestMethod.GET)
#ResponseBody
public String doingSomething() {
return "Only authorized user can read this";
}
}
-
Above code is running well but i dont know how to implementing the "session" part in HTTP. I'm expecting something like put a token or something in header or url in real life application/implementation not in the testing environment. How the client get the token? How do we call the request (with token embedd) in client code.?
Are you looking for mocking a session object.If yes then you need to import the mock session object, and in the test class you can create and use the object.
import org.springframework.mock.web.MockHttpSession;
MockHttpSession session = new MockHttpSession();
session.setAttribute("variable", object);
The configuration you have will use the server side session to maintain the security context, and the link with the client is the standard servlet JSESSIONID cookie, so this has nothing to do with Spring Security. Whether you actually want a session or not will depend on the nature of your client. If there is no state maintained between the client and server, then each request from the client must be separately authenticated/authorized. This might be done using Basic authentication for example, or something like an OAuth2 access token depending on your requirements.