使用CSRF播放框架:"会话#34中找不到CSRF令牌;?

时间:2014-10-01 07:59:52

标签: playframework csrf playframework-2.3

我使用Playframework制作一个简单的身份验证系统,内置CSRF过滤器和Security.Authenticator系统,但我遇到了一个问题:

当用户填写他的登录名/密码并提交回车时,我有以下错误:

  

在会话中找不到CSRF令牌

我检查了我的表单,CSRF令牌确实存在并正确放置(在标签内)

这是我的routes

GET     /login                      controllers.Authentication.login
POST    /login                      controllers.Authentication.authenticate

我的Authentication.java课程:

@play.filters.csrf.AddCSRFToken
public static Result login() {
    if (Session.getAccount() != null) {
        return redirect(controllers.routes.Instances.list());
    }

    LoginForm loginForm = new LoginForm();
    if (ctx().session().containsKey("email")) {
        loginForm.setEmail(ctx().session().get("email"));
    }

    if (request().queryString().containsKey("disconnected")) {
        flash("warning", Messages.get("secure.logout")); // TODO : Warning flash message
    }

    Form<LoginForm> form = Form.form(LoginForm.class).fill(loginForm);
    return ok(views.html.login.render(form));
}

@play.filters.csrf.AddCSRFToken
@play.filters.csrf.RequireCSRFCheck
public static Result authenticate() {
    Form<LoginForm> form = Form.form(LoginForm.class).bindFromRequest();
    if (form.hasErrors()) {
        return badRequest(views.html.login.render(form));
    }

    LoginForm login = form.get();
    Account account = Account.findByEmail(login.getEmail());

    if (account == null || !account.checkPassword(login.getPassword())) {
        form.reject("email", "secure.invalid.login");
        return badRequest(views.html.login.render(form));
    }

    String next = null;
    if (ctx().session().containsKey("next")) {
        next = ctx().session().get("next");
        if ("".equals(next)) {
            next = null;
        }
    }

    session().clear();
    session("email", login.getEmail());

    if (next != null) {
        return redirect(next);
    } else {
        return redirect(controllers.routes.Instances.list());
    }
}

我在application.conf中设置的属性:

csrf.token.name="csrf"
csrf.sign.tokens=true

这是我的表单(表明令牌已正确定位):

<form action="@controllers.routes.Authentication.authenticate" method="post" class="form-vertical" role="form">
    <fieldset>
        @defining(form("email")) { element =>
        <div class="form-group fade-in two@if(element.hasErrors){ has-error}">
            <label class="control-label" for="login-form-@element.name">Email:</label>
            <input type="email" name="@element.name" id="login-form-@element.name" class="form-control" value="@element.value" placeholder="Enter your email" required />
            @element.errors.map { error => <span class="help-block">@play.i18n.Messages.get(error.message)</span>}
        </div>}
        @defining(form("password")) { element =>
        <div class="form-group fade-in three@if(element.hasErrors){ has-error}">
            <label class="control-label" for="login-form-@element.name">Password:</label>
            <input type="password" name="@element.name" id="login-form-@element.name" class="form-control" value="@element.value" placeholder="Enter your password" required />
            @element.errors.map { error => <span class="help-block">@play.i18n.Messages.get(error.message)</span>}
        </div>}
        <div class="form-group actions fade-in four">
            <div class="text-right">
                <input type="submit" name="save" value="Sign me in" class="btn btn-primary" />
                @helper.CSRF.formField
            </div>
        </div>
    </fieldset>
</form>

更新1 :我删除了所有@addCSRFToken@requireCSRFToken,并根据@rhj推荐定义了全局CSRF令牌。现在我得到了这个错误:

  

在表单正文中找到无效的令牌

但是正如您在HTML表单中看到的那样,令牌被放置在表单中并且应该被识别!

更新2 :最让我烦恼的是,这个问题只发生在登录页面,而不是其他地方!是否有一些会话首先启用?我猜不是,但也许?!

更新3 :更奇怪的是,我发现如果我更改csrf.token.name的值并重新加载页面,它就可以了。然后,如果我退出,打开一个新页面并尝试再次登录,它将失败并显示消息Invalid token found in form body。如果我再次更改csrf.token.name的值并再次执行此操作,它将再次有效。

更新4 :我缩小了问题范围。我还使用了play.mvc.Security.Authenticator,只有当我在redirect()之后显示页面时,令牌验证才会失败。如果我直接进入登录页面,我就不会显示错误信息。

最终更新!:我终于找到了问题,这是在另一个我不怀疑的课程中,被调用并清除会话,使令牌无用!

2 个答案:

答案 0 :(得分:4)

最后问题出现在我的代码的另一部分中,即在login()方法中清除会话,用它删除csrf标记......

答案 1 :(得分:3)

Global.java

中添加以下行
@Override
public <T extends EssentialFilter> Class<T>[] filters() {
    return new Class[]{CSRFFilter.class};
}

为此你必须导入

import play.api.mvc.EssentialFilter;
import play.filters.csrf.CSRFFilter;

希望这会对你有所帮助