A request to an Action can be validated using the Interceptors. Also, Direct request to a JSP page can be avoided using an empty action declaration. It is something like this;
<action name="home" >
<result>home.jsp</result>
</action>
I want to validate this calling to the JSP page. As I feel that a simple approach can be adding an Action (ActionSupport) with an Interceptor which checks the action name( and do some validation using session etc). But I've a doubt that whether it reduces the performance,because that action class doesn't do anything and just execute its execute() only(useless task...), and where there should be an empty Action .(BUT: as I ve read some docs on Struts2, it is said that even though we don't add an Action calss,the framework itself adds and action class which returns "success" therefore,adding an action class or not ourselves, doesn't affect newly )
Any way, I d like to know what your best approaches are to validate or authorized access to some JSP pages.( number of pages can be many.. not only to one jsp page)
Added:
Example:
Lets say that, there are some restricted pages where all users can't have access,for example user's account page can be visited only to logged in users.There can be more such type of pages. If a request comes to a such page,the user has to be validated.Then, if the request comes through a empty/anonymous action (as explained in above code snip -only action name-no real class) how to validate such requests to JSP pages? Should an action class be used for this?
If your attention is to secure some part of your application so as only Authenticated as well authorize use can access that part, than you have two option
Use an interceptor based authentication
User a security API like Spring -security
Interceptor based authentication is quite easy. here is an example how to do this, but such authentication is not recommended for a production based and real life application since its really a very simple case.
if you are looking for a complete authentication system, i suggest you to look at Spring security.Its quite easy and configurable, all you need to tell the underlying spring mechanism which all areas and under secure tag and Spring security will intercept them before your action get called and only successful and authorize action will get called by spring security.
//This is for authorization
package com.kogent.action;
import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.ExecuteOperations;
import org.apache.struts2.dispatcher.ng.InitOperations;
import org.apache.struts2.dispatcher.ng.PrepareOperations;
import org.apache.struts2.dispatcher.ng.filter.FilterHostConfig;
import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter;
public class SessionController extends StrutsPrepareAndExecuteFilter {
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns = null;
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(filterConfig.getServletContext(),
dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(),
dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}
/**
* Callback for post initialization
*/
protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
if (excludedPatterns != null
&& prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request,
response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(
request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
//here you have to identify the whether the user have access to requested resource or not
//allow him if he was access.
//if(someCondition)
execute.executeAction(request, response, mapping);
//else{
//redirect the user how do you want it to be.
ActionMapping modfiedActionMapping = new ActionMapping();
modfiedActionMapping.setName("someActionName");
modfiedActionMapping.setNamespace("someNameSpace");
execute.executeAction(request, response, modfiedActionMapping);
//}
}
}
} finally {
prepare.cleanupRequest(request);
}
}
public void destroy() {
prepare.cleanupDispatcher();
}
}
<filter>
<filter-name>struts2</filter-name>
<filter-class>Point to your customized filter</filter-class>
</filter>
Related
I have built a single page webapplication using Angular on the frontend and Spring on the backend. Its a community website and i want to be able to ban misbehaving users the spring way. I have searched stackoverflow to see if there is a thread about this but i could find none.
My attempt to build this functionality is by creating a custom webfilter that filters all incoming requests and checks whether the ip address of the requester is in the blocked ip list. If not then the request gets forwarded, but if it is on the list then an error response is sent back instead.
Here is the code of the filter and an implementing interface:
package RequestPreprocessors;
import DAOs.BanDao;
import Interfaces.IpAddressExtractor;
import Services.ModeratorService;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;
#WebFilter("/*")
public class IpAddressFilter implements Filter, IpAddressExtractor {
private static final Logger log = Logger.getLogger("IpAddressFilter.class");
private Set<String> blockedIpAddresses;
#Autowired
private ModeratorService moderatorService;
#PostConstruct
private void loadBlockedIpAddresses(){
blockedIpAddresses = moderatorService.getBlockedIpAddresses();
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String clientIpAddress = getClientIP((HttpServletRequest) servletRequest);
log.info("ip " + clientIpAddress + " is requesting " + httpServletRequest.getRequestURI());
if(blockedIpAddresses.contains(clientIpAddress)) {
log.info(clientIpAddress + " is currently on the banlist, aborting request...");
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
}
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
Here is the code of the implementing interface
public interface IpAddressExtractor {
public default String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null){
return request.getRemoteAddr();
}
return xfHeader.split(",")[0]; // voor als ie achter een proxy zit
}
}
This should work but i don't think sending a simple http status code is very elegant, i would like to be able to send back a message explaining to the user that he/she is in fact banned.
So my question is; How do i ban a user from a spring web application effectively. And with effectively i mean being able to send an error message back to the single page app that can then be displayed to the user. I would also like to know if this is the best way to deny banned users access from the REST api. I would like to know if there are different more effective ways to accomplish this.
Thank you
EDIT: This is the tutorial i used to create the majority of the code https://www.baeldung.com/java-web-app-without-web-xml
Effective banning is a co-operative effort between your application doing the detection and the operating system implementing the block. If you are deploying on to Linux then an effective strategy is this:
Log an easily parseable message to an audit log file that says the user is to be banned and include the IP address. The HTTP response to the offender can include a suitable 'goodbye' message.
Install and configure fail2ban to parse your log files and implement the ban. It works by making local modifications to the firewall rules to prevent the offender even making a network connection to your server.
I've got a project which is divided in different modules, you have for example a site and a forum.
The forum can be found at:
http://example.com/[forum]/
and for example can be:
http://example.com/support/
http://example.com/helpme/
http://example.com/aforum/
The site can be found at:
http://example.com/[site]/
and for example can be:
http://example.com/site1/
http://example.com/nice/
http://example.com/something/
The [forum] and [site] part are variable. In my database i lookup that "nice" is a site, "helpme" is a forum.
I have a spring RequestMapping for my ForumController:
#RequestMapping(value = { "/{simpleTitle:[0-9a-z-]+}" }, method = RequestMethod.GET, produces = "text/html")
public void list(#PathVariable String simpleTitle, Model model, HttpServletRequest req, HttpServletResponse resp) {
I have the samething for sites, so a SiteController:
#RequestMapping(value = { "/{simpleTitle:[0-9a-z-]+}" }, method = RequestMethod.GET, produces = "text/html")
public void list(#PathVariable String simpleTitle, Model model, HttpServletRequest req, HttpServletResponse resp) {
This of course goes bad, cause 2 controllers with the same requestmapping isn't good.
I can create a FrontController which with above request mapping, lookup what simpleTitle is (a forum or a site) and call functions to display a forum or a site. That works.
But it isn't very "spring" like and structured.
Is it possible "intercept" a request and internally forward (or call the function) on the controller myself?
This way i could have Interceptor which looks a simpleTitle, decides whether it is a forum or site, and "forwards"/"calls" the right controller.
Frankly I like #Luiggi Mendoza solution, but if you want an alternative, use something like this:
package eu.europa.acer.aris.ceremp.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
#Component(value = "yourCustomFilter")
public class YourCustomFilter extends OncePerRequestFilter{
private static final Logger logger = LoggerFactory.getLogger(YourCustomFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//String[] path = request.getRequestURL().toString().split("/");
if (letPagePass(request.getRequestURL().toString()) == false)
{
// if request is bound to static resource like js//img do nothing, the filter chain will activate
if (letResourcePass(request.getRequestURL().toString()))
{
}
else
{
String[] urlInfos = obtainUrlAndParametersLast(request.getRequestURL().toString());
// last element will always give back last part including any parameter
// first element will always be a controller modifier
response.sendRedirect(request.getContextPath()+rebuildControllerPath(urlInfos));
return;
}
}
filterChain.doFilter(request, response);
}
private String rebuildControllerPath(String[] pathElement )
{
//do your thing here
if ("forum".equals(pathElement[0]))
{
String addenda = "/forumController/";
for (String singlePart: pathElement)
{
addenda = addenda+singlePart+"/";
}
return addenda;
}
}
// bind forceful redirect
public boolean letPagePass(String url)
{
// if you have some page that are naturally unique among controllers that you want always to process natively
String[] path = url.split("/");
if (path[path.length-2].startsWith("yourCertainUrlIgnoringParameters"))
{
return true;
}
// directcall
else if (path[path.length-2].startsWith("yourCertainUrlIgnoringParameters2"))
{
return true;
}
else
{
return false;
}
}
public boolean letResourcePass(String url)
{
String[] path = url.split("/");
/*
this assumes you have always a strict structure so your image//js//reource will always be in
https://domainname/a/lot/of/folder/img/actuaresource.png
or
https://domainname/a/lot/of/folder/js/actuaresource.js
etc
*/
//image pass
if (path[path.length-2].equals("img") || url.contains("/img/"))
{
return true;
}
//css pass
else if (path[path.length-2].equals("css") || url.contains("/css/"))
{
return true;
}
//js pass
else if (path[path.length-2].equals("js") || url.contains("/js/"))
{
return true;
}
else
{
return false;
}
}
#Override
public void destroy() {
}
}
and add to your web.xml file the following xml snippet
<!-- your fi Filter -->
<filter>
<filter-name>yourCustomFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>yourCustomFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Cheers Luiggi Mendoza and witchedwiz for the great ideas!
While i was eating i came up with a very simple solution: use servlet filter. Add a request header with a HttpServletRequestWrapper (type: forum or type: site) inside the doFilter.
In the #RequestMapping add a headers property like headers = "type=forum".
Now #RequestMapping can have the same urls, they differ in request header. And all spring functionality works, although i only made a simple test case so far.
WebApplicationContextUtils helped to get spring beans in the servlet filter.
To provide proper browser caching I want to get rid of the conversationContext parameter, that Apache MyFaces Orchestra adds to every request, for requests to css files.
As Bozho suggested, I've implemented a filter that sets the attribute Orchestra is looking for.
public class ResourceFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse theResponse, FilterChain theChain) throws IOException, ServletException {
if(shouldNotAppendConversation(request)) {
request.setAttribute(RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED, Boolean.TRUE);
}
theChain.doFilter(request, theResponse);
}
private boolean shouldNotAppendConversation(ServletRequest theRequest) {
HttpServletRequest aRequest = (HttpServletRequest) theRequest;
String aPath = aRequest.getRequestURI();
if(aPath.endsWith(".css.jsf")) {
return true;
}
return false;
}
#Override
public void init(FilterConfig theFilterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
That doesn't work the parameter is still appended to every request. While debugging, I've found out that the filter gets first hit by a request to the jsf site. For sure I want to include the conversation context in that request, so the filter forwards the request directly to the next filter in the chain. The next request that hits the filter (usually the request for a css file) has already the conversation context included in the request.
The strange thing is, if I modify the filter to always set the attribute, all request will not have the conversation context attribute. But that means, the conversation context is also not included in the request for the jsf site (but should).
I've noticed that the links to css files in the generated html of the jsf site also contains the conversation context attribute or not depending on the filter implementation. I guess for this reason the second request has already included the conversation context parameter?
I don't understand why Orchestra is appending the conversation context parameter to every request and not just for the requests where the attribute is not set.
How can I implement the filter to work correctly?
The next request (e.g. for a CSS file) hitting your filter after the request to your page has already the conversationContext parameter included just because this is how the url for this resource has been rendered by the page in the previous request.
So the control over conversationContext should be taken at render time. The following solution is working for me with JSF 2 (I am using Mojarra 2.1.11, myfaces-orchestra-core20 1.5, RichFaces 4.1.0.Final). A special servlet filter is doing nothing but wraps HttpServletResponse with our own wrapper:
public class RfOrchestraParamControlFilter implements Filter {
...
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
response = new RfOrchestraParamControlResponseWrapper((HttpServletResponse)response);
chain.doFilter(request, response);
}
...
}
The response wrapper tests the url to be encoded for being a richfaces resource and turns Orchestra's ConversationRequestParameterProvider's separation mode on in the current thread for the time of encoding:
package ...
import javax.faces.application.ResourceHandler;
import org.richfaces.resource.ResourceHandlerImpl;
import org.apache.myfaces.orchestra.conversation.ConversationRequestParameterProvider;
public class RfOrchestraParamControlResponseWrapper extends HttpServletResponseWrapper {
public RfOrchestraParamControlResponseWrapper(HttpServletResponse httpServletResponse) {
super(httpServletResponse);
}
#Override
public String encodeURL(String url) {
if (url.contains(ResourceHandler.RESOURCE_IDENTIFIER) || url.contains(ResourceHandlerImpl.RICHFACES_RESOURCE_IDENTIFIER)) {
boolean current = ConversationRequestParameterProvider.isInSeparationMode();
/* Disable conversationContext parameter in current thread for the time of rendering link to a resource */
ConversationRequestParameterProvider.setInSeparationMode(true);
String result = super.encodeURL(url);
/* Restore */
ConversationRequestParameterProvider.setInSeparationMode(current);
return result;
}
else return super.encodeURL(url);
}
}
(I've had to use String.contains() instead of String.startsWith() when testing the url for being a resource as context path and servlet path happen to prepend the passed url.)
However that does not help either by this moment. The reason is that Orchestra uses its own response wrapping that takes place in its RequestParameterFacesContextFactory, and this wrapping happens after our filter is hit. In this way Orchestra's wrapper turns out to be external to our one which results in our wrapper receiving url too late, when the url has already been intercepted and conversationContext appended.
To avoid this we have a way to make our response wrapper external to Orchestra's one by replacing effect from RequestParameterFacesContextFactory interceptor with RequestParameterServletFilter which actually does the same work. Unfortunately using another filter is not quite exquisite where we might not, but I don't see another way so far.
So, in web.xml place your filter after Orchestra's one:
<filter>
<filter-name>requestParameterFilter</filter-name>
<filter-class>org.apache.myfaces.orchestra.requestParameterProvider.RequestParameterServletFilter</filter-class>
</filter>
<filter>
<filter-name>myOrchestraFilter</filter-name>
<filter-class>mypkg.RfOrchestraParamControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestParameterFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>myOrchestraFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
Could not make previous solutions work. I did it this way:
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.stream.Collectors;
import javax.faces.application.ResourceHandler;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;
/**
* #author Felipe Riccetto
*/
public class RemoveConversationParamFilter implements Filter {
public static String removeQueryParameter(final String url,
final String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams()
.stream().filter(p -> !p.getName().equals(parameterName))
.collect(Collectors.toList());
if (queryParameters.isEmpty()) {
uriBuilder.removeQuery();
} else {
uriBuilder.setParameters(queryParameters);
}
return uriBuilder.build().toString();
}
#Override
public void destroy() {
// nothing
}
#Override
public void doFilter(final ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp2 = new HttpServletResponseWrapper(
(HttpServletResponse) resp) {
#Override
public String encodeURL(String url) {
String s = super.encodeURL(url);
try {
String urlPath = new URL(
((url.toLowerCase().startsWith("http://")
|| url.toLowerCase().startsWith("https://"))
? ""
: "http://fake")
+ url).getPath().toString().toLowerCase();
if (urlPath.endsWith(".js") || urlPath.endsWith(".css")
|| urlPath.endsWith(".png")
|| urlPath.endsWith(".jpeg")
|| urlPath.endsWith(".jpg")
|| urlPath.endsWith(".gif") || urlPath.contains(
ResourceHandler.RESOURCE_IDENTIFIER)) {
s = removeQueryParameter(s, "conversationContext");
}
} catch (MalformedURLException | URISyntaxException e) {
// ignore
}
return s;
}
};
chain.doFilter(req, resp2);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// nothing
}
}
web.xml:
<filter>
<filter-name>RemoveConversationParamFilter</filter-name>
<filter-class>RemoveConversationParamFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RemoveConversationParamFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
This last mapping must be the last in your web.xml
I want to fully internationalize my web page and have URLs translated to different languages. For example
http://example.tld/en/page
http://example.tld/de/pagina
all aforementioned pages should be handled by same controller and show same content (translated to desired language of course, this i know how to do - using message properties).
So my questions are:
How to achieve this functionality using #RequestMapping annotation?
Can I configure such aliases in properties or XML file and then "inject" them into controller? i.e.:
properties file:
alias.page=page:pagina
Controller
#RequestMapping("${alias.page}")
...
Or something like this.
Thanks for answers.
I've solved this issue by creating own implementation of servlet Filter
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class ExampleFilter implements Filter {
#Override
public void init(FilterConfig fc) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain fc) throws IOException, ServletException {
// Needed for getting URL from request
final HttpServletRequest hsRequest = (HttpServletRequest) request;
String url = hsRequest.getRequestURI().substring(
hsRequest.getContextPath().length()
);
/* This is just simple example. Here you can connect to database
or read properties or XML file with your configuration */
if ("/de/pagina".equals(url) || "/en/page".equals(url)) {
// change url and forward
RequestDispatcher dispatcher = request.getRequestDispatcher("/page");
dispatcher.forward(request, response);
} else {
// Do nothing, just send request and response to other filters
fc.doFilter(request, response);
}
}
#Override
public void destroy() {}
}
The controller method to handle request will then look like
#Controller
public class MultiLangController {
#RequestMapping(value="/page", method = RequestMethod.GET)
public String pageMethod() {
return ...;
}
}
Finally publish the new filter into web.xml
<filter>
<filter-name>MyExampleFilter</filter-name>
<filter-class>
com.path.to.filter.ExampleFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>MyExampleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And that should do the trick. If you don't need such flexibility, maybe UrlRewriteFilter (analogue of .htaccess for Java) will be sufficient.
Use a PathVariable in the #RequestMapping, with something before it so Spring can map the request:
#RequestMapping("myPage.do")
public String displayBasePage () {
return("pageName"); // a hard-coded JSP
}
becomes
#RequestMapping("/someidentifier/{pageName}.do"
public String displayBasePage (
PathVariable("pageName") String pageName) {
return(pageName); // the language-specific JSP, or whatever else you want to do
}
Spring will match the /someidentifier/ to this RequestMapping, and you can use whatever value you want for the pageName.
This might be a very odd question, but some hints or guidelines will be very helpful. We want to "session proof" our web site, basically make sure that two different sessions generate the same link structure (e.g. Both user-a and user-b will get the same links in the same web page).
The reason behind this test, is that our web site is generated by a content management solution that will generate different links(URLs) for each different session. That CMS (which is internally built) was fixed to return the same links across sessions.
The other situation with session proofing, is that our caching mechanism (SQUID) is giving TCP_MISSes all the time to our website, which makes us think that the complete site is marked dynamic and the caching server is having to reget all objects all the time.
Are you trying to verify that two different users do in fact see the same structure? One way would be to use something like wget to crawl the whole web site from two different IP addresses, and then compare the resulting trees.
This is what we do at amplafi.com
(h/t See http://randomcoder.com/articles/jsessionid-considered-harmful )
in the web.xml:
<filter>
<filter-name>DisableSessionIdsInUrlFilter</filter-name>
<filter-class>
com.amplafi.web.servlet.DisableSessionIdsInUrlFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>DisableSessionIdsInUrlFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And this java code:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
/**
* remove any session id from the Url.
*
*
* Ideally we would like to only remove this container-provided functionality
* only for public portions of the web site (that can be crawled by google)
* or for links that are to be bookmarked.
*
* #author Patrick Moore
*/
public class DisableSessionIdsInUrlFilter implements Filter {
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
/*
* Next, let's invalidate any sessions that are backed by a URL-encoded
* session id. This prevents an attacker from generating a valid link.
* Just because we won't be generating session-encoded links doesn't
* mean someone else won't try
*/
if (httpRequest.isRequestedSessionIdFromURL()) {
HttpSession session = httpRequest.getSession();
if (session != null) {
session.invalidate();
}
}
HttpServletResponseWrapper wrappedResponse = new ResponseWrapper(httpResponse);
chain.doFilter(request, wrappedResponse);
}
#Override
#SuppressWarnings("unused")
public void init(FilterConfig arg0) throws ServletException {
}
/**
* wraps response and prevense jsessionid from being encoded on the output.
*/
private static class ResponseWrapper extends HttpServletResponseWrapper {
ResponseWrapper(HttpServletResponse httpResponse) {
super(httpResponse);
}
#Override
public String encodeRedirectUrl(String uri) {
return uri;
}
#Override
public String encodeRedirectURL(String uri) {
return uri;
}
#Override
public String encodeUrl(String uri) {
return uri;
}
#Override
public String encodeURL(String uri) {
return uri;
}
}
}
If every page in your site has a different link then caching would definitely be broken for the site.
Are you asking how to verify that the links are the same per session?
Or are you asking how to ensure that links are the same per session?
For the former simply browsing from two different browsers and user logins should be sufficient.
As for ensuring the links are the same per session well... That would depend on your implementation which you indicated was inhouse.
Why are your URLs different? Are you storing session IDs in them?
If you are, you should probably move that to a cookie!
You could try using a Load test suite like Pureload to simulate multiple users hitting your site. Pureload can have multiple concurrent simulated users each make the same request and confirm that the results are as expected. Depending on how dynamic your results are this may help you test your bug.
You could write a test in Java using selenium. Add the assertions for the data you expect. Then you could loop over a list of multiple logins to see the test passes for each.
Selenium is pretty easy to get started in and tests can be written in many languages.
See here for details:
http://seleniumhq.org/projects/remote-control/
An example test would be something like this (pseudocode):
public void setUp() throws Exception {
setUp("http://mywebsite.com", "*chrome"); // to run the test in opera replace chrome with opera
}
public void testLoginWithMultipleUsers() {
for(loop over users) {
runLogin(user)
}
}
public void runLogin(User user) throws Exception {
selenium.open("/login.htm");
assertTrue(selenium.isTextPresent("Link1"));
assertTrue(selenium.isTextPresent("2003-2008"));
selenium.open("/account");
assertTrue(selenium.isTextPresent("2003-2008"));
selenium.waitForPageToLoad("30000");
etc...
Selenium provides lots of methods to check if links and other page elements are present and the test can even be recorded in the browser - give it a try!