How to get source address / ip from inside ContainerResponseFilter - jersey

I'm writing a logging filter that logs all HTTP requests / responses for a web app running in Jersey. ContainerResponseFilter seems to a straight forward solution and I've managed to get it to work.
Next step is to log the IP of the requests. Is there a way to do that from inside the ContainerResponseFilter ?

Short answer:
#Provider
public class YourContextFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest sr;
#Override
public synchronized void filter(ContainerRequestContext request) throws IOException {
/*
* Returns the Internet Protocol (IP) address of the client or
* last proxy that sent the request. For HTTP servlets, same as
* the value of the CGI variable REMOTE_ADDR.
*/
String ip = sr.getRemoteAddr();
// ... log it ...
}
}
EDIT
(regarding the wish for a more detailed answer)
Afaig:
The #Context annotation allows to inject JAX-RS–specific components (one might say you are able to inject contextual information objects). JAX-RS itself is a Java based specification for RESTful Web Services over HTTP protocol. So we are able to inject stuff like:
javax.ws.rs.core.UriInfo
javax.ws.rs.core.Request
javax.ws.rs.core.SecurityContext
and also
javax.servlet.http.HttpServletRequest
In the IOC Chapter of the Jersey docs, you will find these notes:
[...] Jersey implementation allows you to directly inject HttpServletRequest instance into your JAX-RS components [...] - https://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e2401
[...] The exception exists for specific request objects which can injected even into constructor or class fields. For these objects the runtime will inject proxies which are able to simultaneously server more request. These request objects are HttpHeaders, Request, UriInfo, SecurityContext. These proxies can be injected using the #Context annotation. [...]
[...] When deploying a JAX-RS application using servlet then ServletConfig, ServletContext, HttpServletRequest and HttpServletResponse are available using #Context. [...]
And if you do so, you inject in fact a Proxy named org.apache.catalina.connector.RequestFacade (link). This proxy functioned as your direct hotline to your Coyote (HTTP Connector) and thereby to the Coyote request object (link).
Hope this was helpful somehow :) - Have a nice day.

Related

HttpServletRequest throws error when used within Aspect

