How to get the sender IP Address in spring integration? - java

I use Spring integration for Web service message handling. Unfortunately the Message does not contains the sender IP Address. How can I get this information?
#Bean
public SimpleWebServiceInboundGateway myInboundGateway() {
SimpleWebServiceInboundGateway simpleWebServiceInboundGateway = new SimpleWebServiceInboundGateway();
simpleWebServiceInboundGateway.setRequestChannelName("testChannel");
simpleWebServiceInboundGateway.setReplyChannelName("testResponseChannel");
return simpleWebServiceInboundGateway;
}
#ServiceActivator(inputChannel = "testChannel", outputChannel = "testResponseChannel")
public Message getHeaders(Message message) {
// how can I reach the sender IP address here
return message;
}

The SimpleWebServiceInboundGateway doesn't map transport headers by default.
See DefaultSoapHeaderMapper.
Of course you can implement your own, but that really might be enough for you to use:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest()
.getRemoteAddr();
in that your target #ServiceActivator.
Of course that will work if you don't shift message to a different thread before the service activator. The RequestContextHolder.currentRequestAttributes() is tied with ThreadLocal.

You can retrieve it from HttpServletRequest, using getRemoteAddr() to get access to user IP address and getHeader() to get header value. This is assuming you can modify your controller class.
Perhaps this will help:
#Controller
public class MyController {
#RequestMapping(value="/do-something")
public void doSomething(HttpServletRequest request) {
final String userIpAddress = request.getRemoteAddr();
final String userAgent = request.getHeader("user-agent");
System.out.println (userIpAddress);
}
}

Related

CXF Ws-Addressing requestContext issue

