I'm using play framework 2.4.2
with Java and I want to validate that a user is logged in by intercepting all requests and checking if a session value is set. So I have extended the DefaultHttpRequestHandler and overridden the createAction
Method to intercept all requests. However, I have not found a good way to validate the session.
Option 1 - Fail
When I try to fetch the session value I get a runtime exception: There is no HTTP Context available from here
Below is the class I'm working with:
public class RequestHandler extends DefaultHttpRequestHandler {
@Override
public Action createAction(Http.Request request, Method method) {
session("loggedIn"); // Throws runtime Exception: no HTTP Context
}
}
Option 2 - Ugly
Since the session is technically a cookie I can retrieve the value from the header with code like the following:
for(String cookie : request.headers().get("Cookie")){
System.out.println("cookie: "+cookie);
}
But then I have to parse the cookie string which looks like the following line to get the loggedIn value. To dirty for my taste.
_ga=GA1.1.1508004144.1421266376; ki_r=; ki_t=1438789543788%378129908%3B1438789543788%3B1%3B1; PLAY_SESSION=0570411c3eb55ad230681539ddcfaa4220583fd-loggedIn=1
Option 3 - Too easy to forget the annotation
I notice some websites document a different approach and instead create an
action composition and add the appropriate annotation to every controller class or method.
The problem with this approach is it requires the developer to remember to add the annotation. I would prefer to reverse this to block every route by default and then add an annotation to the routes that do not need the validation.
A couple sites that document action composition:
- http://alexgaribay.com/2014/06/16/authentication-in-play-framework-using-java/
- https://www.playframework.com/documentation/2.2.1/JavaGuide4
Question
Is there a way to globally validate if a user should have access to a page and how do I get at the session variable?
*Please note that I'm not interested in using a third party plugin for authentication.
Even if I would re-consider using action composition, you can fix Option 1.
Create a custom annotation to mark the actions that don't need validation.
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NoAuthRequired {}
Then change your HttpRequestHandler implementation.
public class RequestHandler extends DefaultHttpRequestHandler {
@Override
public Action createAction(Http.Request request, Method actionMethod) {
return new Action.Simple() {
@Override
public F.Promise<Result> call(Http.Context ctx) throws Throwable {
// if the action is annotated with @NoAuthRequired or user is logged in delegate to it
if (actionMethod.isAnnotationPresent(NoAuthRequired.class) || ctx.session().containsKey("loggedIn")) {
return delegate.call(ctx);
}
// otherwise, block access
else {
return F.Promise.pure(forbidden("You're not allowed"));
}
}
};
}
}
In this way, every route requires validation unless explicitly annotated.
As you can see from the code, the session is available through the Context.
来源:https://stackoverflow.com/questions/31841240/intercept-request-and-check-authorization-in-playframework