How does the doFilter method of the FilterChainProxy work? - spring

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.

Related

Struts 2: Impossible get session after response is commited

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);
}
}
}

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.

JSF ajax request calls filter (should be ignored!)

I have some filters, which grab e.g. a parameter like "id" to check some right (used to load some contents). These filters should ignore all ajax-requests, because e.g. the rights does not have to be checked after every little request (only on page load)
The Problem is, that when I perform an ajax-request, it throws me a null-pointer, because I don't append the ID with ajax requests. I found out, that it still works, when I use and it fails, when I use (both perform ajax requests).
This is my filter:
public class ExtendedAccessFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
//ignore filter if it is an ajax-request (DOES NOT WORK if not p:commandButton!)
if(isAJAXRequest(req)){
chain.doFilter(request, response);
System.out.println("ABORT FILTER, AJAX");
return;
}
//Nullpointer thrown here (because no Id is submitted)
int requestedId = Integer.parseInt(request.getParameter("id"));
}
private boolean isAJAXRequest(HttpServletRequest request) {
boolean check = false;
String facesRequest = request.getHeader("Faces-Request");
if (facesRequest != null && facesRequest.equals("partial/ajax")) {
check = true;
}
return check;
}
}
Am I doing something wrong?
You are doing it right way. You can also do it using JSF API by checking if PartialViewContext exists and it is an Ajax Request
if(FacesContext.getCurrentInstance().getPartialViewContext() !=null &&
FacesContext.getCurrentInstance().getPartialViewContext().isAjaxRequest()) {
}

Alter request header before controller method using #RequestHeader is called

I have a few controllers that use the #RequestHeader annotation to get the logged in user. I cannot seem to find the correct way to alter the header before the method is called.
I already tried to wrap the request and using an interceptor (in the prehandle method, if I'm not mistaken) and pass along the request,but it seems like the headers are not being queried. An exception is thrown by the servlet dispatcher that the username is missing in the header.
Therefore I'm wondering whether someone knows how and when spring handles this annotation, so I can write the proper interceptor.
Don't have the code at hand, but if needed, I will post fragments later on. But the question is simple: how to inject a param into the request header when #RequestHeader is used on a controller method?
Kind regards,
Tom
First edit:
#Sotirios
I tried using the Filter, which works. But this is less convenient for me than the handlerinterceptor. Since I only need the filter for debugging. So again the better question: why isn't this not working with an interceptor?
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpRequest) {
#Override
public Enumeration getHeaders(String name) {
Enumeration headers = super.getHeaders(name);
if( isUseFilter() && Constants.REMOTE_USER.equalsIgnoreCase(name) ){
String user = super.getHeader(name);
headers = enumeration(asList(isEmpty(user)? getDebuggingUserId() :user));
}
return headers;
}
};
chain.doFilter(wrapper, response);
}

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