I'm using java with CXF to create a client with WS-Addressing features:
String url = "http://example.com";
CxfService service = new CxfService(new WSAddressingFeature());
CxfServicePort port = service.getCxfServicePort();
AddressingProperties addressingProperties = new AddressingProperties();
RelatesToType relatesToType = new RelatesToType();
relatesToType.setRelationshipType("correlationid");
addressingProperties.setRelatesTo(relatesToType);
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
bp.getRequestContext().put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, addressingProperties);
I have an interceptor that sets the value of RelatesTo property:
public class CorrelationIdInterceptor extends AbstractSoapInterceptor {
public CorrelationIdInterceptor() {
super(Phase.POST_LOGICAL);
}
public void handleMessage(SoapMessage message) throws Fault {
AddressingProperties addressingProperties = (AddressingProperties) message.get(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES);
addressingProperties.getRelatesTo().setValue(UUID.randomUUID().toString());
}
The issue with above configuration is that all SOAP requests are sharing the same requestContext (as indicated on https://cxf.apache.org/faq.html#FAQ-AreJAX-WSclientproxiesthreadsafe?. That also means that every message has the same MessageID and I need to avoid that.
I could use the thread.local.request.context=true flag, but then I don't know where and how to put the creation of AddressingProperties object. Moreover, setting the flag to true also clears my endpoint address, which is a bit frustrating.
What is recommended way of making sure each SOAP request is using an unique AddressingProperties and MessageID ?
Kind regards

Get real client IP address in Spring Cloud Gateway

I implement rate limiting in Spring Cloud Gateway (SCG). I get client IP address with below code
#Component
public class RemoteAddressKeyResolver implements KeyResolver {
#Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
My SCG is behind a proxy so it gets address of proxy not real client address. How can I get real client address?
I found a solution!. There is an implementation of RemoteAddressResolver is XForwardedRemoteAddressResolver. Just use it, don't need to implement logic again.
#Component
public class RemoteAddressKeyResolver implements KeyResolver {
#Override
public Mono<String> resolve(ServerWebExchange exchange) {
XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
InetSocketAddress inetSocketAddress = resolver.resolve(exchange);
return Mono.just(inetSocketAddress.getAddress().getHostAddress());
}
}
That's all, so simple!
you could check your request headers key, like X-Forwarded-For (depends on your proxy settings)
The X-Forwarded-For (XFF) header is a de-facto standard header for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or a load balancer.
getFirst will return the origin ip
exchange.getRequest().getHeaders().getFirst("X-Forwarded-For")
return exchange -> {
// String origin = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
// if (origin == null)
// origin = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
// return Mono.just(origin);
XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
InetSocketAddress inetSocketAddress = resolver.resolve(exchange);
logger.trace("inetSocketAddress {}", inetSocketAddress);
logger.trace(".getHostName() {}", inetSocketAddress.getHostName());
return Mono.just(inetSocketAddress.getHostName());
};

Zuul proxy - how to forward requests to services depending on request path

Problem
How to forward requests in Spring Cloud application? I need to forward requests to other services depending on the part of uri.
For example
HTTP GET http://user-application/api/users, returns users JSON.
HTTP GET http://user-application/api/proxy/jobs-application/api/jobs, returns jobs JSON, but this request should be forwarded to another application:
HTTP GET http://jobs-application/api/jobs.
Any HTTP method is allowed, not only GET.
Context
I have a SpringBoot Application, User application which has REST end-points which return data.
For example GET http://user-application/api/users would return users in the JSON format.
User application also has an HTTP end-point which should forward the request to other applications - let's call one of them Jobs application.
This end-point is HTTP {ANY_METHOD} /api/proxy/{dynamic-service}/{dynamic-path} as an example,
GET http://user-application/api/proxy/jobs-application/api/jobs
Please, note, initial request comes to the User application, while then it is forwarded to the Jobs application.
Approaches
I put some my approaches which I think about. Maybe you have done similar things in the past, so you could share your experience doing so. Or even improve one of my approaches.
ProxyController approach
I would create a ProxyController in User application with mapping /proxy
#Controller
#RequestMaping("/proxy/**")
ProxyController
public void proxy(final HttpServletRequest request, HttpResponse response) {
final String requestUri = request.getRequestUri();
if (!requestUri.startsWith("/api/proxy/")) {
return null; // Do not proxy
}
final int proxyIndex = "/api/proxy/".lenght(); // Can be made a constant
final String proxiedUrl = requestUri.subString(proxyIndex, requestUri.lenght());
final Optional<String> payload = retrievePayload(request);
final Headers headers = retrieveHeaders(request);
final HttpRequest proxyRequest = buildProxyRequest(request, headers);
payload.ifPresent(proxyRequest::setPayload);
final HttpResponse proxyResponse = httpClient.execute(proxyRequest)
pdateResponse(response, proxyResponse);
}
The problem with this approach, I have to write a lot of code t build a proxy request, to check if it has payload and if it has, copy it into proxy request, then copy headers, cookies etc to the proxy request, copy HTTP verb into proxy request. Then when I get proxy response, I have to populate its details into the response.
Zuul approach
I was inspired by ZuulFilters:
https://www.baeldung.com/spring-rest-with-zuul-proxy
https://stackoverflow.com/a/47856576/4587961
#Component
public class ProxyFilter extends ZuulFilter {
private static final String PROXY_PART = "/api/proxy";
private static final int PART_LENGTH = PROXY_PART.length();
#Autowired
public ProxyFilter() {
}
#Override
public boolean shouldFilter() {
final RequestContext context = RequestContext.getCurrentContext();
final String requestURI = retrieveRequestUri(context);
return requestURI.startsWith(PROXY_PART);
}
#Override
public Object run() {
final RequestContext context = RequestContext.getCurrentContext();
final String requestURI = retrieveRequestUri(context);
final String forwardUri = requestURI.substring(PART_LENGTH);
context.setRouteHost(buildUrl(forwardUri));
return null;
}
#Override
public String filterType() {
return "proxy";
}
#Override
public int filterOrder() {
return 0;
}
private String retrieveRequestUri(final RequestContext context) {
final HttpServletRequest request = context.getRequest();
return request.getRequestURI();
}
private URL buildUrl(final String uri) {
try {
return new URL(uri);
} catch (MalformedURLException e) {
throw new RuntimeException(String.format("Failed to forward request uri %s}.", uri), e);
}
}
}
This code allows me to forward requests with less effort. However, we also use client side load balancer Ribbon and circuit breaker Hystrix in Spring Cloud Zuul out of box. How to enable these features? Will they be enabled out of box in context.setRouteHost(forwardUrl);
I would like to add another approach, maybe it can also work.
Static application.yml file to configure Zuul proxy approach
This approach does not requre dynamic Zuul Filters.
application.yml
zuul:
routes:
user-application:
path: /api/users/**
serviceId: user-service
stripPrefix: false
sensitiveHeaders:
# I have to define all other services similarly.
jobs-application:
path: /api/proxy/jobs/**
serviceId: jobs-application
stripPrefix: true
sensitiveHeaders:
It will work only if I know all the services my clients need to call before I deploy the User application. What if a new application is added dynamically? Then I will have to update the configuration.

Message mapping not done in spring

I am working on spring web socket. i just create a controller for calling apis through WS. but unfortunately its mapping is not done. so i cant call that apis from client.
This is my controller class
#Controller
public class FloorController {
#MessageMapping("/floormapupdate")
public Message greeting(#Header(value = "nativeHeaders") Map s, Principal principal, Message message) throws Exception {
String type = (String) ((List) (s.get("type"))).get(0);
Map headers = new HashMap();
headers.put("type", type);
System.out.println("===================" + principal.getName());
simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/channel/me", message);
return message;
}
}
what is the issue with it ?

How to get Client IP address in Spring bean

I have define a Spring bean.
<beans>
<bean id="remoteService" class="edu.wustl.catissuecore.CaTissueApplictionServicImpl" />
</beans>
Is there any way to get the IP address of client in this class? Similarly as available in the servlet request.getRemoteAddr();
The simplest (and ugliest) approach is to use RequestContextHolder:
String remoteAddress = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes())
.getRequest().getRemoteAddr();
Without knowing more about your bean and how it's wired up, that's the best I can suggest. If your bean is a controller (either subclassing AbstractController or being annotated with #Controller) then it should be able to get direct access to the request object.
The best way to get client ip is to loop through the headers
private static final String[] IP_HEADER_CANDIDATES = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR" };
public static String getClientIpAddress(HttpServletRequest request) {
for (String header : IP_HEADER_CANDIDATES) {
String ip = request.getHeader(header);
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
}
return request.getRemoteAddr();
}
Construct this:
#Autowired(required = true)
private HttpServletRequest request;
and use like this:
request.getRemoteAddr()

Categories