Captcha servlet causes java.lang.IllegalStateException: PWC3999: Cannot create a session after the response has been committed - session

I am creating a CAPTCHA input using SimpleCaptcha, and did validation of the Captcha input. I am created a captcha input with the following codes.
HTML Code:
<form action="submit_proceed.do" method="post">
<img src="captchaImg" /><input type="text" name="captcha" value=""><br />
<input type="submit" value="Submit" name="submit" />
</form>
JavaServlet Code :
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Iterator;
import nl.captcha.Captcha;
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
List errorMsgs = new LinkedList();
try{
// Validate Captcha
HttpSession session = request.getSession(true);
String userCaptcha = request.getParameter("captcha");
Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME);
if (!captcha.isCorrect(userCaptcha)) {
errorMsgs.add("Please input the correct Captcha value.");
}
} catch (RuntimeException e) {
errorMsgs.add("An unexpected error: " + e.getMessage());
RequestDispatcher view = request.getRequestDispatcher("/error.view");
view.forward(request, response);
}
However I kept getting this error:
StandardWrapperValve[Captcha]: PWC1406: Servlet.service() for servlet Captcha threw exception
java.lang.IllegalStateException: PWC3999: Cannot create a session after the response has been committed
How do I create a session on my servlet? How can I resolve this issue?
Thank you very much.

Cannot create a session after the response has been committed
The exception message is pretty clear. There's means of an illegal state. You cannot set/change the response headers anymore when the response is already been committed. A response is committed when the headers are already been sent to the client side. This is a point of no return.
The response will be committed whenever the outputstream has been flushed (in)directly. That can happen when you write more than 2K to the response (depends on server config however), or did flush() manually, or did a sendRedirect() call.
Whenever the session needs to be created, the server needs to set a cookie in the response header so that it can identify the particular client and associate it with a HttpSession instance in the server memory. But that's not possible if the response is already committed, hence this exception.
Back to the root cause of this problem:
Servlet.service() for servlet Captcha threw exception
It is the servlet with the servlet-name of Captcha which caused this problem. You need to check/debug the entire request-response chain to see which servlets/filters are all invoked and which of them might have committed the response before the Captcha servlet was able to create the session. I can't help you more further as this information is missing in your topicstart.
At least, in the as far given code example, I see that you're unnecessarily calling response.getWriter(). I am not sure how the real world code look like, maybe you've stripped some lines, but chances are that you're actually writing to it and that might be the root cause of the problem. If you write too much or did a flush on it, then the resopnse will be committed. Do not write to the response inside a Servlet which is supposed to be a controller. There you normally use the JSP for. Or if it is for debugging purposes, use the stdout (System.out.println()) or a Logger.

The offending code is in nl.captcha.servlet.SimpleCaptchaServlet Servlet. If you change it to StickyCaptcha the problem will go away but specifically the following are the offending lines in SimpleCaptcha Servlet.
CaptchaServletUtil.writeImage(resp, captcha.getImage());
req.getSession().setAttribute(NAME, captcha);
Actually the code is writing the image file to response (which is usually greater than default 2k).
So for the time being you can use the StickyCaptcha or checkout the code and fix the issue.

Move this line:
HttpSession session = request.getSession(true);
to be first statement in doPost method.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
response.setContentType("text/html;charset=UTF-8");
//...
}
This should help.

Seems, that you already sent the header to the client before handling the post. If you create a session, the server needs to send the session ID to the client. This is usually done by sending a cookie. I suppose you check your code for whether you execute the action handling before you send anything back to the client.

create session by adding this
javax.servlet.http.HttpSession session = request.getSession();
try{
javax.servlet.http.HttpSession session = request.getSession();
// Validate Captcha
String userCaptcha = request.getParameter("captcha");
Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME);
if (!captcha.isCorrect(userCaptcha)) {
errorMsgs.add("Please input the correct Captcha value.");
}
} catch (RuntimeException e) {
...
}

This line
(Captcha) session.getAttribute(Captcha.NAME);
implies that the session should already exist before this particular request is being processed, hence I wonder is there is some initialisation you are supposed to do before sending your original form. This should be specified by the framework your are using.
For example you might have an initial serlvet that
creates the image, figures out the value of Capcha.Name
creates the session
session.setAttribute(Capcha.NAME, theName)
emit the html (or forward() to a JSP)
You could do that all in a JSP, or you could have your servlet forward to one.
Are there any examples of usage of this catcha library you could examine?

