Laravel之认证服务
一.用户认证 配置文件在config/auth.php下 1.添加认证路由 // 认证路由... Route::get('auth/login', 'Auth\AuthController@getLogin'); Route::post('auth/login', 'Auth\AuthController@postLogin'); Route::get('auth/logout', 'Auth\AuthController@getLogout'); // 注册路由... Route::get('auth/register', 'Auth\AuthController@getRegister'); Route::post('auth/register', 'Auth\AuthController@postRegister'); 2.视图 a.登录表单 <!-- resources/views/auth/login.blade.php 下面是显示注册失败后的错误信息 --> @if(count($errors)>0) @foreach($errors->all() as $value) {{$value}} @endforeach @endif <form method="POST" action="/auth/login"> {!! csrf_field() !!} <div> Email <input type="email" name="email" value="{{ old('email') }}"> </div> <div> Password <input type="password" name="password" id="password"> </div> <div> <input type="checkbox" name="remember"> Remember Me </div> <div> <button type="submit">Login</button> </div> </form> b.注册表单 <!-- resources/views/auth/register.blade.php --> <form method="POST" action="/auth/register"> {!! csrf_field() !!} <div> Name <input type="text" name="name" value="{{ old('name') }}"> </div> <div> Email <input type="email" name="email" value="{{ old('email') }}"> </div> <div> Password <input type="password" name="password"> </div> <div> Confirm Password <input type="password" name="password_confirmation"> </div> <div> <button type="submit">Register</button> </div> </form> 3.认证 a.在App/Http/Controllors/Auth的AuthController.php,按需修改以下参数 protected $redirectPath = '/dashboard';//登录成功后的重定向 protected $loginPath = '/login';//登录失败后的重定向 b.自定义字段 要修改新用户注册所必需的表单字段,或者自定义新用户字段如何存储到数据库,你可以修改AuthController类。该类负责为应用验证和创建新用户。 AuthController 的validator 方法包含了新用户的验证规则,你可以随意按需要自定义该方法。 AuthController 的create 方法负责使用Eloquent ORM 在数据库中创建新的App\User 记录。你可以基于自己的需要随意自定义该方法。 4.获取认证用户 $user = Auth::user(); 如: class ProfileController extends Controller{ /** * 更新用户属性. * * @param Request $request * @return Response */ public function updateProfile(Request $request) { if ($request->user()) { // $request->user() 返回认证用户实例... } } } 5.判断当前用户是否有认证 if (Auth::check()) { // The user is logged in... } 6.使用中间件 // 使用路由闭包... Route::get('profile', ['middleware' => 'auth', function() { // 只有认证用户可以进入... }]); // 使用控制器... Route::get('profile', [ 'middleware' => 'auth', 'uses' => 'ProfileController@show' ]); 或者在控制器中 public function __construct(){ $this->middleware('auth'); } 7.登录失败次数限制 如果你正在使用 Laravel 内置的AuthController 类, Illuminate\Foundation\Auth\ThrottlesLogins trait可以用于限制用户登录失败次数。默认情况下,用户在几次登录失败后将在一分钟内不能登录,这种限制基于用户的用户名/邮箱地址+IP 地址: <?php namespace App\Http\Controllers\Auth; use App\User;use Validator; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ThrottlesLogins; use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; class AuthController extends Controller{ use AuthenticatesAndRegistersUsers, ThrottlesLogins; // AuthController 类的其它部分... } 8.手动认证用户 使用Auth门面的attempt方法 class AuthController extends Controller{ /** * 处理登录认证 * * @return Response */ public function authenticate() { /* *如果需要记住我这个功能,可以传递一个$remember参数到attempt中 *if(Auth::attempt(['email' => $email, 'password' => $password], $remember)){...} *if (Auth::viaRemember()) {//} *可以使用viaRemember 方法来判断用户是否使用“记住我”cookie 进行认证 *使用自动认证的记住我功能,需要生成一下key,执行php artisan key:generate */ if (Auth::attempt(['email' => $email, 'password' => $password])) { // 认证通过... return redirect()->intended('dashboard'); } } } attempt 方法接收键值数组对作为第一个参数,数组中的值被用于从数据表中查找用户,因此,在上面的例子中,用户将会通过email 的值获取,如果用户被找到,经哈希运算后存储在数据中的密码将会和传递过来的经哈希运算处理的密码值进行比较。如果两个经哈希运算的密码相匹配那么一个认证 session 将会为这个用户开启。 如果认证成功的话attempt 方法将会返回true 。否则,返回 false。 重定向器上的intended 方法将会将用户重定向到登录之前用户想要访问的 URL,在目标 URL 无效的情况下备用 URI 将会传递给该方法。 如果你想的话,除了用户邮件和密码之外还可以在认证查询时添加额外的条件,例如,我们可以验证被标记为有效的用户: if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // The user is active, not suspended, and exists. } 退出: Auth::logout();//这将清除掉session信息 9.用用户名登录,不用email登录 Laravel5.1默认使用email来验证登录的,可以在AuthController中设置$username属性来指定登录账号选项,该属性默认值是email protected $username = 'name'; 10.其他认证方法: Auth::login($user);//认证一个用户实例 Auth::loginUsingId(1);//通过id认证 一次性认证用户: 可以使用 once 方法只在单个请求中将用户登录到应用,而不存储任何 session 和 cookie,这在构建无状态的 API 时很有用。once 方法和 attempt 方法用法差不多: if (Auth::once($credentials)) { // } 二.重置密码 1.迁移数据库 php artisan migrate 2.路由 // 密码重置链接请求路由... Route::get('password/email', 'Auth\PasswordController@getEmail'); Route::post('password/email', 'Auth\PasswordController@postEmail'); // 密码重置路由... Route::get('password/reset/{token}', 'Auth\PasswordController@getReset'); Route::post('password/reset', 'Auth\PasswordController@postReset'); 3.视图 重置密码,填写邮箱页面 <!-- resources/views/auth/password.blade.php --> <form method="POST" action="/password/email"> {!! csrf_field() !!} <div> Email <input type="email" name="email" value="{{ old('email') }}"> </div> <div> <button type="submit"> Send Password Reset Link </button> </div> </form> 点击发送邮件页面 <!-- resources/views/emails/password.blade.php --> Click here to reset your password: {{ url('password/reset/'.$token) }} 4.密码重置表单页面 当用户点击电子邮件中的链接来重置密码时,需要提交一个密码重置表单,该视图位于resources/views/auth/reset.blade.php 。 下面是一个密码重置表单示例: <form method="POST" action="/password/reset"> {!! csrf_field() !!} <input type="hidden" name="token" value="{{ $token }}"> <div> <input type="email" name="email" value="{{ old('email') }}"> </div> <div> <input type="password" name="password"> </div> <div> <input type="password" name="password_confirmation"> </div> <div> <button type="submit"> Reset Password </button> </div> </form> 5.重置密码后 如果你已经定义好路由和视图来重置用户密码,只需要在浏览器中访问这些路由即可。框架自带的PasswordController 已经包含了发送密码重置链接邮件以及更新数据库中密码的逻辑。 密码被重置后,用户将会自动登录到应用并重定向到/home 。你可以通过定义上PasswordController 的redirectTo 属性来自定义post 密码重置跳转链接: protected $redirectTo = '/dashboard'; 注意:默认情况下,密码重置令牌一小时内有效,你可以通过修改config/auth.php 文件中的选项reminder.e xpire 来改变有效时间。 三.通过Gate检查权限 1.当前用户权限检查 用户删除某篇文章时,判断该文章是否有权限,即自己发布的,可以删除,否则不能删除 在Auth服务提供者中定义: <?php namespace App\Providers; use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider{ /** * 注册应用所有的认证/授权服务. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return void */ public function boot(GateContract $gate) { parent::registerPolicies($gate); //如果设置了该类的$policies属性,下面将无效 $gate->define('update-post', function ($user, $post) { return $user->id === $post->user_id; }); } } 注意我们并没有检查给定$user 是否为 NULL,当用户未经过登录认证或者用户没有通过forUser 方法指定,Gate 会自动为所有权限返回 false。 基于类的权限 除了注册授权回调闭包之外,还可以通过传递包含权限类名和类方法的方式来注册权限方法,当需要的时候,该类会通过服务容器进行解析: $gate->define('update-post', 'PostPolicy@update'); 使用: class PostController extends Controller{ /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); if (Gate::denies('update-post', $post)) { abort(403); } } // 更新文章... } 当然, allows 方法和 denies 方法是相对的,如果动作被授权会返回 true ,check 方法是 allows 方法的别 名。 2.为指定的用户检查权限 为指定用户检查权限 如果你想要使用 Gate 门面判断非当前用户是否有权限,可以使用 forUser 方法: if (Gate::forUser($user)->allows('update-post', $post)) { // } 也可以传递多个参数 Gate::define('delete-comment', function ($user, $post, $comment) { // }); 或者传入数组 if (Gate::allows('delete-comment', [$post, $comment])) { // } 四.通过User检查权限 1.控制器中检查 <?php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 更新给定文章 * * @param \Illuminate\Http\Request $request * @param int $id * @return Response */ public function update(Request $request, $id) { $post = Post::findOrFail($id); if ($request->user()->cannot('update-post', $post)) { abort(403); } // 更新文章... } } 当然, can 方法和cannot 方法相反 if ($request->user()->can('update-post', $post)) { // 更新文章... } 2.模板中检查 Laravel 提供了 Blade 指令@can 来快速检查当前用户是否有指定权限 <a href="/post/{{ $post->id }}">View Post</a> @can('update-post', $post) <a href="/post/{{ $post->id }}/edit">Edit Post</a> @endcan 可以将@can 指令和@else 指令联合起来使用 @can('update-post', $post) <!-- The Current User Can Update The Post --> @else <!-- The Current User Can't Update The Post --> @endcan 3.表单请求中检查 可以选择在表单请求的authorize 方法中使用Gate 定义的权限 /** * 判断请求用户是否经过授权 * * @return bool */ public function authorize(){ $postId = $this->route('post'); return Gate::allows('update', Post::findOrFail($postId)); } 五.策略类 由于在AuthServiceProvider 中定义所有的授权逻辑将会变得越来越臃肿笨重,尤其是在大型应用中,所以 Laravel 允许你将授权逻辑分割到多个“策略”类中,策略类是原生的 PHP 类,基于授权资源对授权逻辑进行分组。 1.生成命令 生成一个策略类 php artisan make:policy PostPolicy 生成的策略PostPolicy位于app/Policies目录下 2.映射策略类 <?php namespace App\Providers; use App\Post; use App\Policies\PostPolicy; use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider{ /** * 应用的策略映射 * * @var array */ protected $policies = [ Post::class => PostPolicy::class, ]; } 3.编写策略(PostPolicy.php) <?php namespace App\Policies; use App\User; use App\Post; class PostPolicy{ /** * 判断给定文章是否可以被给定用户更新 * * @param \App\User $user * @param \App\Post $post * @return bool */ public function update(User $user, Post $post) { return $user->id === $post->user_id; } } 你可以继续在策略类中为授权的权限定义更多需要的方法,例如,你可以定义show , destroy , 或者addComment 方法来认证多个Post 动作。 注意:所有策略类都通过服务容器进行解析,这意味着你可以在策略类的构造函数中类型提示任何依赖,它们将会自动被注入。 4.检查策略(使用) 通过 User 模型 User 模型的 can 和 cannot 方法将会自动使用给定参数中有效的策略类。这些方法提供了 便利的方式来为应用接收到的任意User 实例进行授权 1.控制器中Gate public function update($id) { $post = Post::findOrFail($id); if (Gate::denies('update', $post)) { abort(403); } // 更新文章... } 2.通过 User 模型 User 模型的 can 和 cannot 方法将会自动使用给定参数中有效的策略类。这些方法提供了 便利的方式来为应用接收到的任意User 实例进行授权 if ($user->can('update', $post)) { // } if ($user->cannot('update', $post)) { // } 模板中 @can('update', $post) <!-- The Current User Can Update The Post --> @endcan 3.通过帮助函数 if (policy($post)->update($user, $post)) { // } 六.控制器授权 1.手动判断 默认情况下,Laravel自带的控制器基类App\Http\Controllers\Controller使用了AuthorizesRequeststrait,该trait提供了可用于快速授权给定动作的authorize方法,如果授权不通过,则抛出HttpException异常。 该authorize方法和其他多种授权方法使用方法一致,例如Gate::allows和$user->can()。因此,我们可以这样使用authorize方法快速授权更新Post的请求: <?php namespace App\Http\Controllers; use App\Post; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); $this->authorize('update', $post); // 更新文章... } } 如果授权成功,控制器继续正常执行;然而,如果authorize方法判断该动作授权失败,将会抛出HttpException 异常并生成带403 Not Authorized状态码的HTTP响应。正如你所看到的,authorize方法是一个授权动作、抛出异常的便捷方法。 AuthorizesRequeststrait还提供了authorizeForUser方法用于授权非当前用户: $this->authorizeForUser($user, 'update', $post); 2.自动判断 通常,一个策略类方法对应一个控制器上的方法,例如,在上面的update方法中,控制器方法和策略类方法共享同一个方法名:update。 正是因为这个原因,Laravel允许你简单传递实例参数到authorize方法,被授权的权限将会自动基于调用的方法名进行判断。在本例中,由于authorize在控制器的update方法中被调用,那么对应的,PostPolicy上update方法将会被调用: /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id){ $post = Post::findOrFail($id); $this->authorize($post); // 更新文章... }