问题
I'm making a simple authentication system using Playframework with their built-in CSRF filter and Security.Authenticator system, but I'm facing a problem :
When the user fill his login/password and submit enter, I have the following error :
CSRF token not found in session
I checked my form and the CSRF token is really present and correctly placed (inside the tag)
Here is my routes
:
GET /login controllers.Authentication.login
POST /login controllers.Authentication.authenticate
And my Authentication.java
class :
@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());
}
}
The properties I setted in the application.conf :
csrf.token.name="csrf"
csrf.sign.tokens=true
Here's my form (to show that the token is correctly positionned) :
<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>
Update 1: I removed all the @addCSRFToken
and @requireCSRFToken
and defined a global CSRF token instead, per @rhj recommandation. Now I got this error instead :
Invalid token found in form body
But as you can see in the HTML Form, the token is placed inside the form and should be identified!
Update 2: What bothers me the most, is that this problem only occurs in the Login page, and not elsewhere! Is there some session to enable first? I guess not but maybe ?!
Update 3: More odd, I just found out that if I change the value of csrf.token.name
and reload the page, it works. Then if I log out, open a new page and try to logs again, it fails with the message Invalid token found in form body
. If I again change the value of csrf.token.name
and do it again, it will works again.
Update 4: I narrowed down the problem. I also use the play.mvc.Security.Authenticator and it seems that the token verification fails ONLY when I display the page after a redirect()
. If I go straight to the login page, I won't have the error message displayed.
Final update! : I finally found the problem, it was in another class that I wasn't suspecting, that was called and cleared the session, making the token useless!
回答1:
Finally the problem was located in an other part of my code that was cleaning the session in the login()
method, removing the csrf token with it ...
回答2:
Add the below line in Global.java
@Override
public <T extends EssentialFilter> Class<T>[] filters() {
return new Class[]{CSRFFilter.class};
}
For this you have to import
import play.api.mvc.EssentialFilter;
import play.filters.csrf.CSRFFilter;
Hope this will help you
来源:https://stackoverflow.com/questions/26136744/playframework-with-csrf-csrf-token-not-found-in-session