Related

Spring Session not working on Tomcat 8 when using Tiles - SESSION Cookie is not set as response is already included

I am using Spring Session 1.2.0.RELEASE on a Spring Boot Project. This is packaged as a war and deployed on Tomcat 8.
I have followed Spring Session documentation and configured it properly. The problem is that the entry point to the application is a controller that sets some value on session but the SESSION cookie is not sent to the browser.
Debugging I see that:
org.springframework.session.web.http.CookieHttpSessionStrategy.onNewSession() tries to write the cookie:
this.cookieSerializer
.writeCookieValue(new CookieValue(request, response, cookieValue));
org.springframework.session.web.http.DefaultCookieSerializer.writeCookieValue() sets the cookie in the response:
response.addCookie(sessionCookie);
The cookie isn't actually written. The underlying response object is org.apache.catalina.core.ApplicationHttpResponse. Its addCookie() method is:
/**
* Disallow <code>addCookie()</code> calls on an included response.
* #param cookie The new cookie
*/
#Override
public void addCookie(Cookie cookie) {
if (!included)
((HttpServletResponse) getResponse()).addCookie(cookie);
}
The problem is that included attribute, which at some point is set true, preventing the cookie from being added.
This happens when the jsp (using tiles) is being serviced:
UPDATE:
This is the moment when the response is being marked as included (when standard.jsp tiles layout is inserting an attribute:
<tiles:insertAttribute name="header" ignore="false"/>
To work around this problem I ended up creating a filter to enforce the creation of the session.
As seen, the first call to the controller didn't add the cookie because during the Tiles-JSP rendering the response was already marked as included. What I do is forcing the creation of the session in the filter and redirecting asking the very same requestURI. This way, since the call doesn't involve a tiles rendering the cookie is created and can be used right away in the next calls.
#Bean
#ConditionalOnExpression("${sessionEnforcerFilter.enabled:true}")
public FilterRegistrationBean sessionEnforcerFilter(){
logger.info("Registering sessionEnforcerFilter");
FilterRegistrationBean frb = new FilterRegistrationBean();
frb.setName("sessionEnforcerFilter");
frb.setFilter(new SessionEnforcerFilter());
frb.setUrlPatterns(Arrays.asList(new String[]{"/*"}));
return frb;
}
public class SessionEnforcerFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
if(httpServletRequest.getSession(false)==null){
logger.debug("sessionEnforcerFilter.doFilter () - Session is null - forcing its creation");
httpServletRequest.getSession();
String requestURI = httpServletRequest.getRequestURI();
logger.debug("sessionEnforcerFilter.doFilter () - Repeating request [{}]", requestURI);
httpServletResponse.sendRedirect(requestURI);
}else{
chain.doFilter(httpServletRequest, response);
}
}
#Override
public void destroy() {}
}
summary
Hold breakPoint in SessionRepositoryResponseWrapper.onResponseCommitted().
Check that the response object inside the SessionRepositoryRequestWrapper is a non-wrapped response. (included = false)
If it is a wrapped response object, make sure that the sessionRepositoryFilter comes first.
================
Spring-session is already handling the problem when 'DispatcherType.INCLUDE (included = true)'.
SessionRepositoryResponseWrapper.onResponseCommitted() is trying to addCookie to the original response object.
The sessionRepositoryFilter must be in the first position to wrap the original applicationHttpResponse passed by tomcat.
Problem Situation
The SessionRepositoryRequestWrapper receives the wrapped response and holds it.
When executing doInclude() in the servlet container, find the original reponse and wrap it with ApplicationHttpResponse (included = true).
Then, SetResponse (new wrapping response) to the innermost wrapper.
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.doInclude%28javax.servlet.ServletRequest%2Cjavax.servlet.ServletResponse%29
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat.embed/tomcat-embed-core/8.0.24/org/apache/catalina/core/ApplicationDispatcher.java#ApplicationDispatcher.wrapResponse%28org.apache.catalina.core.ApplicationDispatcher.State%29
Spring-session does an addCookie on the response (expecting the original response) stored in SessionRepositoryResponseWrapper.onResponseCommitted(), but it can not because it is set to 'included = true'.

Wildfly Database Module Authentication : How to record logins [duplicate]

