作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!

用户验证(User Authentification)复合的使用Play框架的数个功能,包括前面已经了解的表单和数据库,以及这篇文章里要提到的加密和会话。根据应用或站点的复杂程度,用户验证也可以随之变化。这里将介绍用户验证的一个基本实现方式。

加密

为了信息安全,用户密码需要加密,而不是保存为明文。Bcrypt算法可以对明文密码进行哈希(Hash)转换。我保存在数据库中的密码,是经过转换后的文本。

JBcrypt是一个外部的包,提供了Bcrypt功能。要在build.sbt中说明这个包的来源和版本:

name := "test"

version := "1.0-SNAPSHOT"

libraryDependencies ++= Seq(
javaJdbc,
javaEbean,
cache,
"mysql" % "mysql-connector-java" % "5.1.18",
"org.mindrot" % "jbcrypt" % "0.3m"
) play.Project.playJavaSettings

即上面新增的jbcrypt行。重新运行Play后即可使用。为了Eclipse能自动补齐该包的相关调用,可以使用play eclipse,并重新在Eclipse引入项目。

我下面用一个小例子,来说明该Bcrypt的哈希转换。在Play中增加动作:

public static Result bcrypt() {
String passwordHash = BCrypt.hashpw("Hello",BCrypt.gensalt());
boolean correct = BCrypt.checkpw("Hello", passwordHash);
boolean wrong = BCrypt.checkpw("World", passwordHash);
return ok(passwordHash + " " + correct + " " + wrong);
}

上面程序需引入org.mindrot.jbcrypt.BCrypt。动作中对"Hello"字符串进行了哈希转换,并验证"Hello"和"World"是否为原始的明文文本。

在routes增加对应URL,/bcrypt

GET     /bcrypt                     controllers.Application.bcrypt()

访问页面:

用户注册

有了表单数据库和加密的基础,用户注册很容易实现。首先建立数据模型app/models/User.java:

package models;

import javax.persistence.*;
import play.db.ebean.Model;
import org.mindrot.jbcrypt.BCrypt; @Entity
public class User extends Model {
@Id
private String email;
private String password; // Constructor
public User(String email, String password) {
String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());
this.email = email;
this.password = passwordHash;
}
}

这段代码创建了User类,包含两个属性email和password。在构造器中,我对密码进行了哈希转换。

下面修改控制器Application(app/controllers/Application.java)。控制器中包含两个动作和一个表单类Registration。一个动作register()用于显示注册页面,另一个动作postRegister处理表单提交的信息,并增加相应的数据库记录。Registration则对应注册页面所显示的表格:

package controllers;

import play.*;
import play.mvc.*;
import play.data.Form;
import play.data.validation.Constraints.*;import models.User; public class Application extends Controller {
public static class Registration {
@Email
public String email;
@Required
public String password;
} public static Result register() {
Form<Registration> userForm = Form.form(Registration.class);
return ok(views.html.register.render(userForm));
} public static Result postRegister() {
Form<Registration> userForm =
Form.form(Registration.class).bindFromRequest();
User user = new User(userForm.get().email, userForm.get().password);
user.save();
return ok("registered");
}
}

register()动作使用的模板为app/views/register.scala.html:

@(userForm: Form[controllers.Application.Registration])

<!DOCTYPE html>
<html>
<body>
<h1> Registration </h1>
@helper.form(action = routes.Application.postRegister()) {
@helper.inputText(userForm("email"))
@helper.inputPassword(userForm("password"))
<input type="submit">
}
</body>
</html>

在routes中为两个动作增加对应的URL:

GET     /register                   controllers.Application.register()
POST /register controllers.Application.postRegister()

访问页面:

输入用户名和密码,可以看到数据库中增加的记录:

用户验证

将用户验证的主要逻辑放入到模型User中。修改User类,为User类增加authenticate()方法:

package models;

