how to add custom response HTTP header in Servlet Filter that depends on status code returned from app - servlets

for logging (MDC) I need to set a custom header when the response is failed as the following:
public class MDCFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
MDC.put("trackingId", UUID.randomUUID().toString());
try {
chain.doFilter(request, response);
} catch (Throwable throwable) {
//suppress
}
if (((HttpServletResponse) response).getStatus() >= 300) {
((HttpServletResponse) response).setHeader("x-tracking-id", MDC.get("trackingId"));
// ((HttpServletResponse) response).getHeader("x-tracking-id"); //this returns null
}
MDC.remove("trackingId");
}
}
but this does not work, no header is set. if I set the header before chain.doFilter it works, but I need to set this filter as late as possible, I need to know if the response status is OK or failed.

You need to set the header as soon as the status is set, not after when the response is already sent to the client. That's a point of no return. You can use a HttpServletResponseWrapper to decorate the behavior of setStatus() method.
For example,
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
MDC.put("trackingId", UUID.randomUUID().toString());
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
#Override
public void setStatus(int status) {
super.setStatus(status);
if (status >= 300) {
setHeader("x-tracking-id", MDC.get("trackingId"));
}
}
});
}
finally {
MDC.remove("trackingId");
}
}
Note that I also fixed the sloppy exception handling.

Related

HttpServletRequest UTF-8 Encode

I have a problem with HttpServletRequest when I send a params which has a value = "Hành động". But I use
String categoryName = request.getParameter("categoryname");
Param categoryName will be "Hành ??ng". I don't understand that error.
Somebody help me???
Try to apply following filter on your servlet:
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getCharacterEncoding() == null) {
request.setCharacterEncoding("UTF-8");
}
if (chain != null) {
chain.doFilter(request, response);
}
}

Write to outputStream after HTTPServletResponse is committed

Is it possible to write response bytes to OutputStream of HTTPServletResponse even if Response is committed?
I have a Servlet Filter which forwards request to CXFServlet by calling doFilter. After doFilter I explicitly want to write bytes in OutputStream. Currently I am not getting response when I write to OutputStream after response is committed.
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
HttpServletResponse wrappedResp = new HttpServletResponseWrapper(
response) {
public PrintWriter getWriter() {
return pw.getWriter();
}
public ServletOutputStream getOutputStream() {
return pw.getStream();
}
};
filterChain.doFilter(servletRequest, wrappedResp);
byte[] bytes = pw.toByteArray();
response.getOutputStream().write(bytes);
response.getOutputStream().flush();
response.getOutputStream().close();
// Do logging after response is sent to client.
}
I see two possible issues:
1) If you receive the content in byte[] bytes from pw (put a breakpoint on that line) that means the filter and wrapper did their job and that your servlet (or other inner filter) did close the real response somehow. Or - try using the Writer instead, I guess ByteArrayPrintWriter does have a method that returns char[] so you can use response.getWriter() to write the content.
2) If there is no content in byte[] bytes then ByteArrayPrintWriter does not collect the content correctly. Note that implementation may (or will) close the response writer, so maybe implementation of ByteArrayPrintWriter closes and thats why you don't get any content.
You can also try the following:
BufferResponseWrapper wrappedResp = new BufferResponseWrapper(response);
filterChain.doFilter(request, wrappedResp);
// get buffered content
char[] chars = wrapper.getBufferContentAsChars();
// modify it somehow
chars = ArraysUtil.join(chars, "peep!".toCharArray());
// flush
wrapper.writeContentToResponse(chars);
from the Jodd project: BufferResponseWrapper.java.
EDIT
Here is the similar filter that worked for me on Tomcat, using just Java classes:
public class MyFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(baos);
HttpServletResponse wrappedResp = new HttpServletResponseWrapper((HttpServletResponse) servletResponse) {
#Override
public PrintWriter getWriter() {
return pw;
}
#Override
public ServletOutputStream getOutputStream() {
return new ServletOutputStream() {
#Override
public void write(int b) {
baos.write(b);
}
};
}
};
filterChain.doFilter(servletRequest, wrappedResp);
byte[] bytes = baos.toByteArray();
servletResponse.getOutputStream().write(bytes);
}
public void destroy() {
}
public static class ByteArrayPrintWriter extends PrintWriter {
public ByteArrayPrintWriter(OutputStream out) {
super(out);
}
}
}
Please note it is a quick-and-dirty implementation, used just for the illustration purposes.

Redirect to login page without using spring security

