Playframework com CSRF: “token CSRF não encontrado na sessão”?
Estou criando um sistema de autenticação simples usando o Playframework com o filtro CSRF incorporado e o sistema Security.Authenticator, mas estou enfrentando um problema:
Quando o usuário preenche seu login / senha e envia enter, tenho o seguinte erro:
Token CSRF não encontrado na sessão
Verifiquei meu formulário e o token CSRF está realmente presente e corretamente colocado (dentro da tag)
Aqui está o meuroutes
:
GET /login controllers.Authentication.login
POST /login controllers.Authentication.authenticate
E meuAuthentication.java
classe :
@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());
}
}
As propriedades que defini no application.conf:
csrf.token.name="csrf"
csrf.sign.tokens=true
Aqui está o meu formulário (para mostrar que o token está corretamente posicionado):
<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="[email protected]">Email:</label>
<input type="email" name="@element.name" id="[email protected]" 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="[email protected]">Password:</label>
<input type="password" name="@element.name" id="[email protected]" 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>
Atualização 1: Eu removi todo o@addCSRFToken
e@requireCSRFToken
e definiu um token CSRF global, por recomendação do @rhj. Agora eu recebi esse erro:
Token inválido encontrado no corpo do formulário
Mas como você pode ver no formulário HTML, o token é colocado dentro do formulário e deve ser identificado!
Atualização 2: O que mais me incomoda é que esse problema ocorre apenas na página de login e não em outros lugares! Existe alguma sessão para ativar primeiro? Acho que não, mas talvez ?!
Atualização 3: Mais estranho, acabei de descobrir que se eu alterar o valor decsrf.token.name
e recarregar a página, funciona. Então, se eu sair, abrir uma nova página e tentar registrar novamente, ela falhará com a mensagemInvalid token found in form body
. Se eu mudar novamente o valor decsrf.token.name
e faça novamente, funcionará novamente.
Atualização 4: Reduzi o problema. Também uso o play.mvc.Security.Authenticator e parece que a verificação do token falha apenas quando eu exibir a página após umredirect()
. Se eu for direto para a página de login, a mensagem de erro não será exibida.
Atualização final! : Finalmente encontrei o problema, foi em outra classe que eu não suspeitava, que foi chamado e liberado a sessão, tornando o token inútil!