import javax.persistence.*;
import play.db.ebean.Model;
import org.mindrot.jbcrypt.BCrypt; @Entity
public class User extends Model {
@Id
private String email;
private String password; // Constructor
public User(String email, String password) {
String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());
this.email = email;
this.password = passwordHash;
} // Query
public static Model.Finder<Integer, User> find =
new Model.Finder<>(Integer.class, User.class); // Authentification
public static User authenticate(String email, String password) {
User user = find.where()
.eq("email", email)
.findUnique();
if (user == null) {
return user;
} else if (BCrypt.checkpw(password, user.password)) {
return user;
} else {
return null;
}
}
}

authenticate()接收的是明文密码。上面的验证中,首先检查用户邮箱是否存在。如果存在,则检查密码是否符合数据库的记录。如果邮箱或者密码错误,将返回null。否则返回正确的用户对象。

我进一步修改控制器Application。这一次还是增加两个动作和一个表单类。动作login()用于显示登录页面,动作postLogin()用于处理登录表单填写的信息,并根据信息决定是否登入用户。Login类对应登录页面的表单。

package controllers;

import play.*;
import play.mvc.*; import play.data.Form;
import play.data.validation.Constraints.*; import models.User; public class Application extends Controller { public static class Registration {
@Email
public String email;
@Required
public String password;
} public static Result register() {
Form<Registration> userForm = Form.form(Registration.class);
return ok(views.html.register.render(userForm));
} public static Result postRegister() {
Form<Registration> userForm =
Form.form(Registration.class).bindFromRequest();
User user = new User(userForm.get().email, userForm.get().password);
user.save();
return ok("registered");
} public static class Login {
@Email
public String email;
@Required
public String password; public String validate() {
if (User.authenticate(email, password) == null) {
return "Invalid user or password";
}
return null;
}
} public static Result login() {
Form<Login> userForm = Form.form(Login.class);
return ok(views.html.login.render(userForm));
} public static Result postLogin() {
Form<Login> userForm = Form.form(Login.class).bindFromRequest();
if (userForm.hasErrors()) {
return badRequest("Wrong user/password");
} else {
return ok("Valid user");
}
}
}

上面的表单类Login中,增加了validate()方法,并在其中调用User的验证逻辑。正如postLogin()中所示,表单的hasErrors()方法将自动检查validate()方法的返回值。如果validate()方法返回为null,则说明表单无误。postLogin()的if结构,将根据登录是否合法,来返回不同的结果。

为新增的动作增加对应的URL:

GET     /login                      controllers.Application.login()
POST /login controllers.Application.postLogin()

访问/login页面,并尝试登录。

会话

HTTP协议是无状态的。即使我在/login登录成功,但下一次访问时,服务器又会忘记我是谁。HTTP协议可以用会话(Session)的方式,来记录用户的登录信息。在会话有效期内,服务器可以识别相应客户的访问。Play实现会话相当方便。

提交登录表格时,如果登录合法,我将让服务器开启和该客户的会话,记录客户的信息。因此,修改postLogin()为:

    public static Result postLogin() {
Form<Login> userForm = Form.form(Login.class).bindFromRequest();
if (userForm.hasErrors()) {
return badRequest(views.html.login.render(userForm));
} else {
session().clear();
session("email",userForm.get().email);
return redirect("/");
}
}

这里用户登录成功后,将启动一个会话。在会话中,可放入键值对(key-value pair)形式的信息。这里的键名为"email",对应值为登录用户的邮箱地址。登录成功后将重新定向到/。

增加index()动作,对应/这一URL。在该动作中,我调用session中保存的用户信息:

    public static Result index() {
String email = session("email");
if (email != null) {
return ok(email);
} else {
return ok("not login");
}
}

增加routes中对应的URL:

GET     /                           controllers.Application.index()

访问/login,并登录。成功登录后重新定向到/,页面为:

可以看到,会话中的信息可以持续到以后的页面访问。为了销毁会话,可以在某个动作中调用:

session().clear();

总结

用户验证

会话

欢迎继续阅读“Java快速教程”系列文章

