Struts2 with login interceptor

25 comments

Thursday, December 02, 2010

Hi All,
This is my first blog and this blog is for those who are not beginners because I am not going to explain HelloWorld application here. So if you are a beginner of Struts2 then follow the link below
http://www.mkyong.com
About application
It’s an application of Struts 2 with LoginInterceptor this will perform these tasks:-
  1. Check user exist in session or not.
  2. Runs before every action to check .If some one try to access direct url of welcome page and not present in session then it will redirect towards login page.
  3. If user already in session then call the action called by user.
  4. If session time out and user clicks on any link, then also redirect towards login page.
Package Structure

jar needed

web.xml
web.xml with struts2 configuration and defining session time out 1 min.

 LoginApp
 
 
  struts2
  org.apache.struts2.dispatcher.FilterDispatcher
 

 
  struts2
  /*
 
 
 
 1
 
 
 
  login.jsp
 

struts.xml
Here we configure our custom interceptor named LoginInterceptor defining loginStack as default stack.

 

 

  
   
                  
   
    
    
   
  

  

    
   login.jsp
  

  
   /pages/welcome.jsp
   login.jsp
  
  
  
   login.jsp
  
  
 





LoginInterceptor.java
LoginInterceptor class extends AbstractInterceptor  and checking user present in session or not.
package com.javawithbrain.common.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.xwork.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.StrutsStatics;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class LoginInterceptor extends AbstractInterceptor implements
  StrutsStatics {

 private static final Log log = LogFactory.getLog(LoginInterceptor.class);
 private static final String USER_HANDLE = "loggedInUser";
 private static final String LOGIN_ATTEMPT = "loginAttempt";


 public void init() {
  log.info("Intializing LoginInterceptor");
 }

 public void destroy() {
 }

 public String intercept(ActionInvocation invocation) throws Exception {

  final ActionContext context = invocation.getInvocationContext();
  HttpServletRequest request = (HttpServletRequest) context
    .get(HTTP_REQUEST);
  HttpSession session = request.getSession(true);

  // Is there a "user" object stored in the user's HttpSession?
  Object user = session.getAttribute(USER_HANDLE);
  if (user == null) {
   // The user has not logged in yet.

   // Is the user attempting to log in right now?
   String loginAttempt = request.getParameter(LOGIN_ATTEMPT);

   /* The user is attempting to log in. */
   if (!StringUtils.isBlank(loginAttempt)) {
    return invocation.invoke();
   }
   return "login";
  } else {
   return invocation.invoke();
  }
 }

}
LoginAction.java
LoginAction class with simple bussiness logic you can login with any username and password but cannot left blank the mandatory fields.
package com.javawithbrain.user.action;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {
 private String userName;
 private String password;

 public String getUserName() {
  return userName;
 }

 public void setUserName(String userName) {
  this.userName = userName;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 // all struts logic here
 public String execute() {

  ServletActionContext.getRequest().getSession().setAttribute("loggedInUser", userName);
  return "login-success";

 }

 // simple validation
 public void validate() {
  if(userName.trim().equalsIgnoreCase("") || password.trim().equalsIgnoreCase(""))
  {
   addActionError("Username and Password cann't be blanked");
  }
  else
  {
   addActionMessage("You are valid user!");
  }
    
 }
}
LogoutAction.java
LogutAction class calls when user click on logout link.
package com.javawithbrain.user.action;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class LogoutAction extends ActionSupport {
 
 // all struts logic here
 public String execute() {
  
  ServletActionContext.getRequest().getSession().invalidate();
  addActionMessage("You are successfully logout!");
  return "logout";

 }
}
Login.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login page</title>

<link rel="stylesheet" type="text/css" href="css/style.css" />

</head>
<body>

<s:if test="hasActionErrors()">
 <div class="errors"><s:actionerror /></div>
</s:if>
<s:if test="hasActionMessages()">
 <div class="welcome"><s:actionmessage /></div>
</s:if>

<s:form action="loginAuthenticaion.action">

 <s:hidden name="loginAttempt" value="%{'1'}" />
 <s:textfield label="UserName" name="userName" />
 <s:password label="Password" name="password"/>

 <s:submit label="Login" name="submit" />
</s:form>
welcome.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="java.util.Date"%><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome page</title>

<link rel="stylesheet" type="text/css" href="css/style.css" />

</head>

<body>
<div class="content">Struts 2 with Login Interceptor <a
 style="float: right;" href="logout">logout</a> <br/>
<br/>
<s:if test="hasActionMessages()">
 <div class="welcome"><s:actionmessage /></div>
</s:if> 
<br/>
<h4>Hello : <%=session.getAttribute("loggedInUser").toString()%> <br/>
Login time : <%=new Date()%></h4>

</div>

</body>
</html>
style.css
.errors {
 background-color: #FFCCCC;
 border: 1px solid #CC0000;
 width: 400px;
 margin-bottom: 8px;
}

.errors li {
 list-style: none;
}

.welcome {
 background-color: #DDFFDD;
 border: 1px solid #009900;
 width: 300px;
}

.welcome li {
 list-style: none;
}

.content{
 padding: 5px;
 height: 400px;
 
 border: 2px solid #d0d1d0;
}
Now paste these files according to package structure and run it by typing http://localhost:8080/LoginApp
If u tries to hit direct url without login like  http://localhost:8080/LoginApp/loginAuthenticaion then it will redirect towards login page.
Next time i will try to write some more interesting you can also give me some suggestion about new topics.
Thanks,
Have a great day !!!

25 comments :

  1. What about redirecting user to the page, that user wanted to go (not to the login.jsp)? Another problem - what about parameters that user has filled in the form, then he clicked submit (when session was timed out), all are gone? How to "refill" the form and automatically submit it to the url that user wanted before he was logged in

    ReplyDelete
  2. Hi Anonymous user

    -For the first problem
    In interceptor you can check and provide the jsp what you want.

    -For the second problem
    It is not possible to fill the form again after submitting(if session has been expired).
    Reason - There are four ways for maintaining the session state:
    1. URL Rewriting
    2. By Using hidden value
    3. Can use Cookie
    4. Session

    -URL Rewriting and Hidden Parameter maintains the session only one page to another page but in your problem user has to maintain the session for at least three page such as:
    a) User will attempting to fill the value(say it registraion.jsp)
    b) after submitting(when session is timeout) it will go on login.jsp to attempt the login again.
    c) after login it will again redirect to registration.jsp

    It is easy task to redirect on again registration.jsp but for the value it cant.

    - And when we talk about Cookie, it will completely depends on browser, means it can be deleted any time by the user, You can do with cookie but very difficult task to make it generic for all pages, It may work for only one specific page and there must be a specific logic for that. But dont go this technique because its not secure as well as I am not sure about it.

    -And for the Session, you might already aware with if session has been expired we can't use it until unless its again created by the next request or at the time of login.

    Please correct me if anywhere, I am wrong

    Thanks!

    ReplyDelete
  3. Seems to be posted same content for both login and welcome pages

    ReplyDelete
  4. Sorry for my mistake and thanks for finding the mistake in my blog. I forgot to post the login.jsp now i correct my mistake.
    Again thanks for your comment.

    ReplyDelete
  5. Hi Anonymous,

    Take a look on this article for redirecting to same page after relogin follow this link


    http://today.java.net/pub/a/today/2007/01/04/transparent-state-management-using-decorator-pattern.html


    nice tutorial. This may be solved your problem and you can integrate that code also in this login application.
    so Enjoy ....

    ReplyDelete
  6. Thank You sooooooooooooooooooooooooooooooooooooooooooooooooo much.............................


    i have been searching for this in google... and trying to implement .. but i could not......... because of this blog today i got what i want 100%.

    ReplyDelete
  7. Hi, Before this post i was really struggling to implement LoginInterceptor but when i came across this post i got 100% what i was looking for. I have a little suggestion in this post that is i implemented this code and run it and it worked successfully but i have a scenario which is when i logged in and after logging in i tried the home page or login page and it took me to the login page which should not be like this. Infact it should go to the welcome page if i am already logged in. I want to know what should be in the login page so that when a user is already logged in it should redirect to welcome page other wise to login page.

    ReplyDelete
  8. Hi Rambrij Chauhan,
    I can't run project, please send source code into my email: doantrunghung_xp@yahoo.com

    Thanks so much

    ReplyDelete
  9. the struts.xml that you define does not adhere to the DTD for struts 2.0

    I had to modify it to make it work. The element is only allowed to have intercepor and interceptor-ref elements.

    Thanks for the tutorial.

    ReplyDelete
  10. Hi, Before this post i was really struggling to implement LoginInterceptor but when i came across this post i got 100% what i was looking for. I have a little suggestion in this post that is i implemented this code and run it and it worked successfully but i have a scenario which is when i logged in and after logging in i tried the home page or login page and it took me to the login page which should not be like this. Infact it should go to the welcome page if i am already logged in. I want to know what should be in the login page so that when a user is already logged in it should redirect to welcome page other wise to login page.
    I also have the same issue ... Please help me to solve this issue...
    Thanks

    ReplyDelete
  11. Looks like there is a fatal flaw to this code, all you have to do, to by pass the authentication, is add a parameter "loginAttempt" to your request. Try accessing welcome.jsp?loginAttempt=true without logging in. A better approach would be check the invoking class before allowing the loginattempt switch.

    ReplyDelete
    Replies
    1. Yes you are right, but i have write this post just for demonstrating the interceptor only . You can implement this as you want. By the way nice catch .

      Thanks,
      Dear

      Delete
    2. You really should fix the code, put a disclaimer up or take the page down. There's too much garbage "demonstration" code already and this just adds to it. I'm not for posting code that is "perfect" so people can copy/paste without thinking/understanding but to post this by all accounts "complete" code is poor.

      Delete
    3. Ok , I agreed. I will post the complete and perfect code in this weekend.
      Thanks again.

      Delete
  12. Struts.xml file is giving error. Can any one help me? I think the DTD is different.

    ReplyDelete
  13. Hai i've a error in struts.xml
    at

    //here the error
    "The content of element type "constant" must match "null".

    what i need to do...



    ReplyDelete
  14. Hi,

    I am getting below exception while i run.Could you please help.

    SEVERE: The content of element type "interceptor-ref" must match "(param)*". at (null:19:24)
    org.xml.sax.SAXParseException: The content of element type "interceptor-ref" must match "(param)*".
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.handleEndElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.endElement(Unknown Source)
    at com.sun.org

    ReplyDelete
  15. I would suggest if you can add more code in LoginAction just to validate the username and password with any constant string like:

    LoginValidate.isLoginValid(uName, pwd) ? SUCCESS : INPUT;

    or something in that way and below class for validation:

    Class validating username and password:

    public class LoginValidate {
    public static boolean isLoginValid(String uName, String pwd)
    {
    return "admin".equals(uName) && "admin".equals(pwd)?true:false;
    }
    }

    It will help all folks to find the perfect login App with Struts 2 at your blog and people can impliment validation in there own way w.r.t. your code.

    Thanks!!

    ReplyDelete
  16. Nice code man. Wanted to share javascript code which can be used to give alert to user when the session is about to timeout.

    ReplyDelete
  17. i get the following error while compiling LoginInterceptor.java

    LoginInterceptor.java:22: cannot find symbol
    HttpServletRequest request=(HttpServletRequest)context.get(HTTP_REQUEST);

    ReplyDelete
    Replies
    1. Hi Abinash,

      HTTP_REQUEST is a Constants used by Struts. The constants can be used to get or set objects out of the action context or other collections.

      Example:

      ActionContext.getContext().put(HTTP_REQUEST, request);

      or

      ActionContext context = ActionContext.getContext();
      HttpServletRequest request = (HttpServletRequest)context.get(HTTP_REQUEST);

      For more detail follow the below link

      http://struts.apache.org/release/2.0.x/struts2-core/apidocs/org/apache/struts2/StrutsStatics.html

      Delete
  18. its good example ........... Thnxxxxxx

    ReplyDelete