public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
String logged = (String) ((HttpServletRequest) request).getAttribute("loginstatus");
if(logged != null) {
out.print("ok");
} else {
out.print("not ok");
}
Why is the value of logged always null?
A filter is by default the very first thing which get executed on a HTTP request. The request attribtues are usually managed by server side code. Who/what should have set the request attribute before this filter does its job?
Aren't you confusing how HTTP requests/responses work? A request get finished/garbaged, including all attributes, when the associated response is finished. Every subsequent request is a brand new one which doesn't contain at all the same attributes as the previous one.
Don't you actually want to use the session scope? Do the following on login:
request.getSession().setAttribute("user", user);
And then the following in authentication filter:
if (((HttpServletRequest).getSession().getAttribute("user") != null) {
chain.doFilter(request, response); // Continue.
} else {
((HttpServletResponse) response).sendRedirect("login"); // Redirect to login.
}
See also:
How does a servlet environment work? ServletContext, HttpSession, HttpServletRequest/Response.
Servlet filters wiki page
Related
I have a simple implementation of login filter.
public class LoginFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#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);
if (session == null || session.getAttribute("loggedInUser") == null) {
response.sendRedirect(request.getContextPath() + "/login.jsp");
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {}
}
When I go to any registered page(i.e. /account?id=1) without session attribute loggedInUser, filter works fine. It redirects me to login page.
But if I go to non-exists page (i.e. /blablabla.html), filter redirects me to login page again. Is there any method to get 404 error on entering non-exists pages and redirect to /login on exists?
The bug is in the requirement: you filter all requests to deny access to guests but still want the request to be processed if it's a 404. This would be conceptually wrong: a 404 is still an applicative response in the sense that it gives the user a view of the internals of the system - so the user must be authorized before knowing that something is or is not there.
Another option is splitting your app in a public and a private zone:
/public/style.css
/public/app.js
...
/private/customer/123
/private/oder/8932
...
and just filter requests in the private zone.
Note: if you are concerned about the beauty of the URL consider that the /private/ prefix is not a requirement. The filter can be attached in such a way that any prefix can be omitted
Remember the filters are there to filter any incoming request or outcoming response, so actually the flow is something like this.
client -----> request ---- > filter ----> servlet dispather ----> resources
So now, unfortunately the request will be intercepted by the filter no matter is the resource exist or not, and this happens before the servlet dispather can get the request and get realize that the resource doesn't exist.
I hope, this explanation can answer your question.
Thanks.
I have a signup form that upon submit I need to:
Create a new user and signin the user.
If the username is already in the database I need to return 409 with error message.
I've created a custom filter which sets the HttpStatus
public class UserSignupFilter implements Filter {
...
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (userSignup(request)) {
if (userAlreadyExists(request)){
logger.info("Didnt create user since: "+e.getMessage());
response.sendError(HttpStatus.CONFLICT.value());
} else {
createUser(request);
}
}
chain.doFilter(request, response);
}
}
The problem is that I am getting sometimes:
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed.
Is there a better way to do it?
I am setting my security programmatically (Spring Security V 3.2) how can I use ExceptionTranslationFilter?
I don't think you should be calling:
chain.doFilter(request, response);
If all you want to do is send error code back. Just call return
logger.info("Didnt create user since: "+e.getMessage());
response.sendError(HttpStatus.CONFLICT.value());
return;
If you want to use the ExceptionTranslationFilter you need to throw an exception (not commit the response). As long as your filter is downstream if the security filter that should do it, or else put your filter in the security filter chain after the exception translation filter.
I've been playing around with code inspired from one of balusC's answers. Basicly its a webfilter that tries to login the user if there is a remember me cookie.
The login happens so that it first fetches the MyUser entity from the userService-EJB and places it in the MUserSessionBean which is a #SessionScoped jsf-ManagedBean
Problem is that in the first response, the user appear as not logged in.
But in the logs i can see it is being logged in and if I just request a page refresh in the browser, the response will show a logged in user.
I've tried to place a redirect in som various places after the login happens, but the page-layout breaks when i tried that..
How can i successfully display a logged-in user in first response?
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
MUserSessionBean mUserSessionBean = (MUserSessionBean) request.getSession(true)
.getAttribute("mUserSessionBean");
if (mUserSessionBean != null && mUserSessionBean.getCurrentUser() == null) {
String uuid = CookieUtil.getCookieValue(request, CookieUtil.COOKIE_NAME);
if (uuid != null) {
MyUser user = userService.findUserByUUID(uuid);
if (user != null) {
mUserSessionBean.setCurrentUser(user);
CookieUtil.addCookie(response, CookieUtil.COOKIE_NAME, uuid, CookieUtil.COOKIE_AGE);
} else {
CookieUtil.removeCookie(response, CookieUtil.COOKIE_NAME);
}
}
}
// pass the request along the filter chain
chain.doFilter(req, res);
}
From what I can see, you're not actually logging the user in on the first go around. All you're doing so far is simply setting a value in a session created without the container actually authenticating the user.
The remember-me feature requires you to authenticate against the realm on behalf of the user, using the credentials you pulled up with the remember-me token. You haven't done that. Even on the page refreshes where it appears the user has logged in, technically that's not true. It only appears as logged in because
if (mUserSessionBean != null && mUserSessionBean.getCurrentUser() == null)
resolves to true after the page has been requested the first time and skips all checking. Subsequent requests pass through simply because you forcefully placed an instance of MyUser in the session. In reality, the user is not logged in and this is probably responsible for the page breaks that occur elsewhere within your application.
You need to truly authenticate the user against the realm for peace to reign :)
if (uuid != null) {
MyUser user = userService.findUserByUUID(uuid);
if (user != null) {
request.login(user.username,user.password); //Authenticate!!!!
mUserSessionBean.setCurrentUser(user);
CookieUtil.addCookie(response, CookieUtil.COOKIE_NAME, uuid, CookieUtil.COOKIE_AGE);
} else {
CookieUtil.removeCookie(response, CookieUtil.COOKIE_NAME);
}
}
I did a quick-fix that solved the problem. Instead of storing the user-entity in a SessionScoped ManagedBean, I stored it directly in the session. So the filter method looks like this:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
MyUser user = (MyUser) request.getSession(true).getAttribute("user");
if (user == null) {
String uuid = CookieUtil.getCookieValue(request, CookieUtil.COOKIE_NAME);
if (uuid != null) {
user = ub.findUserByUUID(uuid);
if (user != null) {
request.getSession().setAttribute("user", user);
CookieUtil.addCookie(response, CookieUtil.COOKIE_NAME, uuid, CookieUtil.COOKIE_AGE);
} else {
CookieUtil.removeCookie(response, CookieUtil.COOKIE_NAME);
}
}
}
// pass the request along the filter chain
chain.doFilter(req, res);
}
The advantage is the filter doesnt have to wait for ManagedBean to be constructed. Then the SessionScoped-Bean peak into the session and fetch the entity the filter might have put there.
#ManagedBean
#SessionScoped
public class MUserSessionBean {
private MyUser currentUser;
#PostConstruct
public void init() {
if (currentUser == null) {
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
currentUser = (MyUser) context.getSessionMap().get("user");
}
}
...
}
I will look into to servlet-managed-authentication or what it may be called as Kollossus sugested, but the immediate problem is gone atleast.
I've implemented a simple filter that simply adds two Principles to the current session (see doFilter below). My problem is that this is firing when i request a resource but then I'm never able to see the resource becasue the FORM based login screen pops up. I'm attempting to get around the form based login with this particular filter (eventually using a quick-to-expire token) though nothing seems to seem to allow me to do this.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httprequest = (HttpServletRequest)request;
HttpServletResponse httpresponse = (HttpServletResponse)response;
HttpSession session = httprequest.getSession(true);
Subject subject = (Subject)session.getAttribute("javax.security.auth.subject");
if (subject == null){
subject = new Subject();
PlainUserPrincipal user = new PlainUserPrincipal("admin");
PlainRolePrincipal role = new PlainRolePrincipal("admin");
subject.getPrincipals().add(user);
subject.getPrincipals().add(role);
}
chain.doFilter(httprequest, httpresponse);
}
Due to security reasons you can't map servlets/filters on an URL pattern of /j_security_check when running Tomcat. The symptoms indicate that you're doing this. I say specifically Tomcat, because I've seen cases that it works on other (specific) container makes/versions. But you don't want to be dependent on that.
Rather filter on /*, or at least the same URL pattern as your security constraint, and intercept on the presence of the user principal and the absence of the session object.
if (request.getUserPrincipal() != null && session.getAttribute("subject") == null) {
Subject subject = new Subject();
// ...
session.setAttribute("subject", subject);
}
chain.doFilter(request, response);
I am developing a Java web application that will run on a secure intranet and does not require a user login. The application does, however, keep conversational state in an HttpSession. User input is not persisted to the database until they explicitly click a save button at some stage in the conversation. Until then, their input is retained in the HttpSession object. If their session expires, the user must be directed to a page that informs them of the session expiry.
This is working fine except for a problem with the redirect. When a user allows their session to sit idle for longer than the time defined in <session-timeout>, the session expires as expected. However, my attempt to redirect the user to a simple "Your session has expired" page seems to have backfired. The redirect works alright, but unless the user closes all the open browser windows on their desktop (not just the ones that were open to my web app page) they will continue being redirected to the "session expired" page forever.
Here are my constraints:
Client workstations use Internet Explorer. This is company-wide and will not change anytime soon.
Users will have mulitple instances of IE open on their desktop as part of their normal workflow. Telling them to close all instances of IE is not acceptable.
Not using any AJAX components in this web app
I've implemented the redirect with a Java Servlet Filter. Here are the relevant code snippets:
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
Validate.notNull(filterConfig);
Validate.isTrue(request instanceof HttpServletRequest);
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String requestedSessionId = httpServletRequest.getRequestedSessionId();
logger.info("requestedSessionId: " + requestedSessionId);
HttpSession httpSession = httpServletRequest.getSession(false);
if (requestedSessionId == null) {
// No need to do anything here if no session exists yet
logger.debug("No session exists yet");
filterChain.doFilter(request, response);
} else {
if (httpSession == null) {
Validate.isTrue(response instanceof HttpServletResponse);
HttpServletResponse httpServletResponse =
(HttpServletResponse) response;
handleSessionExpired(
httpServletRequest,
httpServletResponse);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Session OK | requested URL: " +
httpServletRequest.getRequestURL().toString());
}
filterChain.doFilter(request, response);
}
}
}
}
private void handleSessionExpired(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws IOException {
logger.warn("expired session | id: " +
httpServletRequest.getRequestedSessionId());
String expirationPageURL =
httpServletRequest.getContextPath() + "/" +
"SessionExpiredNotification.html";
httpServletResponse.sendRedirect(expirationPageURL);
}
The SessionExpiredNotification.html page is meant to be the end of the line. The user should close this browser window and open a new one if they want to start a new conversation. The problem is that the new browser window still wants to use the old session id value that was associated with the now invalidated session whenever the user has any other instances of Internet Explorer open on their desktop. This isn't specific to IE, as I have confirmed that Firefox behaves exactly the same way.
When this code is reached in my Filter:
String requestedSessionId = httpServletRequest.getRequestedSessionId();
logger.info("requestedSessionId: " + requestedSessionId);
I can see that the client-side browser is still holding on to the old session id value and requesting it over and over again.
I'm not sure if it is relevant, but my web application container is Tomcat 6.x.
MY QUESTION:
How can the server web app signal the client workstation that a session id is no longer valid such that the client will discard it?
If request.getSession(false) returns null, you should then create a new session. You can do this by calling request.getSession(true).
In other words, at no point in the code posted are you instructing the servlet container to create a new session and assign the current request to it.
This is the solution I've used. I should not have been calling sendRedirect() in my filter, since that will never return a new JSESSIONID to the client browser. I need to send an actual response that kills the old JSESSIONID Cookie, otherwise the client browser will just keep trying to use it. My first thought was to get the JSESSIONID Cookie from the request header, set it to be expired, then include the expired Cookie in the response so the client will act on the expiry. Other Stackoverflow users suggested that was not a clean solution, so I have scrapped that idea.
I replaced my handleSessionExpired() method in the Filter to use a RequestDispatcher. This will allow my Filter to dispatch the request to a custom "your session is expired" JSP page. Unlike a redirect, the RequestDispatcher will send a proper response to the client.
Here is the primary method in my Filter:
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
Validate.notNull(filterConfig);
Validate.isTrue(request instanceof HttpServletRequest);
HttpServletRequest httpServletRequest =
(HttpServletRequest) request;
String requestedSessionId = httpServletRequest.getRequestedSessionId();
logger.info("requestedSessionId: " + requestedSessionId);
HttpSession httpSession = httpServletRequest.getSession(false);
if (requestedSessionId == null) {
// No need to do anything here if no session exists yet
logger.debug("No session exists yet");
filterChain.doFilter(request, response);
} else {
if (httpSession == null) {
Validate.isTrue(response instanceof HttpServletResponse);
HttpServletResponse httpServletResponse =
(HttpServletResponse) response;
handleSessionExpired(
httpServletRequest,
httpServletResponse);
} else {
filterChain.doFilter(request, response);
}
}
}
My handleSessionExpired() method is very simple. This extra method call only exists because of another special use case that my filter needs to handle (but is not relevant to my original question).
private void handleSessionExpired(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws IOException, ServletException {
logger.info("expired session | id: " +
httpServletRequest.getRequestedSessionId());
sendSessionExpiredResponse(httpServletRequest, httpServletResponse);
}
My sendSessionExpiredResponse() is also quite simple. The call to getSession() will cause a new session to be created (since no valid HttpSession already exists at this point) and the JSESSIONID to be included in the response. That takes care of cleaning the obsolete session id on the client side. I set a request attribute "isExpired" so the session expiry JSP knows to display a message saying that the session is expired. I'm also using the same JSP page when a user manually ends a session, so I use the attribute to decide what text to display on the page.
private void sendSessionExpiredResponse(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws IOException, ServletException {
httpServletRequest.getSession(true); // force valid session to exist
httpServletRequest.setAttribute("isExpired", true);
RequestDispatcher rd = filterConfig.getServletContext()
.getNamedDispatcher("SessionExpired");
rd.forward(httpServletRequest, httpServletResponse);
}
The getNamedDispatcher() call gets the JSP via my entry in web.xml:
<servlet>
<servlet-name>SessionExpired</servlet-name>
<jsp-file>/SessionExpired.jsp</jsp-file>
</servlet>