Laravel框架初识:扩展Auth功能

Laravel框架初识:扩展Auth功能

最近在尝试使用Laravel框架进行一些实际开发,框架本身提供了很多常用的Web应用的机制和库,虽然很好用,但是却不能包容我预期的设计,比如Auth模块,该模块提供了两种很基本的验证驱动,一个是Database,使用QueryBuilder来与数据库进行交互,另一个是Eloquent ORM,是采用其内置的ORM库来进行数据库交互(感觉类似于.Net中的EF),但是不论采取哪种驱动,都存在同样的局限性:

必须采用内置的Hash方案对入库的密码进行保护

这一局限使得原本系统设计中的MD5 + Salt方案受到了阻挠,尽管实现会稍显麻烦,但我仍然认为这种方案更适合于我的项目,因此我参考了Laravel文档中扩展章节所述的Auth模块扩展以及网上查阅的一些资料,来对原框架进行修改,使其能够支持有我自己定义的验证驱动。

首先介绍我所考虑使用的MD5 + Salt方案的具体设计:

数据库中不应该存储密码明码应该是一个基本安全常识,通常的方案是采用MD5算法对密码进行Hash,再存入数据库,从而避免数据库泄露之后,用户的敏感数据也随之泄露,但是随着时间发展,人们存储的MD5的字典已经越来越庞大,对于一些弱口令(例如12345678、passw0d,不足够长的纯数字或字母串),即便是在不可逆算法MD5 Hash过后再入库,对于拥有字典的人而言,仍然与明文无异,对于有经验的人而言,可能看到25d55ad283aa400af464c76d713c07ad,脑子里就已经浮现了12345678这串数字了。于是对密码加Salt的想法就孕育而生,如果说用户自己设定的密码很简单,那么不妨让系统来帮他进行加强,而Salt就是在原来的密码上加的那一点“佐料”,只要能够保证无论曾经的密码多么简单,只要加完Salt之后,字典中就无法直接找到这个Hash对应的原文,那么这个方案就算是成功了。

首先,用户数据库我们需要两个字段:

psw_hash:用来存储Hash过后的密码 psw_salt:用来存储帮助Hash的的附加串

在加入这两个字段后,我们来考虑两个情景:

第一个情景就是注册(或是修改密码)的时候, 第二个情景是验证的时候,这两个情景的流程如下图所示:

1左侧是情景一的图示,右侧是情景二的图示,由于在数据库中不存储密码的明文,同时采用了每个用户一种单独Salt的方案,在数据库被攻破后,窃密者必须付出极大的成本才可能破解出用户的密码明文,并且方法只能是为每种Salt都重新建立一份字典,在这样的成本下,我们可以认为这种方案是非常安全的。

那么接下来的问题就是,如何在Laravel框架中扩展出这一机制?根据文档,我们可以先找到这个目录:

/vender/laravel/framework/src/illuminate/Auth

这个目录就是Laravel框架所提供的Auth库地址,其中我们首先关注:

UserProviderInterface.php

DatabaseUserProvider.php

EloquentUserProvider.php

第一个文件是一个接口,后面两个文件中的类实现了第一个文件中的接口,而后面两个类就是前文所提到的,默认Auth库所支持的两种验证驱动,我们从他们都实现的UserProviderInterface入手,下面的代码是从文档中摘录的精简过的接口定义代码:

UserProviderInterface

interface UserProviderInterface {

    public function retrieveById($identifier);
    public function retrieveByToken($identifier, $token);
    public function updateRememberToken(UserInterface $user, $token);
    public function retrieveByCredentials(array $credentials);
    public function validateCredentials(UserInterface $user, array $credentials);

}

interface UserProviderInterface {

public function retrieveById ( $identifier ) ;

public function retrieveByToken ( $identifier , $token ) ;

public function updateRememberToken ( UserInterface $user , $token ) ;

public function retrieveByCredentials ( array $credentials ) ;

public function validateCredentials ( UserInterface $user , array $credentials ) ;

}

可以看到,都是和验证过程有关的方法,也就是说我们只需要实现这些接口方法,就可以添加自己的验证驱动,而从实际使用出发,我们在项目中会使用Eloquent ORM来产生相关实体的数据模型,所以我们无须完全重写驱动,只需要改写EloquentUserProvider中的部分方法即可,在这里我们的代码如下所示:

Custom UserProvider

<?php namespace Illuminate\Auth;

class TaskPoolEloquentUserProvider extends EloquentUserProvider {

  public function __construct($model)
  {
    $this->model = $model;
  }

  /**
   * Validate a user against the given credentials.
   *
   * @param  \Illuminate\Auth\UserInterface  $user
   * @param  array  $credentials
   * @return bool
   */
  public function validateCredentials(UserInterface $user, array $credentials)
  {
    $plain = $credentials['password'];
    $authPassword = $user->getAuthPassword();
    return $authPassword['psw_hash'] === md5($plain.$authPassword['psw_salt']);
  }

}

<?php namespace Illuminate \ Auth ;

class TaskPoolEloquentUserProvider extends EloquentUserProvider {

public function __construct ( $model )

{

$this -> model = $model ;

}

/**

* Validate a user against the given credentials.

*

* @param  \Illuminate\Auth\UserInterface  $user

* @param  array  $credentials

* @return bool

*/

public function validateCredentials ( UserInterface $user , array $credentials )

{

$plain = $credentials [ ‘password’ ] ;

$authPassword = $user -> getAuthPassword ( ) ;

return $authPassword [ ‘psw_hash’ ] === md5 ( $plain . $authPassword [‘psw_salt’ ] ) ;

}

}

通过继承和覆盖的方式,我们改写了原有Eloquent驱动的验证逻辑。根据官方文档的描述,这个文件可以放在任意地方,也可以使用任意命名空间,在这里就不过多叙述了。

之后,注意到验证逻辑中的第一个参数UserInterface $user,这个参数告诉我们,支持Auth的User模型应当实现UserInterface,这个接口的定义文件也在这个目录下,简化的代码如下:

UserInterface

interface UserInterface {

  public function getAuthIdentifier();
  public function getAuthPassword();
  public function getRememberToken();
  public function setRememberToken($value);
  public function getRememberTokenName();

}

interface UserInterface {

public function getAuthIdentifier ( ) ;

public function getAuthPassword ( ) ;

public function getRememberToken ( ) ;

public function setRememberToken ( $value ) ;

public function getRememberTokenName ( ) ;

}

这些都很容易理解,都是简单的getter和setter,只需要让自己的User模型类实现它就可以了。

最后一步就是将这个扩展注册到框架当中,打开这个文件:

app/start/global.php

在其中添加如下代码:

global.php
Auth::extend('taskpooleloquent', function($app) 
{
    $provider =  new \Illuminate\Auth\TaskPoolEloquentUserProvider('User');
    return $provider;
});

Auth:: extend ( ‘taskpooleloquent’ , function ( $app )

{

$provider =    new \ Illuminate \ Auth \ TaskPoolEloquentUserProvider ( ‘User’ ) ;

return $provider ;

} ) ;

之后,就可以在Config中的Auth配置文件中使用你自己定义的这个驱动了~关于Laravel框架其实还有很多特性,我将会在实践中陆续进行整理并在这里分享。

Comments are closed.