I have a method which has an aspect. When I try to #Autowire HttpServletRequest, and use request.getHeader(something), I get this error -
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
How do I fix this? I tried using RequestContextHolder, but upon debugging I still see null. How do I use the RequestContextListener when my project has no web.xml.
Request Header can be accessed using HttpServletRequest below way.
private static HttpServletRequest getRequest() {
return((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getApiTraceId() {
return getRequest().getHeader(something);
}
Aspect annotations spins a new thread which is different from the one httpservlet is available in. This is why request was not available within the #ASpect. To resolve it, call the request object BEFORE the aspect method, cache it and call the same method as before.

HTTP Basic Authentication in Spring Web Services Integration Tests

I have a SOAP web service written in Spring Web Services that I would like to integration test.
I would like to use spring-ws-test as the reference documentation points to. So, the test code is similar to the example in the reference, something like that:
#Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
#Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext);
}
#Test
public void customerEndpoint() throws Exception {
Source requestEnvelope = new ResourceSource(new ClassPathResource("request.xml"));
Source responsePayload = new ResourceSource(new ClassPathResource("response.xml"));
mockClient.sendRequest(withSoapEnvelope(requestPayload)).
andExpect(payload(responsePayload));
}
However, the endpoint I am testing is using basic authentication and it expects to read values in the Authorization header. It is not using spring-security for that task but it has custom logic that gets the HTTP headers by getting the HttpServletResponse from the TransportContextHolder. So, the request triggers the endpoint but it fails to retrieve the basic authentication base64 token.
The question is, how may I pass HTTP headers in that situation? Is it possible at all? If not what is the preferred alternative?
I have read the javadoc and I cannot find a way to pass the headers. Also, I have found this question which is similar but it doesn't help me much.

How can I set global context variables in Spring?

So I have a Spring boot application with many api requests.
For a large number of these requests I know want to log the "user-agent" part of the header from the request.
One solution is to do this.
In my controllers I could just put #RequestHeader("user-agent") String userAgent and pass it on to the service layer to be logged.
But it would be much handier if I could add the user agent as a global variable, just like the username is added as a global variable through the SecurityContextHolder.
So my question is, is it possible to add custom global context variables like the authentication details from the Authentication filter class? And if so how?
If you are using Spring MVC then you can Autowire HttpServletRequest and get the request headers from it.
#Service
public class HelloService {
#Autowired
private HttpServletRequest httpServletRequest;
public void print() {
System.out.println(httpServletRequest.getHeader("x-test"));
}
}
Alternatively you can also get hold of request instance from RequestContextHolder:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getHeader("x-test");

HttpServletRequest Object in Simple java file

I am working on the Web Project, and i want to get the information of the request object in our simple java program which is not extending the HttpServlet class.
The same problem i am facing in the #init() of servlet , i want to call some functions in init() in that i need request object , but i am not getting how can i do this functionality.
please do not post any answer with related to the Spring technology :)
I googles alot but didn't find anything for this.Please help me out.
First of all, HttpServletRequest and HttpServletResponse are interfaces.
The implementation classes for these interfaces are provided
by the application server (server container) vendor (like Tomcat, JBoss, Glassfish,
etc..).
When the application server (where the your web application is
deployed), receives the request from the client, the objects for the
HttpServletRequest and HttpServletResponse implementation classes are
created. And the creation of these objects happens for each hit
(request) from client.
In general, these request/response objects (created by container) will be passed to the HttpServlet class as method parameters, from which we will retrieve the data the client intended to send to the server/servlet methods inside doGet() or doPost() (using request.getParameters() method).
Also refer below docs to know more about the HttpServletRequest and HttpServletResponse are interfaces.
http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html
http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html

How to move data from Session's attributes to application context in a spring web application?

There is a web application that I am working on currently and it has to be extended to expose web services. In the current project - when the application context is loaded at startup - database queries are made and static data like role names is set as variables at the session level.
Like this:
private void loadRoles(ServletContext acontext) {
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(acontext);
IMyDataService myDataService = (IMyDataService ) appContext.getBean("myDataService");
List<Roles> rolesList = myDataService.listRoles();
acontext.setAttribute(MyAppConstants.ROLES, rolesList);
}
This value stored in the session attribute is used as follows in other places of the application:
public boolean checkAccess(HttpServletRequest arequest) {
HttpSession session = arequest.getSession();
List<Role> roles = (List<Roles>)session.getServletContext().getAttribute(MyAppConstants.ROLES);
.....
}
If I want to enhance the application to expose web services - my understanding is that I will no longer be having a ServletSession or HttpServletRequest available with me. So I want to move this static data from session variables to another place so that they are available in the context.
Is there a way in which I can achieve this?
I tried getting rid of storing data in session variables all together, but could not do that because there are just too many references.
Is there a better approach?
There is a difference between the session and the servlet context. I think that you are confuse because you are using the session object to get the servlet context. Your data is clearly set in the servlet context in this example. Even if you are using web services, you will have a servlet context provided by the servlet container.
Now I don't know which technologies you are using, but there's many other ways to make static data available to all your web services. For example, using a cache mechanism might be a better solution for data that is stored in the database...
More info :
A Servlet Session is a very different thing from a Servlet Context.
An HttpSession is a mechanism used to simulate and maintain a
continuous user Session between a Web Browser and Web Application,
largely managed by the Servlet Container. The HTTP protocol is
stateless, it is essentially a request-response scheme, so Servlet
Sessions are maintained by passing a unique HTTP cookie value for each
request, or by dynamically including an identifier in Servlet URLs,
known as URL-rewriting.
A ServletContext object represents the overall configuration of the
Servlet Container and has several methods to get configuration
parameters, exchange data amongst Servlets, forward requests and load
resources. The Servlet Context is usually obtained indirectly through
the ServletConfig object, passed to a Servlet's init(ServletConfig)
method, or from the Servlet getServletConfig() method.
Javadoc definition for ServletContext :
Defines a set of methods that a servlet uses to communicate with its
servlet container, for example, to get the MIME type of a file,
dispatch requests, or write to a log file.
There is one context per "web application" per Java Virtual Machine.
(A "web application" is a collection of servlets and content installed
under a specific subset of the server's URL namespace such as /catalog
and possibly installed via a .war file.)
EDIT
To get the ServletContext object in a Spring application, use the #Autowired annotation. Note that the object should be managed by the Spring container, it will be the case if you are using controllers for you REST services.
#Autowired
ServletContext servletContext;
Here an example :
#Controller
#RequestMapping("/foo")
public class RESTController {
#Autowired
ServletContext servletContext;
#ResponseBody
#RequestMapping(value="/bar", method = RequestMethod.GET)
public List<Roles> getRoles() {
return servletContext.getAttribute(MyAppConstants.ROLES);
}
}
Refactor your code by removing MyAppConstants.ROLES and by relying on Spring DI. There are much better ways to store static data than putting them in HTTP session or servlet context... (ex. create a list bean, use cache abstraction, use FactoryBean etc.).

Resources