I am new to spring and creating an web application ,
I want to redirect to login page always when user is not authenticated and without using spring security.xml??
Is it possible with session management??
A simple way would be to use a 'HandlerInterceptorAdapator':
public class CheckUserInterceptor extends HandlerInterceptorAdapter {
#Resource
private UserSession userSession;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws IOException {
if (request.getServletPath().equals("/login.htm")) {
return true;
}
String username = userSession.getUsername();
// If the username has not been set by the login controller
if (username != null) {
return true;
} else {
response.sendRedirect("login.htm");
return false;
}
}
}
In this case you need to declare the interceptor in the Spring XML file:
<mvc:interceptors>
<bean class="fr.unilim.msi.dad.web.mvc.CheckUserInterceptor" />
</mvc:interceptors>
Another approach, if for instance your Spring MVC controller are not configured to handle all requests, is to use a filter at the servlet level:
public class AccessControlFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpSession session = ((HttpServletRequest) request).getSession(true);
User user = (User) session.getAttribute("user");
if (user == null) {
String urlRoot = ((HttpServletRequest) request).getContextPath();
((HttpServletResponse)response).sendRedirect(urlRoot + "/login.jsp");
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
}
I guess you can do something like below in your login controller:
if(isUserAuthenticated())
{
return "home";
}
else
{
session.invalidate();
return "redirect:login";
}

redirecting from a servlet filter to jsf returns the actual jsf code not rendered to html

below is my code;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package racms;
import java.io.IOException;
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
#WebFilter("/faces/*")
public class AuthenticationFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
// If you have any <init-param> in web.xml, then you could get them
// here by config.getInitParameter("name") and assign it as field.
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String pageRequested = request.getRequestURI().toString();
//try{
//FacesContext fctx = FacesContext.getCurrentInstance();
//NavigationHandler myNav = fctx.getApplication().getNavigationHandler();
if(session==null){
session = request.getSession(true); // will create a new session
response.sendRedirect("Login.xhtml");
//myNav.handleNavigation(fctx, null, "Login");
}else if(session==null && pageRequested.contains("Login.xhtml")){
// session.getAttribute("user");
chain.doFilter(request, response); // continue filtering
}else if((session.getAttribute("user")== null) && (!pageRequested.contains("Login.xhtml"))){
response.sendRedirect("Login.xhtml");
//myNav.handleNavigation(fctx, null, "Login");
}else {
chain.doFilter(request, response);
}
//}catch(Exception e){
// System.out.println("Error :"+ e);
//}
/*if ((((HttpServletRequest) req).getSession().getAttribute("user") == null)) {
FacesContext fctx = FacesContext.getCurrentInstance();
NavigationHandler myNav = fctx.getApplication().getNavigationHandler();
myNav.handleNavigation(fctx, null, "Login");
//response.sendRedirect(request.getContextPath() + "/Login.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}*/
}
#Override
public void destroy() {
// If you have assigned any expensive resources as field of
// this Filter class, then you could clean/close them here.
}
}
If i use FacesContext.getCurrentInstance(), java.lang.Nullpointer Exception occurs;
If i use response.sendRedirect("Login.xhtml"); it shows a blank page, if i do view source, i can see the source of Login.xhtml in jsf. it is not rendered to html..
what i want to do is: if the user is not logged in and accessing any page then send him to Login.xhtml, if the user is on Login.xhtml then show him the page to login.
please help..
Redirect to an URL that matches the FacesServlet mapping. Apparently it is mapped on /faces/* instead of *.xhtml. Then redirect to faces/Login.xhtml.
Here's a rewrite, which simplifies the logic:
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
User user = (session != null) ? (User) session.getAttribute("user") : null;
String loginURL = request.getContextPath() + "/faces/Login.xhtml";
if (user == null && !request.getRequestURI().equals(loginURL)) {
response.sendRedirect(loginURL);
} else {
chain.doFilter(request, response);
}
}

How do I specify the URL to a WebSocketServlet in Jetty?

I have read Creating a WebSocket-Chat-Application with Jetty and I would like to create a simple echo websocket using a WebSocketServlet on Jetty.
I have created my WebSocketServlet like this:
public class ChatSocketServlet extends WebSocketServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html><body><h1>Chat</h1></body></html>");
}
#Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
return new ChatWebSocket();
}
class ChatWebSocket implements OnTextMessage {
private Connection connection;
#Override
public void onClose(int closeCode, String message) {
System.out.println("onClose");
}
#Override
public void onOpen(Connection connection) {
this.connection = connection;
}
#Override
public void onMessage(String data) {
try {
connection.sendMessage(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
But what URL should I use to connect to this websocket? It seems that I can't use the #WebServlet (e.g. #WebServlet("/HelloServlet")) as I can with HttpServlet. I have tried to connect to:
ws://localhost:8080/MyJavaWeb/ChatWebSocket
but it returns Not Found. Is there any other annotation to specify the URLs for WebSocketServlet? And I don't know the URL for getting the output from doGet on this servlet either.

Resources