Struts 2: Impossible get session after response is commited - spring-boot

I've already read the another article about it but it doesn't help me to find a generic solution for the entire application.
I've kept a big legacy web application (Struts2, Spring Boot, and Tomcat embedded) and I'm facing these following error.
2018-08-14 11:01:11.872 [http-nio-10010-exec-114] ERROR o.a.c.c.C.[.[localhost].[/].[jsp] - Servlet.service() for servlet jsp threw exception java.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2953)
at org.apache.catalina.connector.Request.getSession(Request.java:2367)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:592)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:537)
at org.apache.jasper.runtime.PageContextImpl.initialize(PageContextImpl.java:137)
at org.apache.jasper.runtime.JspFactoryImpl.internalGetPageContext(JspFactoryImpl.java:109)
at org.apache.jasper.runtime.JspFactoryImpl.getPageContext(JspFactoryImpl.java:60)
at org.apache.jsp.jsp.errorPage_jsp._jspService(errorPage_jsp.java:127)
Other example:
2018-08-14 11:01:11.870 [http-nio-10010-exec-114] ERROR o.a.c.c.C.[.[.[.[.q.q.c.RedirectResourceServlet] - Servlet.service() for servlet [com.mydomain.myapplication.control.RedirectResourceServlet] in context with path [] threw exceptionjava.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2953)
at org.apache.catalina.connector.Request.getSession(Request.java:2367)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:908)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
It occurs in different places like *Actions.java and JSP files.
Does someone know how and which is the best approach to intercept all request.getSession() to check if it was already committed? And how could I in a Struts 2 context create a new session? The request.getSession(true) doesn't work when the response.isCommitted() is true.
I've already tried to create a #WebFilter where I check and try to create a new session, but it doesn't work as expected. I also believe it is not the best approach.
#WebFilter(urlPatterns = "/*")
public class SessionValidatorFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(getClass().getName());
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (servletResponse.isCommitted() && servletRequest instanceof HttpServletRequest && servletResponse instanceof HttpServletResponse) {
logger.debug("Response was already committed. Trying to create a new session.");
try {
HttpSession session = ((HttpServletRequest) servletRequest).getSession(true);
if (session != null)
logger.debug("Session created.");
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception e) {
logger.debug("Error trying to create new Session.");
}
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
}

Related

How to exchange deprecated getExtraInformation() in context of UsernamePasswordAuthenticationFilter?

I am upgrading older Spring 2.5 code to Spring 3.0 (as a first step). During this I found the following problem:
The method getExtraInformation() from the type AuthenticationException is deprecated
The point is that this happens in a subclass of UsernamePasswordAuthenticationFilter:
#Override
protected void unsuccessfulAuthentication(final HttpServletRequest req, final HttpServletResponse res, final AuthenticationException authException) throws IOException, ServletException
{
req.setAttribute("exception", authException);
super.unsuccessfulAuthentication(req, res, authException);
if (authException instanceof CredentialsExpiredException)
{
final User user = ((UserDetailsImpl)authException.getExtraInformation()).getUser();
if (user.getCredentials().getUserCannotChange())
{
throw authException;
}
req.setAttribute("user", user);
req.setAttribute("msg", this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpiredPleaseChange"));
}
}
Until now I found no way to get the User in another way. So my question is how to get the user when it is no longer transported via the exceptions extra information?
The point is that the User is required here, because a decision has to be made if the exception is only rethrown or if a message should be presented to the user.
Btw. I have found no code that creates a CredentialsExpiredException with ExtraInformation, so I assume this will be done by the Spring/Spring Security Framework?
I think you have to step back and do this "extra information" check when Spring Security checks if there is CredentialsExpiredException. Assuming you are using the default settings , the CredentialsExpiredException is checked in the postAuthenticationChecks UserDetailsChecker in DaoAuthenticationProvider. The default implementation is DefaultPostAuthenticationChecks which you can override it with yours :
public class MyPostAuthenticationChecks extends DefaultPostAuthenticationChecks {
public void check(UserDetails user) {
UserDetailsImpl userImpl = (UserDetailsImpl)user;
if (user.getCredentials().getUserCannotChange()){
throw new CredentialsExpiredException("Some customized error message blalblbal");
}else{
super.check(user);
}
}
}

How to know the handler spring controller class and the handler method for the specified HttpServletRequest object

I need to somehow access the handler method with the reflection in the web filter and get the requestPattern value without passing the control to the dispatcher servlet. How can I do this?
I only have for that HttpServletRequest object and somehow I can also #Autowire there ApplicationContext object.
Thanks.
I have started to look how the DispatcherServlet itself decides which handler method it will give the control and implemented with such a way.
Here is the code:
//Initialization in filter constructor
....
final HandlerMapping handlerMappings = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, HandlerMapping.class, true, false).get("requestMappingHandlerMapping");
....
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
.....
Method mappingMethod = null;
try {
mappingMethod = ((HandlerMethod)handlerMappings.getHandler(request).getHandler()).getMethod();
RequestMapping requestMapping = mappingMethod.getAnnotation(RequestMapping.class);
final String requestPattern = requestMapping.value();
}
catch(Exception ex){
logger.error("Error getting the mapping bean for the request URL " + request.getRequestURI(), ex);
return;
}
....
}
On top of the proposal of #Arsen I would suggest to do like this:
try {
handlerMappings.getHandler(request);
String requestPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
} catch (Exception ex) {
logger.error("Error getting the mapping bean for the request URL " + request.getRequestURI(), ex);
return;
}
The getHandler() instruction is enoght to trig the spring lookup of the correct controller that will also set the HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE attribute, this is usually available in controllers but not in filters.
Be aware that these solutions will waste some resources, cause spring will do the lookup again after that.