Given an authentication mechanism of type FORM defined for a Java web app, how do you capture the login performed event before being redirected to requested resource? Is there any kind of listener where I can put my code to be executed when a user logs in?
I feel like defining a filter is not the best solution, as the filter is linked to the resource and would be invoked even when the user is already authenticated and asking for a resource. I'm wondering if there's some class/method triggered only by login event.
There's no such event in Java EE. Yet. As part of JSR375, container managed security will be totally reworked as it's currently scattered across different container implemantations and is not cross-container compatible. This is outlined in this Java EE 8 Security API presentation.
There's already a reference implementation of Security API in progress, Soteria, developed by among others my fellow Arjan Tijms. With the new Security API, CDI will be used to fire authentication events which you can just #Observes. Discussion on the specification took place in this mailing list thread. It's not yet concretely implemented in Soteria.
Until then, assuming FORM based authentication whereby the user principal is internally stored in the session, your best bet is manually checking in a servlet filter if there's an user principal present in the request while your representation of the logged-in user is absent in the HTTP session.
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
String username = request.getRemoteUser();
if (username != null && request.getSession().getAttribute("user") == null) {
// First-time login. You can do your thing here.
User user = yourUserService.find(username);
request.getSession().setAttribute("user", user);
}
chain.doFilter(req, res);
}
Do note that registering a filter on /j_security_check is not guaranteed to work as a decent container will handle it internally before the first filters are hit, for obvious security reasons (user-provided filters could manipulate the request in a bad way, either accidentally or awarely).
If you however happen to use a Java EE server uses the Undertow servletcontainer, such as WildFly, then there's a more clean way to hook on its internal notification events and then fire custom CDI events. This is fleshed out in this blog of Arjan Tijms. As shown in the blog, you can ultimately end up with a CDI bean like this:
#SessionScoped
public class SessionAuthListener implements Serializable {
private static final long serialVersionUID = 1L;
public void onAuthenticated(#Observes AuthenticatedEvent event) {
String username = event.getUserPrincipal().getName();
// Do something with name, e.g. audit,
// load User instance into session, etc
}
public void onLoggedOut(#Observes LoggedOutEvent event) {
// take some action, e.g. audit, null out User, etc
}
}
You can use Servlet filter on the j_security_check URI. This filter will not be invoke on every request, but only on the login request.
Check the following page - Developing servlet filters for form login processing - this works in WebSphere App Server, and WebSphere Liberty profile.
Having such filter:
#WebFilter("/j_security_check")
public class LoginFilter implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Filter called 1: " +((HttpServletRequest)request).getUserPrincipal());
chain.doFilter(request, response);
System.out.println("Filter called 2: " + ((HttpServletRequest)request).getUserPrincipal());
}
gives the following output:
// on incorrect login
Filter called 1: null
[AUDIT ] CWWKS1100A: Authentication did not succeed for user ID user1. An invalid user ID or password was specified.
Filter called 2: null
// on correct login
Filter called 1: null
Filter called 2: WSPrincipal:user1
UPDATE
Other possible way to do it is to use your own servlet for login, change the action in your login page to that servlet and use request.login() method. This is servlet API so should work even in Wildfly and you have full control over login. You just need to find out how wildfly passes the originally requested resource URL (WebSphere does it via cookie).
Servlet pseudo code:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user = request.getParameter("j_username");
String password = request.getParameter("j_password");
try {
request.login(user, password);
// redirect to requested resource
} catch (Exception e) {
// login failed - redirect to error login page
}

Exception handling in JSF ajax requests

