0

I am using play framework 2.1.5.. As this version of play does not support in built session timeout, I started implementing it functionally... Here is my code..

// Global.java file
@Override
public Action onRequest(Http.Request request, Method method) {
    System.out.println(" Request .... " + request.toString());
    return new Action.Simple() {
        public Result call(Http.Context ctx) throws Throwable {
            String lastActionTime = ctx.session().get("lastActionTime");
            if (lastActionTime != null && sessionExpired(Long.valueOf(lastActionTime))) {
                ctx.session().clear();
                flash("success", "Session expired");
                return redirect("/login");
            }
            return delegate.call(ctx);
        }
    };
}


private boolean sessionExpired(Long lastActionTime) {
    maxSessionTime = Integer.parseInt(Play.application().configuration().getString("maxSessionTime"));
    return ((System.currentTimeMillis() - lastActionTime) / MILLISECONDS_IN_A_MINUTE) >= maxSessionTime;
}

I am overriding the on request Method...
Session_ timed_ out contains logic where I fetch the session's last inactive time and subtract it from the current time to see if the difference has exceeded the maximum session time.

The problem now is that on session expire, a request fires to /login and it gives a html page as response.

The response is as follows..

<html xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <link rel="shortcut icon" href="/assets/img/favicon.png">
    <link rel="stylesheet" href="/assets/stylesheets/dashboard.css" type="text/css" media="screen" charset="utf-8">
</head>
<body>
<header class="clearfix">
    <h1>Login</h1>
</header>
<form action="/login" method="POST" >   
<div id=login_form>
    <h1>Sign in</h1>  
    <p class="success">
        Session expired
    </p>
    <p>
        <input type="email" name="email" placeholder="Email" value="">
    </p>
    <p>
        <input type="password" name="password" placeholder="Password" >
    </p>
    <p>
        <button type="submit">Login</button>
    </p>
</div>
</form>
</body>
</html>

The method that handles /login request is:

public Result login(){
        return ok(login.render(form(Login.class)));
}

My login class:

public static class Login{
        public String email;
        public String password;

        public String validate(){
            if (User.authenticate(email, password) == null){
                return "Invalid username or password";
            }
            return null;
        }
    }

How can I render this html page to the view?

A few observations...

  1. When a page request is captured by on request method, redirect to login is also considered as a page request and the page is rendered properly....

  2. When an ajax request is intercepted by on request redirect to login is also made as ajax call (X-Requested-With is present in header)....

Now how can I convert this ajax call to page request??

Sajani
  • 117
  • 8
  • This is more of an observation than an answer, but when you redirect to your login action on session timeout, I believe ``onRequest`` will fire again and intercept the request for the login page. The way I see it, your code at the moment will go into an infinite loop on session timeout. I think you need to skip the session expiration check if the request is for the login page. – avik Dec 16 '13 at 11:27
  • If session time expires, I have cleared the cookie... In session_ timed_ out, I have also checked if the cookie exists., The first redirect to login clears the cookie... the next time it would go to delegate call... But thanks for the response... – Sajani Dec 17 '13 at 02:20
  • OK, that sounds reasonable. Coming back to your main question, I don't fully understand what you're asking for. Your code at present will serve back a login page to a user if their session expires. Is this not what you want? – avik Dec 17 '13 at 09:06
  • This code is currently serving back a html page as its response... But this page is not getting rendered in the browser's workspace.... – Sajani Dec 17 '13 at 13:06
  • Ah ok. I think I see the problem now :) – avik Dec 17 '13 at 14:01

3 Answers3

1

You need two different strategies depending on the nature of the request. For AJAX requests, you could reply with some relevant http error code and implement some client-side code to redirect to your login page. For normal page requests, your current strategy will work fine.

Here's another way to achieve this.

Community
  • 1
  • 1
mantithetical
  • 1,755
  • 2
  • 16
  • 21
0

Remove the static keyword from your anonymous inner class:

public Result call (final Context ctx) {
    ...
}
avik
  • 2,708
  • 17
  • 20
  • Hmmmm ok. What are you seeing on the browser? – avik Dec 17 '13 at 15:24
  • I have a dashboard page specific to my application... On session time out, an ajax call to login page is made which returns a html response... But still the dashboard page is what remains on the browser.... – Sajani Dec 17 '13 at 15:49
  • Are you sure you meant to say AJAX? All your session-expiry logic is held server-side, and all the code you have posted is server-side Java code. There shouldn't have to be an AJAX aspect to your session expiry solution, although the behaviour you're describing sounds like you may be using AJAX. Could you post your code for your session expiry method, your login action, and the HTML view template being served back from this action? – avik Dec 17 '13 at 16:55
  • I have posted the code including the session expiry method.... I am using play framework as my server-side and ember js as my client side framework.... – Sajani Dec 18 '13 at 02:43
  • Could you also post the action method that handles the ``GET /login`` request? – avik Dec 18 '13 at 10:38
  • Posted it.... What does the delegate.call(ctx) do??? public Result call(Http.Context ctx) throws Throwable { return redirect(routes.Application.login()); } ... This piece of code properly renders the login page... {Made this call only if request header is not "/login"}... – Sajani Dec 18 '13 at 10:46
  • ``delegate.call`` forwards the request onto the next action method that is in line to handle the request. It provides the _if the session is still active, continue to process the request as normal_ behaviour. – avik Dec 18 '13 at 12:30
  • Your action method looks fine. Check your ``routes`` file and make sure that ``GET /login`` maps to ``Application.login``. If that's ok, then it looks like your problem is in Global.java. I would debug your session expiry check and look for any logic errors there. Happy to move this discussion to chat if you need any more assistance. – avik Dec 18 '13 at 12:41
0

Finally figured out the answer...

When an incoming request is a page request, redirect to login is also considered as a page request.. Hence the response is rendered on the screen...

But, when the incoming request is an ajax request, redirect to login is treated as an ajax request...

So, to render this response...

//Global.java

@Override
    public Action onRequest(Http.Request request, Method method) {
        System.out.println(request.path());
          return new Action.Simple() {
            public Result call(Http.Context ctx) throws Throwable {
                String lastActionTime = ctx.session().get("lastActionTime");
                if (lastActionTime != null && sessionExpired(Long.valueOf(lastActionTime))) {
                    ctx.session().clear();
                    flash("success", "Session expired");
                    return temporaryRedirect("/login");
                }
                return delegate.call(ctx);
            }
        };
    }

    private boolean sessionExpired(Long lastActionTime) {
        maxSessionTime = Integer.parseInt(Play.application().configuration().getString("maxSessionTime"));
        return ((System.currentTimeMillis() - lastActionTime) / MILLISECONDS_IN_A_MINUTE) >= maxSessionTime;
    }

In the client side,

$.ajaxSetup({
       error: function (XMLHttpRequest, textStatus, errorThrown) {
            document.open();
            document.write(XMLHttpRequest.responseText);
            document.close();
        }
    });

Thanks Avik & Mantithetical... But is there any other better way to achieve the same?

Sajani
  • 117
  • 8