Spring Framework 3.2 : How to add basic authentication for all Requests through RestTemplate - spring

I'm working on a Spring framework 3.2 version project. Requirement is to call an external Web-service which requires basic authentication.
Currently, I'm using HttpClient and adding basic authentication into header. but this is being done for each requests. I know in Spring Boot we can achieve that by using RestTemplateBuilder. IS there a way we can maintain a single authentication for all the requests by adding authentication only once?
TIA

You can define a RestTemplate bean with authentication details like this:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.basicAuthorization("user", "secret").build();
}
Updated
An alternative way to do this is by implementing an interceptor. RestTemplate extends the InterceptingHttpAccessor interface which has a setInterceptors() method. You can use it to inject an interceptor to set request headers as needed.
public class MyInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// ... set headers on the request ...
return execution.execute(request, body);
}
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new MyInterceptor()))
return restTemplate
}
Note: untested code, but should work. You can check the docs for more details.
https://docs.spring.io/autorepo/docs/spring-framework/3.2.0.RELEASE/javadoc-api/index.html?org/springframework/web/client/RestTemplate.html

The HttpClient can be built from the HttpClientBuilder.
This builder has setDefaultHeaders(Collection<? extends org.apache.http.Header> defaultHeaders) method which you can set the headers you want. Then when you want an HttpClient, call httpClientBuilder.build().

Related

Create route in Spring Cloud Gateway with OAuth2 Resource Owner Password grant type

How to configure a route in Spring Cloud Gateway to use an OAuth2 client with authorization-grant-type: password? In other words, how to add the Authorization header with the token in the requests to an API? Because I'm integrating with a legacy application, I must use the grant type password.
I have this application:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("route_path", r -> r.path("/**")
.filters(f -> f.addRequestHeader("Authorization", "bearer <token>"))
.uri("http://localhost:8092/messages"))
.build();
}
}
Replacing the <token> with an actual token, everything just works fine.
I found this project that does something similar: https://github.com/jgrandja/spring-security-oauth-5-2-migrate. It has a client (messaging-client-password) that is used to configure the WebClient to add OAuth2 support to make requests (i.e. by adding the Authorization header).
We can't use this sample project right away because Spring Cloud Gateway is reactive and the way we configure things changes significantly. I think to solve this problem is mostly about converting the WebClientConfig class.
UPDATE
I kinda make it work, but it is in very bad shape.
First, I found how to convert WebClientConfig to be reactive:
#Configuration
public class WebClientConfig {
#Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultOAuth2AuthorizedClient(true);
oauth.setDefaultClientRegistrationId("messaging-client-password");
return WebClient.builder()
.filter(oauth)
.build();
}
#Bean
ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.refreshToken()
.password()
.build();
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// For the `password` grant, the `username` and `password` are supplied via request parameters,
// so map it to `OAuth2AuthorizationContext.getAttributes()`.
authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
return authorizedClientManager;
}
private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
return authorizeRequest -> {
Map<String, Object> contextAttributes = Collections.emptyMap();
ServerWebExchange serverWebExchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
String username = serverWebExchange.getRequest().getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
String password = serverWebExchange.getRequest().getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
contextAttributes = new HashMap<>();
// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
}
return Mono.just(contextAttributes);
};
}
}
With this configuration, we can use the WebClient to make a request. This somehow initializes the OAuth2 client after calling the endpoint:
#GetMapping("/explicit")
public Mono<String[]> explicit() {
return this.webClient
.get()
.uri("http://localhost:8092/messages")
.attributes(clientRegistrationId("messaging-client-password"))
.retrieve()
.bodyToMono(String[].class);
}
Then, by calling this one we are able to get the reference to the authorized client:
private OAuth2AuthorizedClient authorizedClient;
#GetMapping("/token")
public String token(#RegisteredOAuth2AuthorizedClient("messaging-client-password") OAuth2AuthorizedClient authorizedClient) {
this.authorizedClient = authorizedClient;
return authorizedClient.getAccessToken().getTokenValue();
}
And finally, by configuring a global filter, we can modify the request to include the Authorization header:
#Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> {
//adds header to proxied request
exchange.getRequest().mutate().header("Authorization", authorizedClient.getAccessToken().getTokenType().getValue() + " " + authorizedClient.getAccessToken().getTokenValue()).build();
return chain.filter(exchange);
};
}
After running this three requests in order, we can use the password grant with Spring Cloud Gateway.
Of course, this process is very messy. What still needs to be done:
Get the reference for the authorized client inside the filter
Initialize the authorized client with the credentials using contextAttributesMapper
Write all of this in a filter, not in a global filter. TokenRelayGatewayFilterFactory implementation can provide a good help to do this.
I implemented authorization-grant-type: password using WebClientHttpRoutingFilter.
By default, spring cloud gateway use Netty Routing Filter but there is an alternative that not requires Netty (https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-netty-routing-filter)
WebClientHttpRoutingFilter uses WebClient for route the requests.
The WebClient can be configured with a ReactiveOAuth2AuthorizedClientManager through of an ExchangeFilterFunction (https://docs.spring.io/spring-security/site/docs/current/reference/html5/#webclient). The ReactiveOAuth2AuthorizedClientManager will be responsible of management the access/refresh tokens and will do all the hard work for you
Here you can review this implementation. In addition, I implemented the client-credentials grant with this approach

Spring cloudFeignClient send header programmatically in RequestInterceptor

i'm working on a spring boot project where i should call a rest api using Feign via Spring Cloud, i can call the rest api using feignClient without any problem,
now the rest api that i call needs a JWT to let me consume it, to send a JWT from my code i used RequestInterceptor and this my code :
class AuthInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
template.header("Authorization", "Bearer eyJraWQiOiJOcTVZWmUwNF8tazZfR3RySDZkenBWbHhkY1uV_1wSxWPGZui-t1Zf2BkbqZ_h44RkjVtQquIe0Yz9efWS6QZQ");
}
}
i put manually the JWT in the code and this work fine ...
my issue is : the JWT expire after 30 min and i should call manually another rest api that generate a JWT then i hardcode it in my code...
my question is : there any solution to call programmatically the api that generate JWT then inject this JWT in the Interceptor?
Thanks in advance.
Best Regards.
Get the Token from the current HttpServletRequest header.
public void apply(RequestTemplate template) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
String jwtToken = request.getHeader(HttpHeaders.AUTHORIZATION);
if (jwtToken != null) {
template.header(HttpHeaders.AUTHORIZATION, jwtToken);
}
}