How do I handle the exception and access the stack trace when an exception is thrown while processing a JSF ajax request? Right now, I only get the exception class name and message in a JavaScript alert when JSF project stage is set to Development. Even worse, there's no visual feedback whatsoever when JSF project stage is set to Production, and the server log doesn't show any information about the exception.
If that's relevant, I'm using GlassFish in Netbeans.
This problem is known and fleshed out in among others the OmniFaces FullAjaxExceptionHandler showcase.
By default, when an exception occurs during a JSF ajax request, the enduser would not get any form of feedback if the action was successfully performed or not. In Mojarra, only when the project stage is set to Development, the enduser would see a bare JavaScript alert with only the exception type and message.
The technical reason is that asynchronous requests (read: Ajax requests) by default don't return a synchronous response (read: a full page). Instead, they return small instructions and parts how to update the HTML DOM tree of the already-opened page. When an exception occurs, then these instructions are basically fully absent. Instead, some error information is sent back. You can usually handle them in the onerror attribute of the Ajax component and e.g. display an alert or perhaps perform a window.location change. At least, this is what JSF expected from you.
In order to catch and log the exception and optionally change the whole response, you basically need to create a custom ExceptionHandler. Standard JSF unfortunately doesn't provide a default one out the box (at least, not a sensible one). In your custom exception handler you will be able to get hands on the Exception instance causing all the trouble.
Here's a kickoff example:
public class YourExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public YourExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public void handle() throws FacesException {
FacesContext facesContext = FacesContext.getCurrentInstance();
for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator(); iter.hasNext();) {
Throwable exception = iter.next().getContext().getException(); // There it is!
// Now do your thing with it. This example implementation merely prints the stack trace.
exception.printStackTrace();
// You could redirect to an error page (bad practice).
// Or you could render a full error page (as OmniFaces does).
// Or you could show a FATAL faces message.
// Or you could trigger an oncomplete script.
// etc..
}
getWrapped().handle();
}
#Override
public ExceptionHandler getWrapped() {
return wrapped;
}
}
In order to get it to run, create a custom ExceptionHandlerFactory as follows:
public class YourExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public YourExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new YourExceptionHandler(parent.getExceptionHandler());
}
}
Which needs to be registered in faces-config.xml as follows:
<factory>
<exception-handler-factory>com.example.YourExceptionHandlerFactory</exception-handler-factory>
</factory>
Alternatively, you can go ahead using the OmniFaces one. It will fully transparently make sure that exceptions during asynchronous requests behave the same as exceptions during synchronous requests, using <error-page> configuration in web.xml.
See also:
Why FullAjaxExceptionHandler does not simply perform an ExternalContext#redirect()?
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same

Getting Request object from HttpSessionEvent

I have a Session listener which extends PortalSessionListener. I have sessionCreated(HttpSessionEvent httpSessionEvent) and sessionDestroyed(HttpSessionEvent httpSessionEvent) methods
When my Session gets invalidated (after 15 mins as per my configuration in web.xml), my listener is called and Session is invalidated.
In my listener I want to clear off Cookie values before logging out the User. So, I want Request and Response objects so that I can clear off Cookie values and set it in Response.
But, how can I get Request / Response objects in my listener which has HttpSessionEvent?
I tried below code. But, this is not getting invoked when my sessionDestroyed method is called or any other phase for that matter.
public void requestInitialized(ServletRequestEvent servletRequestEvent)
{
log.debug("Entered into requestInitialized method");
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
log.debug("Request object created is :" +request);
}
It has been suggested that implementing a Filter suits this requirement (for getting Request object). How that can be applied to my scenario?
This might be realated:
you can access the RequestContextHolder and get the value
String ipAddr =
((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes())
.getRequest().getRemoteAddr();
Posted here

Linking Server Side Message/Exception with AJAX request

I am making an ajax submit using Primefaces but I am having trouble linking my server side message with my ajax request.
Supposed I have this button that calls an action.
In my managed bean, do I need to raise an exception? How do I pass this message into my ajax request
public void checkout(ActionEvent event){
if(expression){
throw new CustomException("Account balance is not enough!");
}
}
public class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
}
How do I handle this case? Will my onerror javascript method be able to handle this?
Also, in one case supposed DB is down then how do I handle the exception? Do I have accessed to the error message
in my javascript function?
public void checkout(ActionEvent event){
try{
//DB is down
if(expression){
throw new CustomException("Account balance is not enough!");
}
}catch(Exception e){
}
}
As to your concrete question, you need to implement a custom ExceptionHandler for this which does basically the following when an exception occurs in an ajax request:
String errorPageLocation = "/WEB-INF/errorpages/500.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
This is not exactly trivial if you want to take web.xml error pages into account. You'd need to parse the entire web.xml for this to find the error page locations. The OmniFaces utility library has exactly such an exception handler, the FullAjaxExceptionHandler. You can find the full source code here and the showcase example here.
As to your concrete functional requirement, I wouldn't throw an exception when there's just an user error. This is fully recoverable. You need to create and add a FacesMessage and have ajax to update the <h:messages>, <p:messages> or <p:growl>. The PrimeFaces ones support an autoUpdate="true" which would auto-update itself on ajax requests. E.g.
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Insufficient balance", null));
with
<p:messages autoUpdate="true" />
Throwing an exception makes only sense in unrecoverable situations like as when the DB is down. Note that you usually don't throw such an exception yourself. In case of JPA it would already be thrown as PersistenceException which you in turn shouldn't catch in JSF managed bean, but just let it go.

Resources