来玩Play框架06 用户验证的更多相关文章

  1. Play框架的用户验证。

    最近刚刚参与一个基于Play框架的管理平台的升级工作,其中涉及到了用户的验证工作.第一次接触play框架,直接看已有代码,有点晕.因此,自己实现了一个简单的用户验证功能. 首先,新建一个User类,包 ...

  2. Django用户验证框架

    一  分析源码 User   Django的标准库存放在 django.contrib 包中.每个子包都是一个独立的附加功能包. 这些子包一般是互相独立的,不过有些django.contrib子包需要 ...

  3. php-laravel框架用户验证(Auth)模块解析(一)

    一.初始化 使用php artisan命令进行初始化:php artisan make:auth 和 php artisan migrate(该命令会生成users表.password_resets表 ...

  4. spring4.0整合mongodb3.0.4项目实践(用户验证)

    我们的项目用到了spring框架和mongdb数据库,随着mongodb升级到3.0已有半年时间,我们也开始随之升级,但是3.0的用户验证有所更改,导致原来的很多配置无法再用. 经过几天的尝试后,终于 ...

  5. php用户验证代码的简单例子

    发布:sunday01   来源:net     [大 中 小] 分享一个简单的php用户验证代码,适合初学的朋友参考,主要学习$_post传递数据及isset检测变量的方法. php简单用户验证代码 ...

  6. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(五)——实现注册功能

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

  7. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

  8. 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(三)——使用Flask-Login库实现登录功能

    使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(一)——创建应用 使用 Flask 框架写用户登录功能的Demo时碰到的各种坑(二)——使用蓝图功能进行模块化 使用 Flask 框架写用 ...

  9. Laravel 5.3 用户验证源码探究 (一) 路由与注册

    https://blog.csdn.net/realghost/article/details/52558962 简介 Laravel 从 5.2 开始就有了开箱即用的用户验证,5.3 又在 5.2 ...

随机推荐

  1. Kooboo CMS技术文档之三:切换数据存储方式

    切换数据存储方式包括以下几种: 将文本内容存储在SqlServer.MySQL.MongoDB等数据库中 将站点配置信息存储在数据库中 将后台用户信息存储在数据库中 将会员信息存储在数据库中 将图片. ...

  2. ,net core mvc 文件上传

    工作用到文件上传的功能,在这个分享下 ~~ Controller: public class PictureController : Controller { private IHostingEnvi ...

  3. chattr用法

    [root@localhost tmp]# umask 0022 一.chattr用法 1.创建空文件attrtest,然后删除,提示无法删除,因为有隐藏文件 [root@localhost tmp] ...

  4. Fedora 22中的RPM软件包管理工具

    Introduction The RPM Package Manager (RPM) is an open packaging system that runs on Fedora as well a ...

  5. 借助Nodejs探究WebSocket

    文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...

  6. Top Coder算法题目浏览器

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/top-code-offline-browser/ 关于 左耳朵耗子 ...

  7. redhat7 修改hostname

    修改linux redhat的 hostname 其实有好一些陷阱.. 通常 我们修改 /etc/sysconfig/network 的 HOSTNAME 变量即可 但是它不会立即生效, 需要执行下面 ...

  8. 关于CommonJS规范摘录

    CommonJS规范 1. 概述 为什么要用commonjs 模块化的目的: 减少循环依赖 减少耦合,提高了模块的复用率 有利于多人开发,提高开发的效率. 规避命名的冲突.全局变量的污染.有利于代码的 ...

  9. 3. SVM分类器求解(1)——Lagrange duality

    先抛开上面的二次规划问题,先来看看存在等式约束的极值问题求法,比如下面的最优化问题: 目标函数是f(w),下面是等式约束.通常解法是引入拉格朗日算子,这里使用来表示算子,得到拉格朗日公式为 是等式约束 ...

  10. Matlab 利用参数方程绘制空心球体

    基本原理: 实质为利用球面参数方程,利用网格化数据绘制 x=R*sin(theta)*cos(phi) y=R*sin(theta)*sin(phi) z=R*cos(theta) 绘制函数: fun ...