This question already has answers here:
Design Patterns web based applications [closed]
(5 answers)
Closed 6 years ago.
I want to understand, If request has to be processed through multiple functions-
is it good to forward through multiple Servlets OR
implement as object methods
Example
req.getRequestDispatcher("LoadSession").forward(req, resp);
req.getRequestDispatcher("AuthoriseRoles").forward(req, resp);
req.getRequestDispatcher("Boot").forward(req, resp);
OR
sessionLoader.loadSession(req,res);
authoriseService.authoriseRoles(req,res);
bootService.boot(req, res);
I assume you are at the phase of designing an API. According to REST design principles, the url should reflect the resource that is handled or requested and the HTTP method should reflect what action is required to be taken on the resource.
So, instead of /LoadSession and having the session id as query param in the Http request, it should be GET /session/{id} for example GET /session/e841092fa2194340bc40 (I am assuming LoadSession is a request to return an existing session)
You might ask yourself what is the advantage of following this design. It is that there are several libraries and frameworks that are able to parse incoming HTTP requests and take care of the routing for you (for example, Jersey is the reference JAX-RS implementation, JAX-RS being JavaEE's REST standard) . So instead of writing a servlet as you mentioned, you write the class that represents the resource and methods that are fired according to the HTTP method. you tie it all together with annotations:
#Path("/session")
import javax.ws.rs.*;
import javax.ws.rs.core.*;
#Produces({MediaType.APPLICATION_JSON})
public class SessionHandler
{
#Context
private HttpServletRequest httpRequest;
#Context
private HttpServletResponse httpResponse;
#GET
#Path("{id}")
public Session load(#PathParam("id") String id) {
...
Related
We have a legacy application that uses embedded Jetty and provides functionality through clients making HTTP calls. Most of the information/parameters needed by the server is sent by the client through HTTP headers. We are now prototyping the use of REST API calls using Jersey where the same parameters are provided as JSON inputs. One of the requirements is to maintain backward compatibility and not disturb the existing functionality.
While we were able to use Jersey and pass in parameters, we are looking for help with the following:
We want to avoid changing the current jetty handlers in the application, so we would like to convert the json input from the REST API input into headers and pass them on to the handlers, so that the current functionality can kick in from that point.
I have tried the other (very helpful) posts on using the wrapper/filter mechanisms to add custom headers, even one using the ContainterRequestFilter. Following are my references:
Adding an HTTP header to the request in a servlet filter
How to add servlet filter with embedded jetty
How to add a header to http request using a filter in jax-rs
However for security reasons, the legacy application has this line of code (recommended in Jetty docs) which uses the base request instead of the wrapped request:
Request base_request = request instanceof Request ? (Request)request : HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
Response base_response = response instanceof Response ? (Response)response : HttpConnection.getCurrentConnection().getHttpChannel().getResponse();
This effectively does not use the HttpServletRequestWrapper object that I send in. Since this line of code looks for the org.eclipse.jetty.server.Request object, I tried creating a wrapper around this object, like so, but this did not work because this instance seems to have most of the content as null, plus it would not provide the rest of the methods that the Request object would provide.
class MyRequestWrapper extends Request
{
public MyRequestWrapper(HttpServletRequest request)
{
super( ((Request)request).getHttpChannel(), ((Request)request).getHttpInput());
}
#Override
public String getHeader(String name)
{
if(name.equalsIgnoreCase("X-My-Test"))
{
return "MyName";
}
return super.getHeader(name);
}
}
What is the best way to send the JSON inputs in as headers from the REST handling method to the existing Jetty handlers, without creating security concerns? I guess I could tweak that check for the base request a bit, but I am not sure of the best way to do it.
Wrapped requests are only valid for the same ServletContext and Filter chain that the wrapped request was created in and only applies to the rest of the executing Filter chain from the point in time it was created.
A wrapped request will never apply to a standard Jetty Handler, as that's not participating in a ServletContext or Filter chain.
It is also impossible to wrap the core Jetty Request object due to the needs of the context-less environment it executes within. You cannot change this behavior.
If you are wrapping requests and whatnot just to provide a custom request header, then stop doing ALL of the wrapping and nonsense you are dealing with right now.
Note: The minute you stop wrapping HttpServletRequest, HttpServletResponse, or the Servlet streams is the minute you will have the ability to use features introduced for Servlet 3.0 and newer, such as AsyncContext and Async I/O. The technique of wrapping these components is discouraged in modern usage because it limits your options for better performing webapps.
You have 2 choices, both modify the Request headers in-place.
Modify the Request headers before dispatch.
Modify the Request headers during dispatch via a low level Jetty Handler.
If you choose to modify the headers before dispatch, there are 2 places you can do this.
As a HttpConfiguration.Customizer
During one of the pre-dispatch HttpChannel.Listener events
If you choose to modify the headers during dispatch, then create a Jetty Handler that modifies the Request headers, and put it somewhere early in your server handler hierarchy.
The code that modifies the Request headers will all do the same thing, here's the Handler version as an example.
package jetty.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
public class ModifyRequestHeadersHandler extends AbstractHandler
{
#Override
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
// As fully fleshed out field
final HttpField X_MY_TEST = new HttpField("X-My-Test", "MyName");
baseRequest.getHttpFields().put(X_MY_TEST);
// As predefined header and custom value
baseRequest.getHttpFields().put(HttpHeader.X_POWERED_BY,
"ModifiedRequestHeadersHandler");
// As string header and string value
baseRequest.getHttpFields().put("X-Foo-Test", "MyFooName");
}
}
Short Version
Is javax.servlet.ServletRequest's method setAttribute(<key>, <Object>) only used as a means of passing objects between methods in Java code?
Long version
Let's say I have a javax.servlet.Filter implementation to handle all logged in users' authentication using cookies:
in Spring Boot
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class AuthFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Cookie[] cookies = null;
if (request instanceof HttpServletRequest) {
cookies = ((HttpServletRequest) request).getCookies();
}
Optional<User> user = mySessionAuthMethod(cookies);
if (user.isPresent()) {
request.setAttribute("user", user.get());
}
chain.doFilter(request, response);
}
}
Then later, I can avoid manual authentication in all of the Web API methods, and just check the user attribute. Example of a #RestController's method:
#RequestMapping(value = "/profile")
#CrossOrigin(origins = {MyProperties.ORIGIN}, allowCredentials = "true")
public ResponseEntity getProfile(HttpServletRequest request, HttpServletResponse response) {
String user = request.getAttribute("user");
if (user != null) {
return myGetProfileResponse(user);
}
return myNotLoggedInResponse();
}
My questions are:
Is this form of authentication secure? What I mean is, are the attributes in the ServletRequest only added and used in Java for communication between methods, or could they be added to the request already before reaching the server?
Is this way of authentication using Filters a good practice to avoid duplicate code?
Additional Explanation
The real reason of doing this is not only authentication. I have also Filters which need to process each and every request and pass objects to the Controllers. What I definitely want is that none of these objects and information can be forged even by a person who knows the implementation of the system.
I think I have found the answer from the documentation of getAttribute
Attributes can be set two ways. The servlet container may set attributes to make available custom information about a request. For example, for requests made using HTTPS, the attribute javax.servlet.request.X509Certificate can be used to retrieve information on the certificate of the client. Attributes can also be set programatically using ServletRequest#setAttribute. This allows information to be embedded into a request before a RequestDispatcher call.
So according to this (if there is no missing information), it should be completely safe to pass custom objects and know that they were always created by the server.
I'm new to JAX-RS and I'm trying to understand how the #Context annotation works. I have a REST service and am currently using certain filters to do extra processing for different request types. Inside one of those filters is the following line:
public class SentryFilter {
#Context
HttpServletResponse response;
...
This value is used later on in the filter() method of that filter. The problem is that the response object is null. I've stepped through a debugger and can't determine why it's null.
From what I've read in the JAX-RS documentation, the #Context annotation for HttpServletResponse can be filled by the resource methods. So, I modified my the API I'm calling to include HttpServletResponse
public interface APIStuff {
#Path("deviceName")
#GET
#Sentry
String getDeviceName(#PathParam("deviceId") #Size(min = 1, max = 1024) final String deviceId, #Context HttpServletResponse httpServletResponse);
...
This returns the same HttpServletResponse is null error.
So the question is, where is this value supposed to be "filled"? The person who wrote the filter class obviously did so with the belief that the response object would be filled, so I don't think it's a matter of passing in #Context HttpServletResponse to the filter() method.
The #Context annotation allows you to inject request/response context details into JAX-RS provider and resource classes. Injection can be performed into a class field, a bean property or a method parameter.
The types available for injection are listed in this answer.
You haven't provided any details about how your application is deployed, but be aware that HttpServletResponse is available for injection only when the application is deployed in servlet containers (Tomcat, for example). It might be your problem.
#Context varies with usage and depends on what is actually being injected. If you are injecting the HttpServletResponse, it needs to be part of the method signature. This is because most beans are implemented as a singleton, and having multiple requests will just override the response if implemented at an instance level.
As an aside, IMO the API should support injecting the method-only '#Context' resources into a (for example) ThreadLocal<HttpServletResponse>
Your Filter that you posted does not extend javax.servlet.Filter, are you including the full signature? how is this filter being used?
As to why your resource method level injections are turning up null, are you sure it's actually being called within the servlet context?
As #cassiomolin said, you haven't provided any details about how your application is deployed, HttpServletResponse is available for injection only when the application is deployed in servlet containers (Tomcat, for example). It might be your problem.
I had similar problem with GrizzlyHttpServer.
Cause of my issue was, Grizzly provides similar abstractions to those offered by the Servlet specification: HttpHandler (Servlet), Request (HttpServletRequest), Response (HttpServletResponse).
So I had to use: import org.glassfish.grizzly.http.server.Request;
instead of: import javax.servlet.http.HttpServletRequest;
This question already has answers here:
Is it okay to use same resource name for both get and post rest api
(2 answers)
Closed 7 years ago.
In java RESTfull service can I define two methods in same path differentiate by http calling method.
EG : first method using GET and Second one using POST
#GET
#Produces("application/pdf")
public Response getFile(#Context HttpServletRequest req,#PathParam("search") final String search,Map<Object, Object> input) {
....}
#Post
#Produces("application/pdf")
public Response getFile(#Context HttpServletRequest req,#PathParam("search") final String search) {
....}
Annotation is just a decorator for the given method. The core principle is , it should not stop original structure of java classes. So it is perfectly legal to have multiple handler in single file.
Yes, it is perfectly valid to have separate handlers for different methods at the same path.
We want to implement a public RESTful API integrated in our software (written in java) that might be used by various clients to build small e-commerce apps (e.g. for Android or iPhone). This API includes getting a list of products, categories, shopping cart support, etc.
We need to provide an API that will allow user registration and couple of other sensitive functions. How should we protect this API against spam and bruteforcing? In the standard product we use reCAPTCHA. Any alternative for the REST counterpart?
First, think of separation of concerns. What is the purpose of REST API?
A REST API should do offer a service to the client. Client sends a request via REST protocol, and gets a response for its request. In code, this looks something like:
#GET
public Response getClientInfo(#QueryParam("clientId") Integer clientId) {
ClientDTO clientDTO = database.getClientInfo(clientId);
return ResponseWrapper.wrap(clientDTO);
}
Now, you want your REST method doing ONLY this and nothing else. Otherwise, you would put block-bruteforce-and-spam-logic in your REST method and you would get a mess of the code that is not extensible, hard to version, etc. If you want to change your, e.g. blacklisting policy you would have to change each and every REST method, and it's bulky. If you want to check the calls before the make it to REST methods, then take a look at Filters. Every request and response pass through a chain of filters and could be check for misuse of the server.
I don't know what is your technology stack is, but I would suggest looking into these:
JBoss AS7.
DeltaSpike (enables you powerful Interceptors that will check user rights and execution rights before the execution of the REST method).
for example:
#LoggedInUser
#GET
public Response getClientInfo(...) {
...
}
This security annotation #LoggedInUser (which, by the way, you define) will give sign to an Interceptor to check this security constraint, e.g.
#Secures (built in annotation)
#LoggedInUser
public boolean hasRight(Identity identity) {
return identity.isLoggedIn(); //or if he is in certain group of users
}
Context and Dependency Injection context (used in DeltaSpike).
JBoss Filters (a filter chain where you can create your own filter that, for example, checks if some IP is trying to send multiple calls within a very short period ~ 10 lines of code).
An example of the Filter
#Startup
#ApplicationScoped
#Filter(around= "org.jboss.seam.web.ajax4jsfFilter")
public class IPTrackerFilter extends AbstractFilter {
//IPTracker is your #ApplicationScoped bean that remembers all IP addresses accessing the application.
#Inject
private IPTracker fIPTracker;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (!(req instanceof HttpServletRequest)) {
chain.doFilter(req, res);
return;
}
final String ipAddress= ((HttpServletRequest)req).getRemoteAddr();
if (fIPTracker.isBlackListed(ipAddress)) {
//implement error message here
sendErrorMessage(response);
return;
} else {
//all good, continue
chain.doFilter(req, res);
}
}
}
PS. I gave you the link for DeltaSpike, for others is really easy to find. Also, if you find DeltaSpike to obscure, try with JBoss Seam Security Framework.