Spring Cloud RestTemplate add auth token

How to correctly implement restTemplate with authorisation token?
I have a Zuul gateway which passes a JWT downstream to other services correctly, assuming I don't want to do anything on the gateway first, using a config like:
zuul:
sensitive-headers:
routes:
instance-service:
path: /instances/**
strip-prefix: false
And using a token relay filter:
#Component
public class TokenRelayFilter extends ZuulFilter {
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
Set<String> headers = (Set<String>) ctx.get("ignoredHeaders");
headers.remove("authorization");
return null;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 10000;
}
}
Which just forwards everything to the instance-service, works a treat.
However if I remove the routes config from the config.yml file because I want to handle some things on the gateway before manually calling the service I loose the access token and get a 401 back from the downstream services
#ApiOperation(value = "List all instances and their properties.")
#GetMapping("/instances")
public ResponseEntity<String> instances() {
ParameterizedTypeReference<String> reference = new ParameterizedTypeReference<String>() {
};
return restTemplate.exchange("http://instance-service", HttpMethod.GET, null, reference);
}
My RestTemplate is just wired up generically
#Configuration
public class MyConfiguration {
#LoadBalanced
#Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
How do I correctly get the JWT back into the new RestTemplate without having to manually create and add a header in each request?
Am I supposed to be using OAuth2RestTemplate?
After some discussion, it seems like you have two options:
Implement and endpoint and dig the Auth header out via #RequestParam on request. From there, you can add it back on for the subsequent outbound request via RestTemplate to your downstream service.
Use Zuul to proxy your request (Auth header included, make sure its excluded from the sensitive-headers config) and implement a pre filter to include any additional logic you might need.
If I had to pick, it sounds like something Zuul should be doing since it's likely acting as your gateway for both your queue and other services, and it looks like you are trying to implement a proxy request, which Zuul can already do, but it's tough to say without knowing the full scope of the architecture.

How to use Spring WebSessionIdResolver with Spring Security 5 in a Spring webflux application?

Currently I was experiencing the new Spring reactive stack, and want to use reactive features in Spring Session 2.0.
In traditional Servlet approach, Spring Session provides a HttpSessionStrategy to detect session in cookie or request headers. It is easy to use HeaderHttpSessionStrategy to implement a token like authentication(by default the he name is X-AUTH-TOKEN) for RESTful APIs.
Spring 5 core provides a WebSessionIdResolver to do the same thing for Reactive environment.
But when use it with Spring Security and wish it worked as traditional way, I can not get it work.
The SessionConfig file.
#EnableSpringWebSession
public class SessionConfig {
#Bean
public ReactorSessionRepository sessionRepository() {
return new MapReactorSessionRepository(new ConcurrentHashMap<>());
}
#Bean
public WebSessionIdResolver headerWebSessionIdResolver() {
HeaderWebSessionIdResolver resolver = new HeaderWebSessionIdResolver();
resolver.setHeaderName("X-SESSION-ID");
return resolver;
}
}
The partial SecurityConfig.
#EnableWebFluxSecurity
class SecurityConfig {
#Bean
SecurityWebFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeExchange()
.pathMatchers(HttpMethod.GET, "/posts/**").permitAll()
.pathMatchers(HttpMethod.DELETE, "/posts/**").hasRole("ADMIN")
//.pathMatchers("/users/{user}/**").access(this::currentUserMatchesPath)
.anyExchange().authenticated()
.and()
.build();
}
A test rest controller file, it returns the current Session ID.
#RestController
public class SessionRestController {
#GetMapping("/sessionId")
public Map<String, String> sessionId(WebSession session){
Map<String, String> map = new HashMap<>();
map.put("id", session.getId());
return map ;
}
}
When I started up the application, and use curl to access the /sessionId, there is no session info the response header.
curl -v -u "user:password" http://localhost:8080/sessionId
And I got the session id in the query result and put it into request headers to access the protected resources and got 401.
curl -v -X POST -H "X-SESSION-ID:xxxx" http://localhost:8080/posts
Update: A working sample can be found here.
Spring Framework's spring-web module defaults to using it's CookieWebSessionIdResolver, which is based on cookies. If you create an alternative bean of type HeaderWebSessionIdResolver, it will get picked up automatically by Spring Session and switch to a header-based strategy.
In either strategy, it's geared to read the incoming ServerExchange headers, and look up the session id, whether that is reading the Cookie header or the SESSION http header.
These strategies also create response headers, whether that is a set-cookie directive for the client (web browser or your code) to populate the Cookie, or to give you the SESSION header (default name for the HeaderWebSessionIdResolver's header name).

Modify Spring Security Config at Runtime

I am using the latest Spring Boot + Spring Boot Starter Security for a simple proxy application. The goal is to launch the application with a single route/method:
#RequestMapping(value = "/api/register",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<?> register(Registration registration) {
With a security configuration of:
#Override
protected void configure(HttpSecurity http) throws Exception {
this.http = http
.authorizeRequests()
.antMatchers("/api/register").hasAuthority(AuthorityConstants.ADMIN)
.and();
}
public HttpSecurity getHttpSecurity() {
return http;
}
The goal of the application would be to accept registration requests of the form:
{
"route":"/api/foo/bar",
"proxy_location": "http://back-end-server/path/to/resource",
"role": "some-authority"
}
And then the application would add an /api/foo/bar route with a pre-defined method that will proxy (forward) future requests to the backend service.
I know this is a little goofy, the real use-case involves websockets and dynamic creation of topics.
The issue I'm facing is that I cannot seem to update the security configuration after the SecurityConfigurer has completed.
In the code sample above I am caching the HttpSecurity object given to my SecurityConfigurer and then trying to use that object again to configure a new route:
#Inject
private SecurityConfigurer security;
#RequestMapping(value = "/api/register",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<?> allowGetAccounts(Registration registration) {
try {
security.getHttpSecurity()
.authorizeRequests()
.antMatchers(registration.getRoute()).hasAuthority(registration.getRole());
...
} catch (Exception e) {
log.error("Updating security failed!", e);
}
return new ResponseEntity<>(null, HttpStatus.OK);
}
Is there any way to update the security configuration dynamically during runtime?
Also, if anyone has any notes on creating websocket topics dynamically that would be appreciated too!
You have several options:
use antchMatcher("/some/path").access("#someBean.hasAccess(authentication)") . This allows you basically use any bean in your application context to apply the validation you need.
Use #PreAuthorize("#someBean.hasAccess(authentication)") on you RequestMapping annotated method. Same idea as before but as an interceptor on the endpoint itself.
Implement your own SecurityExpressionHandler and plug it into http.authorizeRequests().expressionHandler(...).
Implement your own Security filter that handles whatever you need.

Resources