问题
I have a java User object which i want to pass from my Login controller to my Dashboard controller. ive read a bunch and im trying the following implementation
public static Result authenticate(){
Form<LoginForm> loginform = Form.form(LoginForm.class).bindFromRequest();
if(loginform.hasErrors()){
return badRequest(login.render(loginform));
}
else{
session().clear();
AppUser user = AppUser.getUserByEmail(loginform.get().email);
Context.current().args.put("user", user);
return redirect(routes.DashBoard.index());
}
and my Dashboard controller
public static Result index(){
AppUser user = (AppUser) Context.current().args.get("user");
return ok(views.html.dashboard.index.render(user));
}
this results in nullpointerexception
this is because the two requests are not the same request of course.
how to solve this issue the a play friendly way By the way ive seen in the documentation something about action composition but i did not understand it.
回答1:
Though I agree with Mauno's answer, I wanted to add that it is possible to use the cache API in play to do what you are asking.
One thing to keep in mind is that the cache is the same across multiple users. If you want to be able to access a particular user object from the cache, you're going to have to have a unique id for it. You're also going to store that id between requests. A play session is an easy way to do this.
here's an example
public static Result authenticate(){
Form<LoginForm> loginform = Form.form(LoginForm.class).bindFromRequest();
if(loginform.hasErrors()){
return badRequest(login.render(loginform));
}
else{
session().clear();
AppUser user = AppUser.getUserByEmail(loginform.get().email);
String uuid= java.util.UUID.randomUUID().toString();
Cache.set(uuid,user);
//store uuid in session for extracting the proper user from cache later
session("uuid",uuid);
return redirect(routes.DashBoard.index());
}
public static Result index(){
//gather uuid stored in session (cookies)
String uuid = session("uuid")
AppUser user = Cache.get(uuid);
return ok(views.html.dashboard.index.render(user));
}
Also these objects won't have a predictable lifespan so you're going to have to make sure you check for their existence and potentially recreate the objects.
edit: I am unsure of this last statement, in playframework 1 the objects in the cache would be removed if they weren't used for a while, however it appears from this post that if you don't specify a timeout in playframework 2, they may persist indefinitely by default. It really depends on the configuation/module being used with the play api.
I wouldn't recommend this solution for the situation you've described. The database is a reasonable place to store this information, you should just retrieve the user data on each request.
Just wanted to say a cache for objects is possible.
回答2:
Note: as your question is in general about 'passing objects between actions' Mike's answer fits best your needs, conclusion is: use cache and/or well prepared SQL statement (means getiing as small data as possible at the moment without additional relations, etc)
I think that's best candidate for 'accept answer'.
On the other hand, as from discussion it's becoming clear that you are asking in context of auth, thera are my 2cc:
- Mauno's answer describes typical auth approach. Yes it requires additional query, however if you will use for an example H2 database in embeded mode it should not be a big problem - it's fast. Additionally you can exactly optimize that a little with cache API.
- Actually there is some security gap in this approach... Although session cookies are signed, so they theoretically can't be manipulated... that doesn't change the fact, they can be just hijacked and reused by other person. Simplest solution is saving session on the server-side - in DB or in cache, so you can compare more details like IP, fingerprint etc. What's most important you can also invalidate whole server-side session on logout or after some time of inactivity, so even if attacker will grab the session, he will not be able to reuse it anymore. Of course this approach creates additional DB queries anyway you need to choose yourself what is more important to you, security or performance :)
- There is ready to use authorization/authentication stack: Play-Authenticate, warned by hijack possibility I've created a sample on top of it, to demonstrate how to add a session handling using cache and/or DB: https://github.com/biesior/play-authenticate/commits/2.0.4_sessions/samples/java/play-authenticate-usage - check my latest commits for comments and code diffs.
回答3:
You should not do this (or even need to do this), that was actually something I was also thinking when I started using play - then I realized that I didnt use framework how it was meant to be used.
Action composition is actually something which is used to protect certain methods or controllers being accessed. (Although you can use it for other things too). - But it is needed when you are implementing login.
Instead of passing your User
object around .. your user email
(username) are placed into session and it cant be later retrieved inside your controller side and templates. (Any of the controllers or templates.)
public static Result authenticate() {
Form<Login> loginForm = form(Login.class).bindFromRequest();
if (loginForm.hasErrors()) {
return badRequest(login.render(loginForm));
} else {
session().clear();
session("email", loginForm.get().email);
return redirect(
routes.Application.index()
);
}
}
I highly recommend checking out full zentask tutorial, if you are just more eager to know about login, you can check zentask page 4 (working zentask example is also located inside your play/samples/java/zentasks
so its really easy to pick up with).
Relevant parts includes setting up own login form, view, controller with authenticate
method and Secured
class to handle actual method & or controller action protecting. Dont forgot to persist your users to database - otherwise there are not much to check against ;)
Edit:
If you open up your zentask tutorial you can see an example when controller needs authentication and when username is taken from the session, see the annotation: @Security.Authenticated(Secured.class)
and method of request().username()
which returns username as a String.. see: Http.Request.html#username() documentation
@Security.Authenticated(Secured.class)
public class Projects extends Controller {
/**
* Display the dashboard.
*/
public static Result index() {
return ok(
dashboard.render(
Project.findInvolving(request().username()),
Task.findTodoInvolving(request().username()),
User.find.byId(request().username())
)
);
}
...
Method itself for example User.find.byId(request().username())
loads complete User
from database for the model to be rendered. :)
But read the tutorial I provided, its complete example. Cheers.
来源:https://stackoverflow.com/questions/15441061/play-framework-2-passing-java-objects-between-controllers