1

我有一个 java User 对象,我想将它从我的登录控制器传递到我的仪表板控制器。我读了一堆,我正在尝试以下实现

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());
    }

和我的仪表板控制器

public static Result index(){
    AppUser user = (AppUser) Context.current().args.get("user");
    return ok(views.html.dashboard.index.render(user));
}

这导致空指针异常

这是因为这两个请求当然不是同一个请求。

如何以一种友好的方式解决这个问题顺便说一句,我在文档中看到了一些关于动作组合的东西,但我不明白。

4

3 回答 3

3

尽管我同意 Mauno 的回答,但我想补充一点,可以使用缓存 API来执行您的要求。

要记住的一件事是缓存在多个用户之间是相同的。如果您希望能够从缓存中访问特定的用户对象,那么您将必须拥有一个唯一的 ID。您还将在请求之间存储该 ID。玩游戏是一种简单的方法。

这是一个例子

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));
}

此外,这些对象不会有可预测的寿命,因此您必须确保检查它们的存在并可能重新创建对象。

编辑:我不确定最后一条语句,在 playframework 1 中,如果缓存中的对象有一段时间不使用,它们将被删除,但是从这篇文章中可以看出,如果你没有在 playframework 2 中指定超时,默认情况下,它们可能会无限期地持续存在。这实际上取决于与 play api 一起使用的配置/模块。

对于您描述的情况,我不会推荐此解决方案。数据库是存储此信息的合理位置,您应该只检索每个请求的用户数据。

只想说对象的缓存是可能的。

于 2013-03-15T21:35:16.063 回答
2

注意:由于您的问题通常是关于“在动作之间传递对象”,因此迈克的回答最适合您的需求,结论是:使用缓存和/或准备充分的 SQL 语句(意味着在没有其他关系的情况下目前尽可能少地获取数据等)

我认为这是“接受答案”的最佳人选。

另一方面,从讨论中可以清楚地看出,您是在 auth 的上下文中询问,这是我的 2cc:

  • Mauno 的回答描述了典型的身份验证方法。是的,它需要额外的查询,但是如果您将在嵌入模式下使用示例 H2 数据库,它应该不是一个大问题 - 它很快。此外,您可以使用缓存 API 对其进行一些优化。
  • 实际上这种方法存在一些安全漏洞......虽然会话cookie是签名的,所以它们理论上不能被操纵......这并不能改变事实,它们可以被其他人劫持和重用。最简单的解决方案是在服务器端保存会话 - 在数据库或缓存中,因此您可以比较更多详细信息,如 IP、指纹等。最重要的是,您还可以在注销或一段时间不活动后使整个服务器端会话无效,所以即使攻击者会抢到会话,他也不能再重用它了。当然,这种方法会创建额外的数据库查询,您需要自己选择对您来说更重要的内容,安全性或性能:)
  • 已准备好使用授权/身份验证堆栈: Play-Authenticate,被劫持的可能性警告我已经在其上创建了一个示例,以演示如何使用缓存和/或 DB 添加会话处理:https://github。 com/biesior/play-authenticate/commits/2.0.4_sessions/samples/java/play-authenticate-usage - 检查我最新提交的评论和代码差异。
于 2013-03-16T08:13:03.910 回答
1

你不应该这样做(甚至不需要这样做),这实际上是我在开始使用 play 时也在考虑的事情 - 然后我意识到我没有使用框架来使用它。

动作组合实际上是用于保护某些被访问的方法或控制器的东西。(尽管您也可以将它用于其他事情)。- 但是在您实现登录时需要它。

而不是传递你的User对象..你的用户email(用户名)被放入会话中,以后不能在你的控制器端和模板中检索它。(任何控制器或模板。)

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()
        );
    }
}

我强烈建议您查看完整的 zentask 教程,如果您只是更想了解登录,可以查看zentask 第 4 页(工作 zentask 示例也位于您play/samples/java/zentasks的内部,因此非常容易上手)。

相关部分包括设置自己的登录表单、视图、带有authenticate方法和Secured类的控制器来处理实际的方法和或控制器动作保护。不要忘记将您的用户持久保存到数据库 - 否则没有太多要检查的内容;)

编辑:

如果您打开 zentask 教程,您可以看到控制器需要身份验证以及从会话中获取用户名时的示例,请参阅注解:@Security.Authenticated(Secured.class)以及request().username()将用户名作为字符串返回的方法。请参阅:Http.Request.html#username( ) 文件

@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())
            )
        );
    }

...

方法本身例如从数据库User.find.byId(request().username())加载完整User的模型以呈现模型。:)

但是请阅读我提供的教程,它的完整示例。干杯。

于 2013-03-15T20:11:57.270 回答