I'm working on an web application and have a problem of session storage.
The application is deployed to a cluster, and we place an apache http server to handle load-balancing. So each request may be handled by different node in the cluster. We set out to use Redis(master/slave) as a shared session storage.
My question is how to plug it to Spring mvc, so we can use redis and don't need change our application code.
I find a solution on the internet which uses Filter and HttpServletRequestWrapper:
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
//get sessionId from cookie or generate one if not found
filterChain.doFilter(new CustomHttpServletRequestWrapper(sid, request),
servletResponse);
}
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
String sid = "";
public HttpServletRequestWrapper(String sid, HttpServletRequest arg0) {
super(arg0);
this.sid = sid;
}
public HttpSession getSession(boolean create) {
return new HttpSessionSidWrapper(this.sid, super.getSession(create));
}
public HttpSession getSession() {
return new HttpSessionSidWrapper(this.sid, super.getSession());
}
}
I wonder whether I can find an equivalent in spring mvc?
The first one came to my mind is HandlerInterceptor. But not like filter's api, HanlderIntercepor does not handle the chain, so there is no way to pass a custom request to the next interceptor.
Any idea is appreciate, thanks in advance.
UPDATE:
I found two strategy on this topic:
The first one is extending your web container(such as Tomcat) and There is some mature open source project focused on it already.
The second one is use filter + wrapper. This stratey is web container agnositic.
Personally I prefer the first one because it's transparent to the developers. Let's say we use Weblogic in test/production evironment and we use embedded jetty for development because it's faster and requires less resources. In this situation, developer doesn't have to setup a session storage for development. On the other hand, however, every developer need to setup a session storage of his/her own if we adopt the second strategy. The alternative solution is provide a shared session storage for development environment or some configurations to swtich the sesson storage strategy(built-in for development, shared for test/production). I think it's easier to perform dependency(configuration) injection via spring than that via raw servlet filter and hence raise this question.
Does anyone know is there an out-of-box implementation of the first strategy for Weblogic by the way?
I don't know about WebLogic session storage configuration options, but I can comment on the filter based strategy.
You can implement the filter to be aware of its environment. You can add configuration for the development, which will say "don't wrap request" and enable wrapping only in production (or test) environment.
You can implement your filter to be Spring application context aware (check WebApplicationContextUtils) or even being managed by Spring's application context (via DelegatingFilterProxy). Then you will be able to pull out configuration values or use Spring profiles to set up the filter manually.
Related
Lets say I have a simple "Hello world" type servlet, configured with the annotation #WebServlet("/hello").
I want to disable it for build/deployment, so it will not be possible to "call" the servlet. How would I do that?
Then, through a configuration file, I want to be able to enable the servlet at run-time, so it can be used by a client. How would I do that?
Is either of these possible?
You can't enable servlets during runtime via standard API. It can at most only be enabled during build time in web.xml or during deploy time by ServletContext#addServlet(). Your best bet is to always enable it and control it on a per-request basis. You can use a servlet filter for this.
First give the servlet a name.
#WebServlet(urlPatterns="/hello", name="yourServlet")
public class YourServlet extends HttpServlet {
// ...
}
So that you can easily map a filter directly to it without worrying about servlet's URL patterns.
#WebFilter(servletNames="yourServlet")
public class YourFilter implements Filter {
// ...
}
In your filter, just decide whether to continue the chain, or to return a 404 based on your configuration file setting.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (isYourConfigurationFileSettingSet()) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
The isYourConfigurationFileSettingSet() part can't be answered in detail based on the information provided so far. In case you actually also couldn't figure out that, then head to Where to place and how to read configuration resource files in servlet based application?
Instead of defining the servlet through an annotation, do it in the web.xml file. Different versions of this file may allow you to have the servlets enabled or not.
The version of web.xml to use should be selected at build and deployment time. Maybe by a Maven profile or similar. Take a look at the following link for some ideas on that: https://maven.apache.org/guides/mini/guide-building-for-different-environments.html
If you want truly run-time control, then you may have to do a little custom coding. A filter (or, I suppose, the servlet itself) could check the value of a property and return a response with an HTTP error code (I suppose 403 would be vaguely appropriate; 404 less so, but if you want it to appear as though the servlet didn't exist in that configuration, it would work...)
In an server application I use bare websockets without the spring messaging layer and sockjs/stomp. I need my own messaging layer on top, but I want to use for example spring session to leverage the servlet HttpSession replacement to keep the http session alive which is beneath the websocket session, to prevent timeouts. The websocket is opened from a static HTML-page using JavaScript.
In the spring-framework documentation there is all the necessary information to setup bare websocket support with e.g. a basic BinaryWebSocketHandler or TextWebSocketHandler as well as how to
provide your own HandshakeInterceptor.
I've added spring session 1.1.0.RC1 with hazelcast for storing sessions and replacing the servlet container HttpSession implementation with the Session/ExpiringSession proivided by spring session. I just followed the given instructions to do this.
So far everything is fine, but as I'm serving a static HTML page from which I open the websocket using JavaScript without using spring security or any other mechanism to trigger a processing by the servlet container, no http session is intialized so far.
So the question is: How do I intitialze the custom HttpSession which is then stored in a the SessionRepository provided by spring session?
Note: I will provide an answer myself as I think I solved the issue for my case and it may be helpful for others facing a similar problem.
As can be seen from the spring-framework documentation, it is possible to add a custom HandshakeInterceptor. Spring session offers the
// example for reference:
org.springframework.session.web.socket.server.SessionRepositoryMessageInterceptor
which targets the spring messaging layer. It shows how to reset the timer of the HttpSession prior to sending a message within the method preSend(...) as well as how to optain the HttpSession from the request in the method beforeHandshake(...).
I implemented my own HandshakeInterceptor based on this with an almost identical beforeHandshake(...) method, but with one minor difference and of course leaving out the ChannelInterceptorAdapter stuff. The reset of the HttpSession time has to be done somewhere else, but that is out-of-scope.
Because in my case no HttpSession has been created so far, I changed the line obtaining the session from the request to look like this:
HttpSession session = servletRequest.getServletRequest().getSession(true);
Changing the boolean parameter from false to true in the method getSession(...). Now this call will trigger the creation of a new HttpSession (provided by spring session) if none is available so far.
This solution seems to do what is required in my case. Please correct me if I'm missing something or using anything the wrong way.
In short - I would like to add such service endpoints to my servlet that can only be called from localhost. The restriction should be coded in the servlet itself, i.e it should not depend on Tomcat/Apache to be configured in a certain way. At the same time, there are many other, existing endpoints that should be reachable externally.
Longer description - I am creating an HTTP API that 3rd parties can implement to integrate with my application. I am also supplying a default implementation, bundled together with my app, that customers with simple requirements can use, without having to implement anything.
The endpoints of my default implementation should be reachable only for my app, which happens to be the same servlet as the one supplying the implementation, i.e it runs on the same host. So for security reasons (the API is security related), I want my implementation to be usable only for my app, which in the first round means restricting access to localhost for a set of HTTP endpoints.
At the same time, I don't want to rely on customers setting up their container/proxy properly, but do the restriction in my servlet, so that there are no changes required for existing installations.
So far the only idea I had was to check the requestor's IP addess in a servlet filter - so I am wondering if there is a better, more sophisticated way.
I think you should add Web Filter to your application and check your url in doFilter method. Check request.getRemoteAddr() and endpoint link you can put in urlPattern.
Like this:
#WebFilter(urlPatterns = "/*")
public class RequestDefaultFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (isForbidden(request, response))
return;
else
chain.doFilter(request, response);
}
}
isForbidden implementation is up to you. In response you just send 403 error code for example.
You can check make same check in servlet and send in response 403 error.
We are trying to have a spring MVC Controller that works as a portlet and a servlet, in order to be deployed in a Liferay context or as a standalone version. But it seems that the we have a conflict if we decide to have multiple RequestMappings on the method level (as opposed to having just 1 mapping on the level of the controller). we get the error shown below.
Note that if we decide to just have a requestMapping on the level of the controller that hosts a servlet mapping and a portlet mapping, it works.
#RequestMapping({"view", "/"})
The controller that does not work:
#Controller("controller")
#RequestMapping("VIEW")
public class MyController {
#RenderMapping
public ModelAndView doView(RenderRequest request, RenderResponse response) throws Exception {
HttpServletRequest portletHttpReq = PortalUtil.getHttpServletRequest(request);
HttpServletResponse portletHttpResp = PortalUtil.getHttpServletResponse(response);
return doView(portletHttpReq, portletHttpResp);
}
#RequestMapping(value="/home")
protected ModelAndView doView(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// do something
return new ModelAndView("view");
}
}
The resulting error:
[...]
Caused by: java.lang.IllegalStateException: Mode mappings conflict between method and type level: [/home] versus [view]
Do you have any suggestions on how we could implement such thing? What we would really like to avoid is having to maintain 2 controllers for every portlet/servlet.
Thank you.
I don't really think this is a good idea... the #RequestMapping annotation on the class level is going to cause problems for sure, simply because Spring portlet MVC expects the portlet mode, while Spring Web MVC expects a root URL.
Also, your code doesn't seem to be correct either, since ModelAndView exists in both the portlet MVC as the web MVC part of the Spring framework, and since you cannot import both, you'll have to specify the full package for one of them, and since you're not doing that either, your code is just wrong.
Except the technical issues, both portlets and servlet have different terminology and point of views. These are some key questions that pop up with me if I hear this:
What are you going to do about the different phases (ACTION and RENDER)
What about the the different portlet modes (VIEW, EDIT, HELP, ...)
What are you going to do about the specific portlet features (PortletSession, PortletPreferences, ...)
How are you going to handle the different kind of requests (ResourceRequest, ActionRequest, RenderRequest, PortletRequst, EventRequest vs HttpServletRequest)
How are you going to handle security? The portal container provides authentication for you, a standalone web application does not.
And these are just questions from a portlet mindset, I'm pretty sure there are also technical issues if you look at it from the web application point of view.
It makes much more sense to divide your code into a view layer and business logic. Put the business logic in a separate package or separate services, and build a separate portlet- and standalone application, using the same/shared business logic.
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.