路由
其实原文件不是这样写的,我只是把它抽了出来
原文件的路由写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); }); Auth::routes();//用户认证路由 Route::get('/home', 'HomeController@index')->name('home'); |
原路由的非常优雅,只需要调用Auth::routes();
这行代码即可。
查看Auth::routes();的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Auth extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'auth'; } /** * Register the typical authentication routes for an application. * * @param array $options * @return void */ public static function routes(array $options = []) { //1.判断UiServiceProvider这个服务是否存在,没有就报异常 if (! array_key_exists(UiServiceProvider::class, static::$app->getLoadedProviders())) { throw new LogicException('Please install the laravel/ui package in order to use the Auth::routes() method.'); } //2.实例化router到系统容器 static::$app->make('router')->auth($options); } } |
原来路由需要UiServiceProvider这个服务,接下来看看UiServiceProvider这个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class UiServiceProvider extends ServiceProvider { /** * Register the package services. * * @return void */ public function register()//注册服务 { if ($this->app->runningInConsole()) { $this->commands([ AuthCommand::class, ControllersCommand::class, UiCommand::class, ]); } } /** * Bootstrap any application services. * * @return void */ public function boot()//提供服务 { Route::mixin(new AuthRouteMethods); } } |
可以发现这是一个普通的服务注册流程,它其实对外提供的服务,是由实例化的new AuthRouteMethods
提供的,AuthRouteMethods
类的代码如下,才发现了真正路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
<?php namespace Laravel\Ui; class AuthRouteMethods { /** * Register the typical authentication routes for an application. * * @param array $options * @return void */ public function auth() { return function ($options = []) { // Authentication Routes... $this->get('login', 'Auth\LoginController@showLoginForm')->name('login'); $this->post('login', 'Auth\LoginController@login'); $this->post('logout', 'Auth\LoginController@logout')->name('logout'); // Registration Routes... if ($options['register'] ?? true) { $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register'); $this->post('register', 'Auth\RegisterController@register'); } // Password Reset Routes... if ($options['reset'] ?? true) { $this->resetPassword(); } // Password Confirmation Routes... if ($options['confirm'] ?? class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) { $this->confirmPassword(); } // Email Verification Routes... if ($options['verify'] ?? false) { $this->emailVerification(); } }; } /** * Register the typical reset password routes for an application. * * @return void */ public function resetPassword() { return function () { $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request'); $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email'); $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset'); $this->post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update'); }; } /** * Register the typical confirm password routes for an application. * * @return void */ public function confirmPassword() { return function () { $this->get('password/confirm', 'Auth\ConfirmPasswordController@showConfirmForm')->name('password.confirm'); $this->post('password/confirm', 'Auth\ConfirmPasswordController@confirm'); }; } /** * Register the typical email verification routes for an application. * * @return void */ public function emailVerification() { return function () { $this->get('email/verify', 'Auth\VerificationController@show')->name('verification.notice'); $this->get('email/verify/{id}/{hash}', 'Auth\VerificationController@verify')->name('verification.verify'); $this->post('email/resend', 'Auth\VerificationController@resend')->name('verification.resend'); }; } } |
总的来说把路由规则写成一个AuthRouteMethods
类,然后注册一个UiServiceProvider
服务,通过UiServiceProvider
对外提供路由规则的服务,这样实现了解耦。
1.登录与退出
1 2 3 4 5 6 7 8 9 10 11 |
class LoginController extends Controller { use AuthenticatesUsers; protected $redirectTo = RouteServiceProvider::HOME; public function __construct() { $this->middleware('guest')->except('logout'); } } |
登录控制器就几行代码就能实现用户的登录功能,其实是依靠AuthenticatesUsers
这个Trait,这里包含了登录的整个流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<?php namespace App\Traits; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\ValidationException; trait AuthenticatesUsers { use RedirectsUsers,ThrottlesLogins; //显示登录页面 public function showLoginForm() { return view('auth.login'); } //登录 public function login(Request $request) { //1.验证登录参数 $this->validateLogin($request); //2.限流 if (method_exists($this,'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } //3.尝试登录 if ($this->attemptsLogin($request)) { //登录成功 返回结果 return $this->sendLoginResponse($request); } //4.登录失败 增加登录次数 $this->incrementLoginAttempts($request); //5.返回登录失败的响应 return $this->sendFailedLoginResponse($request); } //验证登录参数 protected function validateLogin(Request $request) { //$this->username() 登录的帐户,可以变更某个字段作为登录帐户 $request->validate([ $this->username() => 'required|string', 'password' => 'required|string' ]); } //尝试登录 protected function attemptsLogin(Request $request) { $this->guard()->attempt($this->credentials($request),$request->filled('remember')); } // 登录的凭证 提交的用户名和密码作为登录的凭证 protected function credentials(Request $request) { return $request->only($this->username(),'password'); } // 获取guard对象 protected function guard() { return Auth::guard(); } //登录成功后返回的响应(在这里存session) protected function sendLoginResponse(Request $request) { $request->session()->regenerate();//生成session $this->clearLoginAttempts($request);//清空登录失败的次数 //是否有自定义的返回 if ($respond = $this->authenticated($request,$this->guard()->user)) { return $respond; } //是否json请求 return $request->wantsJson() ? new Response('',204)//是Jason请求,返回json : redirect()->intended($this->redirectPath());//不是Jason请求,重定向 } //登录成功后,自定义的响应 protected function authenticated(Request $request,$user) { } //登录失败,返回的响应头 protected function sendFailedLoginResponse(Request $request) { throw ValidationException::withMessages([ $this->username() => [trans('auth.failed')] ]); } //那个字段作为帐户 public function username() { return 'email'; } //退出 public function logout(Request $request) { $this->guard()->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); if ($respond = $this->loggedOut($request)) { return $respond; } $request->wantsJson() ? new Response('',204) : redirect('/'); } //自定义退出响应头 protected function loggedOut(Request $request) { } } |
登录的核心方法是login方法,而存储用户信息的是调用guard()->attempt()
;
登录限流器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<?php namespace App\Traits; use Illuminate\Auth\Events\Lockout; use Illuminate\Cache\RateLimiter; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Lang; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; trait ThrottlesLogins { //是否有太多次尝试登录但失败 protected function hasTooManyLoginAttempts(Request $request) { return $this->limiter()->tooManyAttempts( $this->throttleKey($request),$this->maxAttempts() ); } //增加尝试登录失败的次数 protected function incrementLoginAttempts(Request $request) { return $this->limiter()->hit( $this->throttleKey($request),$this->decayMinutes()*60 ); } //清除失败登录记录 protected function clearLoginAttempts(Request $request) { return $this->limiter()->clear( $this->throttleKey($request) ); } //发送限制的响应 protected function sendLockoutResponse(Request $request) { $seconds = $this->limiter()->availableIn( $this->throttleKey($request) ); throw ValidationException::withMessages([ $this->username() => [Lang::get('auth.throttle',[ 'seconds' => $seconds, 'minutes' => ceil($seconds/60) ])], ])->status(Response::HTTP_TOO_MANY_REQUESTS); } //添加锁定事件 protected function fireLockoutEvent(Request $request) { event(new Lockout($request)); } //设置衰变时间 protected function decayMinutes() { return property_exists($this,'decayMinutes') ? $this->decayMinutes : 1; } //系统限流器对象 protected function limiter() { return app(RateLimiter::class); } //生成限流key protected function throttleKey(Request $request) { return Str::lower($request->input($this->username())).'|'.$request->ip(); } //设置最大的尝试登录次数 public function maxAttempts() { return property_exists($this,'maxAttempts') ? $this->maxAttempts : 5; } } |
登录限流器,主要是调用系统的app(RateLimiter::class)
限流器来实现的,通过添加系统事件event(new Lockout($request));
来锁定请求。
重定向
RedirectsUsers
trait是用来定义重定向的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Traits; trait RedirectsUsers { public function redirectPath() { if (method_exists($this,'redirectTo')) { return $this->redirectTo(); } return property_exists($this,'redirectTo')?$this->redirectTo:'/home'; } } |
注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Traits\RegistersUsers; use App\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; class RegisterController extends Controller { use RegistersUsers;//注册流程 protected $redirectTo = RouteServiceProvider::HOME;//重写RedirectsUsers Trait的重定向地址 public function __construct()//先执行guest中间件 { $this->middleware('guest'); } protected function validator(array $data)//表单数据验证 { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); } //用户数据入库 protected function create(array $data) { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } } |
注册的核心是RegistersUsers
Trait
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
trait RegistersUsers { use RedirectsUsers; //显示注册表单 public function showRegistrationForm() { return view('auth.register'); } //注册 public function register(Request $request) { //验证表单数据 controller 需要实现validate()这个方法 $this->validator($request->all())->validate(); //添加一个注册用户的事件 controller 需要实现create()这个方法 来创建用户 event(new Registered($user = $this->create($request->all()))); //注册成功 自动登录 $this->guard()->login($user); // 自定义响应 if ($response = $this->registered($request,$user)) { return $response; } // 返回默认的响应 return $request->wantsJson()?new Response('',201):redirect($this->redirectPath()); } //取得系统的guard对象 protected function guard() { return Auth::guard(); } //自定义注册成功的响应 public function registered(Request $request,$user) { } } |
主要是执行了注册事件event(new Registered($user = $this->create($request->all())));
忘记密码
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
}
主要的核心在SendsPasswordResetEmails
Trait
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
trait SendsPasswordResetEmails { //显示表单 public function showLinkRequestForm() { return view('auth.passwords.email'); } //发送重置密码邮件 public function sendResetLinkEmail(Request $request) { //表单验证,是否存在接收重置密码邮件的邮箱地址 $this->validateEmail($request); //发送邮件 $response = $this->broker()->sendResetLink( $this->credentials($request) ); //通过Password::RESET_LINK_SENT判断邮件是否发送成功,返回对应的响应 return $response = Password::RESET_LINK_SENT ? $this->sendResetLinkResponse($request,$response) : $this->sendResetLinkFailedResponse($request,$response); } //验证表单是否存在邮件地址 protected function validateEmail(Request $request) { $request->validate(['email'=>['required','email']]); } //获得密码重置对象 protected function broker() { return Password::broker(); } //充值密码的凭证 protected function credentials(Request $request) { return $request->only('email'); } //邮件发送成功的响应 protected function sendResetLinkResponse(Request $request,$response) { return $request->wantsJson() ? new JsonResponse(['message'=>trans($response)],200) : back()->with('status',trans($response)); } //邮件发送失败的响应 protected function sendResetLinkFailedResponse(Request $request,$response) { if ($request->wantsJson()) { throw ValidationException::withMessages([ 'email'=>trans($response) ]); } return back() ->withInput($request->only('email')) ->withErrors(['email'=>trans($response)]); } } |
发送邮件的是Password::broker();
的sendResetLink()
,是否发送成功。通过Password::RESET_LINK_SENT
判断
重置密码
1 2 3 4 5 6 |
class ResetPasswordController extends Controller { use ResetPasswords; protected $redirectTo = RouteServiceProvider::HOME; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
<?php namespace App\Traits; use Illuminate\Auth\Events\PasswordReset; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Password; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; trait ResetPasswords { use RedirectsUsers; //显示存在密码的表单 public function showResetForm(Request $request,$token=null) { return view('auth.passwords.reset')->with( ['token'=>$token,'email'=>$request->email] ); } //重置密码 public function reset(Request $request) { //验证表单数据 $request->validate($this->rules(),$this->validationErrorMessages()); //重置密码 $response = $this->broker()->reset($this->credentials($request),function ($user,$password) { $this->resetPassword($user,$password); }); //通过Password::PASSWORD_RESET判断是否重置密码成功 return $response == Password::PASSWORD_RESET ? $this->sendResetResponse($request,$response) : $this->sendResetFailedResponse($request,$response); } //表单数据验证规则 protected function rules() { return [ 'token' => 'required', 'email' => 'required|email', 'password' => 'required|confirmed|min:8', ]; } //数据验证失败提示 protected function validationErrorMessages() { return []; } //取得系统broker protected function broker() { return Password::broker(); } //用户凭证 protected function credentials(Request $request) { return $request->only( 'email', 'password', 'password_confirmation', 'token' ); } //重置密码 protected function resetPassword($user,$password) { //生成加密后的密码 $this->setUserPassword($user,$password); //设置"记住密码" $user->setRememberToken(Str::random(60)); //入库 $user->save(); //添加重置密码事件 event(new PasswordReset($user)); //重置成功 自动登录 $this->guard()->login($user); } //对密码进行加密 protected function setUserPassword($user,$password) { $user->password = Hash::make($password); } //获得guard protected function guard() { return Auth::guard(); } //重置密码成功的回调 protected function sendResetResponse(Request $request,$response) { if ($request->wantsJson()) { return new JsonResponse(['message'=>trans($response)],200); } return redirect($this->redirectPath()) ->with('status', trans($response)); } //重置密码失败的回调 protected function sendResetFailedResponse(Request $request, $response) { if ($request->wantsJson()) { throw ValidationException::withMessages([ 'email' => [trans($response)], ]); } return redirect()->back() ->withInput($request->only('email')) ->withErrors(['email' => trans($response)]); } } |
验证邮箱
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class VerificationController extends Controller { use VerifiesEmails; protected $redirectTo = RouteServiceProvider::HOME; public function __construct() { $this->middleware('auth'); $this->middleware('signed')->only('verify'); $this->middleware('throttle:6,1')->only('verify','resend'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<?php namespace App\Traits; use foo\bar; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Events\Verified; use Illuminate\Http\Request; use Illuminate\Http\Response; trait VerifiesEmails { use RedirectsUsers; //显示表单 public function show(Request $request) { return $request->user()->hasVerifiedEmail() ? redirect($this->redirectPath()) : view('auth.verify'); } //验证邮件 public function verify(Request $request) { if (!hash_equals((string)$request->route('id'),(string)$request->user()->getKey())) { throw new AuthorizationException(); } //验证链接一次有效,通过带参hash 判断是否点击过 if (!hash_equals((string)$request->route('hash'),sha1($request->user()->getEmailForVerification()))) { throw new AuthorizationException(); } //邮件是否验证过了 if ($request->user()->hasVerifiedEmail()){ return $request->wantsJson() ? new Response('',204) : redirect($this->redirectPath()); } //将邮箱标记为已验证 if ($request->user()->markEmailAsVerified()) { event(new Verified($request->user())); } //自定义邮箱验证响应 if ($response = $this->verified($request)) { return $response; } //默认的响应 return $request->wantsJson() ? new Response('',204) : redirect($this->redirectPath())->with('verified',true); } protected function verified(Request $request) { // } //重新发送验证邮件 public function resend(Request $request) { //判断是否验证过 if ($request->user()->hasVerifiedEmail()) { return $request->wantsJson() ? new Response('',204) : redirect($this->redirectPath()); } $request->user()->sendEmailVerificationNotification(); return $request->wantsJson() ? new Response('',202) : back()->with('resent',true); } } |
确认密码
1 2 3 4 5 6 7 8 9 10 11 |
class ConfirmPasswordController extends Controller { use ConfirmsPasswords; protected $redirectTo = RouteServiceProvider::HOME; public function __construct() { $this->middleware('auth'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?php namespace App\Traits; use Illuminate\Http\Request; use Illuminate\Http\Response; trait ConfirmsPasswords { use RedirectsUsers; //显示确认密码表单 public function showConfirmForm() { return view('auth.passwords.confirm'); } //确认密码 public function confirm(Request $request) { $request->validate($this->rules(),$this->validationErrorMessages()); $this->resetPasswordConfirmationTimeout($request); return $request->wantsJson() ? new Response('',204) : redirect()->intended($this->redirectPath()); } //表单验证规则 protected function rules() { return [ 'password' => 'required|password' ]; } //规则验证错误 返回的信息 protected function validationErrorMessages() { return []; } protected function resetPasswordConfirmationTimeout(Request $request) { $request->session()->put('auth.password_confirm_at',time()); } } |