来玩Play框架06 用户验证

作者: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快速教程”系列文章

时间: 2024-11-01 03:07:47

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

利用Python的装饰器解决Bottle框架中用户验证问题

  这篇文章主要介绍了Python的Bottle框架中解决用户验证问题,代码基于Python2.x版本,需要的朋友可以参考下 首先来分析下需求,web程序后台需要认证,后台页面包含多个页面,最普通的方法就是为每个url添加认证,但是这样就需要每个每个绑定url的后台函数都需要添加类似或者相同的代码,但是这样做代码就过度冗余,而且不利于扩展. 接下来我们先不谈及装饰器,我们都知道Python是个很强大的语言,她可以将函数当做参数传递给函数,最简单的: ? 1 2 3 4 5 6 7 8 9 10

Play框架(六) 用户验证

用户验证(User Authentification)复合的使用Play框架的数个功能,包括前面已经了解的表单和数据库,以及这篇文章里要提到的加密和会话.根据应用或站点的复杂程度,用户验证也可以随之变化.这里将介绍用户验证的一个基本实现方式. 加密 为了信息安全,用户密码需要加密,而不是保存为明文.Bcrypt算法可以对明文密码进行哈希(Hash)转换.我保存在数据库中的密码,是经过转换后的文本. JBcrypt是一个外部的包,提供了Bcrypt功能.要在build.sbt中说明这个包的来源和版

ZenTaoPHP框架的数据验证机制

项目中数据验证在web应用中尤其显得重要.下面来给大家介绍下ZenTaoPHP框架的数据验证机制. 一.首先来确定一个问题,即验证的规则放在什么地方? mvc程序中,每一层都可以放验证规则.比如很多的表单验证,会自动根据用户的输入进行验证,然后给予提示.那么数据验证放在哪一层呢?这个问题网络上大家有很多的争议.有的人主要放在view这一层,有的则主张放在control层.禅道框架选择了model层. 为什么这样做呢?因为model层是最低的一层,所有的数据操作,都要经过model来进行处理.那么

基于mvc5+ef6+Bootstrap框架实现身份验证和权限管理_C#教程

近和朋友完成了一个大单子架构是mvc5+ef6+Bootstrap,用的是vs2015,数据库是sql server2014.朋友做的架构,项目完成后觉得很多值得我学习,在这里总结下一些心得. 创建项目一开始删掉App_Start目录下的IdentityConfig.cs和Startup.Auth.cs文件;清空Modle文件夹,Controller文件夹和相应的View; 删除目录下的ApplicationInsights.config文件和Startup.cs文件 修改web.config文

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

我们的项目用到了spring框架和mongdb数据库,随着mongodb升级到3.0已有半年时间,我们也开始随之升级,但是3.0的用户验证有所更改,导致原来的很多配置无法再用. 经过几天的尝试后,终于成功的用spring配置验证. 升级用了两个新的jar包,分别是pring-data-mongodb1.7.2(http://pan.baidu.com/s/1bnkAA67)和mongodb-java-driver3.0.2(http://pan.baidu.com/s/1jG6bc3c): sp

关于Apache服务器如何实现用户验证

Apache服务器已经内置用户验证机制,大家只要适当的加以设置,便可以控制网站的某些部分要用户验证.大家只要跟着我一步步做下来就应该能轻松实现用户验证. 前期准备,必须已经安装apache,如果还没安装,或者对安装很模糊的话,请查询相应的资料. 第1步 我们在/var/www(apache的主页根目录)下建立一个test目录 mkdir /var/www/test 第2步 然后我们编辑httpd.conf 添加 Alias /test"/var/www/test"<Directo

JavaScript框架提升用户体验(UE)

提升用户体验(UE) 到目前为止,这篇文章完全集中于使用JavaScript框架的好处,以及如何更容易的构建交互式应用程序.但是,另一方面,各个框架都有各自诱人的前景,包括用户界面(UI)组件和用户体验(UE)增强,这需要花大量的精力. 这章将分别探讨下面框架的用户体验:Prototype. jQuery.YUI.ExtJS和MooTools. Prototype Prototype是少数几个不包括现成的UI组件或用户体验提升.相反,它顺从所有姊妹库script.aculo.us (Script

我来写个详细的: Win32下具体实现Apache的用户验证(原创)

apache|原创 Win32下具体实现Apache的用户验证相信大家采用的验证方法很多种,而在Win32环境下如何设置Apache用户验证的介绍很少,手册上介绍的也是Unix和linux环境下的配置,象我这样在win32下苦苦摸索Apache的人应该不少,我根据自己的经验介绍一下自己是如何在Win32环境下实现apache的用户验证功能的. 方法一:以单用户方式实现某目录只能由某个用户或几个用户访问.假设Apache的web根目录为 d:/home,对应URL为 http://localhos

Asp.Net自定义IPrincipal和IIdentity实现用户验证

引言 前一段时间有两个朋友问我,为什么在HttpModule中无法获得到Session值,因为他们希望自定义一个 HttpModule,然后在其中获取Session来进行用户验证.我奇怪为什么不使用.Net Framework已经提供的 验证机制,而要和Asp时一样,自己手工进行cookie+Session验证?我们是基于.Net Framework这个平台 进行编程,所以我觉得,在很多情况下,使用Framework已经建立好的机制会显著地提高工作效率,而 且.NET Framework内置的验