Authentication may be extended the same way as the cache and session facilities. Again, we will use the extend
method we have become familiar with:
身份认证模块的扩展方式和缓存与会话的扩展方式一样:使用我们熟悉的extend
方法就可以进行扩展:
Auth::extend('riak', function($app)
{
// Return implementation of Illuminate\Auth\UserProviderInterface
});
The UserProviderInterface
implementations are only responsible for fetching a UserInterface
implementation out of persistent storage system, such as MySQL, Riak, etc. These two interfaces allow the Laravel authentication mechanisms to continue functioning regardless of how the user data is stored or what type of class is used to represent it.
接口UserProviderInterface
负责从各种持久化存储系统——如MySQL,Riak等——中获取数据,然后得到接口UserInterface
的实现对象。有了这两个接口,Laravel的身份认证机制就可以不用管用户数据是如何储存的、究竟哪个类来代表用户对象这种事儿,从而继续专注于身份认证本身的实现。
Let's take a look at the UserProviderInterface
:
咱们来看一看UserProviderInterface
接口的代码:
interface UserProviderInterface {
public function retrieveById($identifier);
public function retrieveByCredentials(array $credentials);
public function validateCredentials(UserInterface $user, array $credentials);
}
The retrieveById
function typically receives a numeric key representing the user, such as an auto-incrementing ID from a MySQL database. The UserInterface
implementation matching the ID should be retrieved and returned by the method.
方法retrieveById
通常接受一个数字参数用来表示一个用户,比如MySQL数据库的自增ID。该方法要找到匹配该ID的UserInterface
的实现对象,并且将该对象返回。
The retrieveByCredentials
method receives the array of credentials passed to the Auth::attempt
method when attempting to sign into an application. The method should then “query” the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a “where” condition on $credentials['username']
. This method should not attempt to do any password validation or authentication.
retrieveByCredentials
方法接受一个参数作为登录帐号。该参数是在尝试登录系统时从Auth::attempt
方法传来的。那么该方法应该“查询”底层的持久化存储系统,来找到那些匹配到该帐号的用户。通常该方法会执行一个带有“where”条件的查询来匹配参数里的$credentials['username']
。该方法不应该做任何密码验证。
The validateCredentials
method should compare the given $user
with the $credentials
to authenticate the user. For example, this method might compare the $user->getAuthPassword();
string to a Hash::make
of $credentials['password']
.
validateCredentials
方法会通过比较$user
参数和$credentials
参数来检测用户是否通过认证。比如,该方法会调用$user->getAuthPassword();
方法,将得到的字符串与$credentials['password']
经过Hash::make
处理后的结果进行比对。
Now that we have explored each of the methods on the UserProviderInterface
, let's take a look at the UserInterface
. Remember, the provider should return implementations of this interface from the retrieveById
and retrieveByCredentials
methods:
现在我们探索了UserProviderInterface
接口的每一个方法,接下来咱们看一看UserInterface
接口。别忘了UserInterface
的实例应当是retrieveById
和retrieveByCredentials
方法的返回值:
interface UserInterface {
public function getAuthIdentifier();
public function getAuthPassword();
}
This interface is simple. The getAuthIdentifier
method should return the “primary key” of the user. In a MySQL back-end, again, this would be the auto-incrementing primary key. The getAuthPassword
should return the user's hashed password. This interface allows the authentication system to work with any User class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes a User
class in the app/models
directory which implements this interface, so you may consult this class for an implementation example.
这个接口很简单。 getAuthIdentifier
方法应当返回用户的“主键”。就像刚才提到的,在MySQL中可能就是自增主键了。getAuthPassword
方法应当返回经过散列处理的用户密码。有了这个接口,身份认证系统就可以不用关心用户类到底使用了什么ORM或者什么存储方式。Laravel已经在app/models
目录下,包含了一个默认的User
类且实现了该接口。所以你可以参考这个类当例子。
Finally, once we have implemented the UserProviderInterface
, we can ready to register our extension with the Auth
facade:
当我们最后实现了UserProviderInterface
接口后,我们可以将该扩展注册进Auth
里面:
Auth::extend('riak', function($app)
{
return new RiakUserProvider($app['riak.connection']);
});
After you have registered the driver with the extend
method, you switch to the new driver in your app/config/auth.php
configuration file.
使用extend
方法注册好驱动以后,你就可以在app/config/auth.php
配置文件里面切换到新的驱动了。
Almost every service provider included with the Laravel framework binds objects into the IoC container. You can find a list of your application's service providers in the app/config/app.php
configuration file. As you have time, you should skim through each of these provider's source code. By doing so, you will gain a much better understanding of what each providers adds to the framework, as well as that keys are used to bind various services into the IoC container.
Laravel框架内几乎所有的服务提供者都会绑定一些对象到IoC容器里。你可以在app/config/app.php
文件里找到服务提供者列表。如果你有时间的话,你应该大致过一遍每个服务提供者的源码。这么做你便可以对每个服务提供者有更深的理解,明白他们都往框架里加了什么东西,对应的什么键。那些键就用来联系着各种各样的服务。
For example, the PaginationServiceProvider
binds a paginator
key into the IoC container, which resolves into Illuminate\Pagination\Environment
instance. You can easily extend and override this class within your own application by overriding this IoC binding. For example, you could create a class that extend the base Environment
:
举个例子,PaginationServiceProvider
向容器内绑定了一个paginator
键,对应着一个Illuminate\Pagination\Environment
的实例。你可以很容易的通过覆盖容器绑定来扩展重写该类。比如,你可以创建一个扩展自Environment
类的子类:
namespace Snappy\Extensions\Pagination;
class Environment extends \Illuminate\Pagination\Environment {
//
}
Once you have created your class extension, you may create a new SnappyPaginationProvider
service provider class which overrides the paginator in its boot
method:
子类写好以后,你可以再创建个新的SnappyPaginationProvider
服务提供者来扩展其boot
方法,在里面覆盖paginator:
class SnappyPaginationProvider extends PaginationServiceProvider {
public function boot()
{
App::bind('paginator', function()
{
return new Snappy\Extensions\Pagination\Environment;
}
parent::boot();
}
}
Note that this class extends the PaginationServiceProvider
, not the default ServiceProvider
base class. Once you have extended the service provider, swap out the PaginationServiceProvider
in your app/config/app.php
configuration file with the name of your extended provider.
注意这里我们继承了PaginationServiceProvider
,而非默认的基类ServiceProvider
。扩展的服务提供者编写完毕后,就可以在app/config/app.php
文件里将PaginationServiceProvider
替换为你刚扩展的那个类了。
This is the general method of extending any core class that is bound in the container. Essentially every core class is bound in the container in this fashion, and can be overridden. Again, reading through the included framework service providers will familiarize you with where various classes are bound into the container, and what keys they are bound by. This is a great way to learn more about how Laravel is put together.
这就是扩展绑定进容器的核心类的一般方法。基本上每一个核心类都以这种方式绑定进了容器,都可以被重写。还是那一句话,读一遍框架内的服务提供者源码吧。这有助于你熟悉各种类是怎么绑定进容器的,都绑定的是哪些键。这是学习Laravel框架到底如何运转的好方法。
Because it is such a foundational piece of the framework and is instantiated very early in the request cycle, extending the Request
class works a little differently than the previous examples.
由于这玩意儿是框架里面非常基础的部分,并且在请求流程中很早就被实例化,所以要扩展Request
类的方法与之前相比是有些许不同的。
First, extend the class like normal:
首先还是要写个子类:
namespace QuickBill\Extensions;
class Request extends \Illuminate\Http\Request {
// Custom, helpful methods here...
}
Once you have extended the class, open the bootstrap/start.php
file. This file is one of the very first files to be included on each request to your application. Note that the first action performed is the creation of the Laravel $app
instance:
子类写好后,打开bootstrap/start.php
文件。该文件是应用的请求流程中最早被载入的几个文件之一。要注意被执行的第一个动作是创建Laravel的$app
实例:
$app = new \Illuminate\Foundation\Application;
When a new application instance is created, it will create a new Illuminate\Http\Request
instance and bind it to the IoC container using the request
key. So, we need a way to specify a custom class that should be used as the “default” request type, right? And, thankfully, the requestClass
method on the application instance does just this! So, we can add this line at the very top of our bootstrap/start.php
file:
当新的应用实例创建后,它将会创建一个Illuminate\Http\Request
的实例并且将其绑定到IoC容器里,键名为request
。所以我们需要找个方法来将一个自定义的类指定为“默认的”请求类,对不对?而且幸运的是,应用实例有一个名为requestClass
的方法就是用来干这事儿的!所以我们只需要在bootstrap/start.php
文件最上面加一行:
use Illuminate\Foundation\Application;
Application::requestClass('QuickBill\Extensions\Request');
Once you have specified the custom request class, Laravel will use this class anytime it creates a Request
instance, conveniently allowing you to always have an instance of your custom request class available, even in unit test!
一旦你指定了自定义的请求类,Laravel将在任何时候都可以使用这个Request
类的实例。并使你很方便的能随时访问到它,甚至单元测试也不例外!