Playframework con CSRF: "token CSRF no encontrado en la sesión"?
playframework-2.3 (2)
Estoy haciendo un sistema de autenticación simple usando Playframework con su filtro CSRF incorporado y sistema Security.Authenticator, pero estoy enfrentando un problema:
Cuando el usuario completa su nombre de usuario / contraseña y envía enter, aparece el siguiente error:
Token de CSRF no encontrado en la sesión
Revisé mi formulario y el token CSRF está realmente presente y colocado correctamente (dentro de la etiqueta)
Aquí están mis routes
:
GET /login controllers.Authentication.login
POST /login controllers.Authentication.authenticate
Y mi clase 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());
}
}
Las propiedades que configuré en la aplicación.conf:
csrf.token.name="csrf"
csrf.sign.tokens=true
Aquí está mi formulario (para mostrar que el token está correctamente ubicado):
<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>
Actualización 1 : @addCSRFToken
todos los @addCSRFToken
y @addCSRFToken
y @requireCSRFToken
un token CSRF global en su lugar, por recomendación de @rhj. Ahora tengo este error en su lugar:
Token inválido encontrado en el cuerpo del formulario
Pero como puede ver en el Formulario HTML, el token se coloca dentro del formulario y debe ser identificado.
Actualización 2 : Lo que más me molesta es que este problema solo ocurre en la página de inicio de sesión, ¡y no en otra parte! ¿Hay alguna sesión para habilitar primero? Supongo que no, pero ¿tal vez?
Actualización 3 : Más extraño, acabo de descubrir que si cambio el valor de csrf.token.name
y csrf.token.name
cargar la página, funciona. Luego, si cierro la sesión, abro una nueva página y trato de volver a iniciar sesión, falla con el mensaje Invalid token found in form body
. Si vuelvo a cambiar el valor de csrf.token.name
y lo csrf.token.name
hacer, volverá a funcionar.
Actualización 4 : reduje el problema. También uso el play.mvc.Security.Authenticator y parece que la verificación del token falla SÓLO cuando visualizo la página después de un redirect()
. Si voy directamente a la página de inicio de sesión, no tendré el mensaje de error en la pantalla.
¡Actualización final! : Finalmente encontré el problema, era en otra clase de la que no sospechaba, ¡eso fue llamado y borró la sesión, haciendo que el token fuera inútil!
Gracias por tu ayuda.
Agregue la siguiente línea en Global.java
@Override
public <T extends EssentialFilter> Class<T>[] filters() {
return new Class[]{CSRFFilter.class};
}
Para esto tienes que importar
import play.api.mvc.EssentialFilter;
import play.filters.csrf.CSRFFilter;
Espero que esto te ayudará
Finalmente, el problema se encontraba en otra parte de mi código que estaba limpiando la sesión en el método de login()
, eliminando el token csrf con él ...