servlet Filter is not allowing to load application resources

I am trying to prevent CSRF(Cross site request forgery). To prevent CSRF I have created filter which will filter every request.
After implementing the javax.servlet.Filter as expected filter does its job. But after implementing servlet filter my application resources are not loading properly.
CSS, jQuery, datatable, all the resources are not loading properly, some time they are loading, some time not.
Before implementing the filter it was working fine.
Sample error in firebug:
"NetworkError: 500 Internal Server Error - http://localhost:8080/myApp/resources/images/bg-report-content.jpg"
"NetworkError: 500 Internal Server Error - http://localhost:8080/myApp/resources/images/bg-header.jpg"
tworkError: 500 Internal Server Error - http://localhost:8080/myApp/resources/css/dataTables.bootstrap.css"
"NetworkError: 500 Internal Server Error - http://localhost:8080/myApp/resources/js/fnStandingRedraw.js"
"NetworkError: 500 Internal Server Error - http://localhost:8080/myApp/resources/js/dataTables.tableTools.js"
This How I implemented the Filter for CSRF
What I am doing is , I have created a class called LoadSalt whic creates the salt(random number). that random number I am taking in jsp. and along with the jsp I am sending it with the request.
LoadSalt calss
public class LoadSalt implements Filter{
public Cache<String, Boolean> csrfPreventionSaltCache= null;
HttpServletRequest httpReq=null;
//int count=0;
#SuppressWarnings("unchecked")
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//count++;
// Assume its HTTP
httpReq = (HttpServletRequest)request;
if(httpReq.getAttribute("csrfPreventionSaltCache")!=null)
{
csrfPreventionSaltCache= (Cache<String, Boolean>) httpReq.getAttribute("csrfPreventionSaltCache");
}
if(csrfPreventionSaltCache == null)
{
// creating a new cache
csrfPreventionSaltCache = CacheBuilder.newBuilder().maximumSize(5000)
.expireAfterAccess(5, TimeUnit.MINUTES).build();
// Setting to httpReq
httpReq.setAttribute("csrfPreventionSaltCache", csrfPreventionSaltCache);
}
// Generate the salt and store it in the users cache
String salt = RandomStringUtils.random(20, 0, 0, true, true, null, new SecureRandom());
//System.out.println("Salt: "+salt);
csrfPreventionSaltCache.put(salt, Boolean.TRUE);
// Add the salt to the current request so it can be used
// by the page rendered in this request
httpReq.setAttribute("csrfPreventionSalt", salt);
chain.doFilter(httpReq, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
}
The another filter which validate the salt
public class ValidateSalt implements Filter {
public Cache<String, Boolean> csrfPreventionSaltCache= null;
#SuppressWarnings("unchecked")
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Assume its HTTP
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResponse =(HttpServletResponse) response;
String salt =(String) httpReq.getAttribute("csrfPreventionSalt");
// Validate that the salt is in the cache
if(httpReq.getAttribute("csrfPreventionSaltCache")!=null)
{
csrfPreventionSaltCache = (Cache<String, Boolean>) httpReq.getAttribute("csrfPreventionSaltCache");
}
if(csrfPreventionSaltCache !=null && salt !=null && csrfPreventionSaltCache.getIfPresent(salt)!=null)
{
String metodName =httpReq.getMethod();
String saltFromJspPage = httpReq.getParameter("salt");
//String saltFromRequest =(String) httpReq.getAttribute("csrfPreventionSalt");
if(metodName.equalsIgnoreCase("POST"))
{
if(saltFromJspPage!=null && csrfPreventionSaltCache.getIfPresent(saltFromJspPage)!=null)
{
chain.doFilter(httpReq, response);
else
{
//throw new ServletException("Potential CSRF detected!! Please contact to system admin ASAP.");
httpResponse.sendRedirect("/myApp/pages/pageNotFound.jsp");
}
}
else
{
chain.doFilter(httpReq, response);
}
}
else
{
// Otherwise we throw an exception aborting the request flow
//throw new ServletException("Potential CSRF detected!! Inform a scary sysadmin ASAP.");
httpResponse.sendRedirect("/myApp/pages/pageNotFound.jsp");
}
}
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
}
servlet filter mapping in web.xml
<filter>
<filter-name>loadSalt</filter-name>
<filter-class>com.mpApp.security.LoadSalt</filter-class>
</filter>
<filter-mapping>
<filter-name>loadSalt</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>validateSalt</filter-name>
<filter-class>com.mpApp.security.ValidateSalt</filter-class>
</filter>
<filter-mapping>
<filter-name>validateSalt</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
What is wrong with my application?
Why servlet filter is not allowing to load resources?, though some time it does work , some time it does not,
What is the route cause of this?
Am I implementing servlet filter in wrong way.
please help.
The url pattern is too wide, will try to apply the salt to every request. Keep it to the dynamic parts you can set and check the salt value, like /transferOperationServlet or /prettyImportantServlet or *.jsp

How does the doFilter method of the FilterChainProxy work?

I was going through the source code of the org.springframework.security.web.FilterChainProxy class. I want to undersatnd how its doFilter method work. The following is the code.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
FilterInvocation fi = new FilterInvocation(request, response, chain);
List<Filter> filters = getFilters(fi.getRequestUrl());
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(fi.getRequestUrl() +
filters == null ? " has no matching filters" : " has an empty filter list");
}
chain.doFilter(request, response);
return;
}
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters);
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
}
My understanding is If I define custom filter not related to Spring in the web.xml , they will be included in the FilterChain object passed to the FilterChainProxy (I understand this happens via the DelegatingFilterProxy). Is that correct?
I think the IF block gets executed when there are non-spring Filters defined in the web.xml and when there are no Filters defined in the application context.
VirtualFilterChain here caters for Filters defined in the application text.
There is a return statement in the If block which prevents VirtualFilterChain section getting executed.
But how does this handle both Filters defined in the web.xml and the ones defined in the application context?
the "filterChain" parameter refers to the Servlet filters defined in web.xml. Look at this code in DelegatingFilterProxy.java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
...
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
The invokeDelegate(...) is what invokes FilterChainProxy's doFilter(...) method.
List<Filter> filters = getFilters(fi.getRequestUrl());
generates a list of Spring Security filters that match given url (some filters are listed in this section).
If no Spring Security filters match the requestUrl, the execution just moves on to the rest of the filters defined in web.xml. That's what the if() block is for.
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
This is where Spring Security filters' doFilter(...) methods get called. So, for example, if you have UsernamePasswordAuthenticationFilter as one of the filters configured, then virtualFilterChain.doFilter(...) will eventually invoke UsernamePasswordAuthenticationFilter's doFilter(...) method.

Setting/getting session attribute in JSF

I am trying to implement simple log in functionality in a JSF application. Following this answer, I have implemented an AuthenticationFilter. I am trying to set an object in my managed bean as :
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);
session.setAttribute("user", user);
doFilter method of AuthenticationFilter looks like this:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
if (((HttpServletRequest) req).getSession().getAttribute("user") == null){
((HttpServletResponse) resp).sendRedirect("../login.jsf");
} else {
chain.doFilter(req, resp);
}
}
I always get ((HttpServletRequest) req).getSession().getAttribute("user") == null (true). I have searched and applied many alternatives like (in my bean) :
facesContext.getExternalContext().getSessionMap().put("user", user);
request.getSession().setAttribute("user", user);
session.getServletContext().setAttribute("user", user); // DISASTER
I don't have a clue how to manage this thing. Seemingly duplicate question did'nt help either. What am I doing wrong? How can I make it work? Is there a good and clean way to do it using JSF capabilities?
I recommend you use a security library like the previous answer. There are too many ways to do this incorrectly...
But if you're dead set on doing it yourself, don't set this in the Session. Declare a ManagedBean and scope it as the session level. Have a property of the bean be the username.

Resources