一、經過 Auth 中間件檢查后跳轉至登錄頁面
也就是沒有通過 auth
中間件的認證檢查,被 auth
中間件攔截后跳轉至登錄頁面。這種情況下,Laravel 默認會在用戶登錄成功后自動跳轉回登錄前瀏覽的頁面。auth
中間件是怎么做到的?
打開 auth
中間件文件:
// vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php protected function authenticate(array $guards) { if (empty($guards)) { return $this->auth->authenticate(); } foreach ($guards as $guard) { if ($this->auth->guard($guard)->check()) { return $this->auth->shouldUse($guard); } } throw new AuthenticationException('Unauthenticated.', $guards); }
auth
中間件通過 authenticate()
方法檢查用戶是否經過了認證,如果沒有經過認證就拋出一個異常。其中並沒有跳轉至登錄頁面的操作,也就是說 Laravel 是在捕捉到這個異常后進行進一步的操作。
Laravel 在異常處理類 Illuminate\Foundation\Exceptions\Handler
中處理這個 AuthenticationException
異常。由於在異常處理類的 $internalDontReport
屬性中包含了該異常,所以 AuthenticationException
異常不需要報告或者寫入日志,只需要通過 render()
方法處理為一個響應。在 render()
方法中會調用 unauthenticated()
方法來處理 AuthenticationException
異常:
protected function unauthenticated($request, AuthenticationException $exception) { return $request->expectsJson() ? response()->json(['message' => $exception->getMessage()], 401) : redirect()->guest(route('login')); }
在
unauthenticated()
方法中首先檢查請求是否需要 Json 響應,如果不需要就會執行 redirect()->guest()
方法:
public function guest($path, $status = 302, $headers = [], $secure = null) { $this->session->put('url.intended', $this->generator->full()); return $this->to($path, $status, $headers, $secure); }
在重定向的 guest()
方法中,先將用戶訪問(但沒通過認證)的頁面地址存入 Session 的 url.intended
鍵上,然后重定向到登錄界面。這個 Session 的 url.intended
鍵被創建的意義就是在登錄成功后用來跳轉的。
打開登錄控制器 Auth\LoginController.php
文件中 LoginController
繼承的 AuthenticatesUsers
這個 Trait。在這個 Trait 中,login()
方法處理登錄請求,驗證成功后調用 sendLoginResponse()
方法返回響應:
// vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php protected function sendLoginResponse(Request $request) { $request->session()->regenerate(); $this->clearLoginAttempts($request); return $this->authenticated($request, $this->guard()->user()) ?: redirect()->intended($this->redirectPath()); }
在該方法最后的 return
中可以看到:如果 authenticated()
方法返回值不為真,則執行 redirect()->intended()
方法。而 authenticated()
方法默認為空,所以必然會執行 redirect()->intended()
方法:
// vendor/laravel/framework/src/Illuminate/Routing/Redirector.php public function intended($default = '/', $status = 302, $headers = [], $secure = null) { $path = $this->session->pull('url.intended', $default); return $this->to($path, $status, $headers, $secure); }
在重定向的 intended()
方法中會檢查 Seesion url.intended
鍵的值。如果有值,就會跳轉到該地址,也就是訪問但被 Auth 中間件攔截的那個頁面。
總結流程如下:
訪問需要認證的頁面 -> 被 Auth 中間件攔截后拋出異常 -> 處理異常:在 Session 中存入要訪問的頁面地址,然后跳轉至登錄頁面 -> 登錄成功后從 Session 中取出先前存入的頁面地址,並跳轉至該地址。
二、由不需認證的頁面跳轉至登錄頁面
也就是訪問的頁面是公開的,登錄或者沒登錄的用戶都能訪問,在這個頁面上點擊登錄按鈕后進入登錄頁面。這種情況下,Laravel 默認返回的是域名的根地址。只要搞明白了第一種情況中 Lararvel 的處理流程,這種情況處理起來非常簡單:
只需在 Auth\LoginController.php
控制器中重寫其繼承的 AuthenticatesUsers
這個 Trait 中的 showLoginForm()
方法即可:
// app/Http/Controllers/Auth/LoginController.php use AuthenticatesUsers; // 打開登錄頁面 public function showLoginForm() { session(['url.intended'=>url()->previous()]); return view('auth.login'); }
只需在原有的 showLoginForm()
方法中添加一行即可!這個操作的關鍵就是打開登錄頁面時,將上一個瀏覽的頁面地址存入 Session 的 url.intended
鍵。
由於登錄步驟和第一種情況一樣,所以 Laravel 會在登錄成功后檢查 Session url.intended
鍵的值,如果有值就會跳轉到該頁面。
(完)