對於一個有登錄限制(權限限制)的網站,用戶輸入身份驗證信息以後,驗證成功後跳轉到登錄前的頁面是一項很人性化的功能。那麼獲取登錄前的頁面地址就很關鍵,今天在做一個yii2項目的登錄調試時發現了一些很有意思的問題,記錄下來。
1,場景描述
網站SiteA上的頁面Page2需要登錄後才能查看,Page2的鏈接放在頁面Page1的一個按鈕Button上,Page1在登錄前後都是可以訪問的,SiteA只提供了微信掃碼登錄的入口。
2,功能需求
假定訪客User已經在SiteA上注冊過,但當前未登錄。User在浏覽Page1時,如果點擊頁面內的Button,則會來到掃碼登錄頁。用微信掃碼登錄成功後則會跳轉至Page2。
3,跳轉頁面的代碼
在微信授權後要回調的地址中設置好控制器controller和方法callback。
然後在callback方法中進行用戶登錄後的邏輯編寫,如果登錄成功:
return $this->goBack();
這麼寫在電腦的浏覽器上訪問網站時是沒有問題的,可以實現上述的功能需求。但是如果User是用手機在微信中訪問SiteA的Page1,然後點擊了Button,那麼他是不是會像在電腦上那樣來到Page2呢?
今天測試了下,沒有跳轉到Page2,反而來到了網站的首頁。至於原因是什麼,現在還不太清楚。不過倒是找到了解決的方法:在微信授權後要回調地址的地址中加上state參數,在state參數中附帶上User登錄前浏覽的頁面地址url。這樣在callback方法中就可以拿到url,上面登錄成功後的頁面跳轉就可以這麼寫:
1 if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') === false) { 2 // User在pc浏覽器中的跳轉 3 return $this->goBack(); 4 } else { 5 // User在微信浏覽器中的跳轉 6 return $this->redirect(url); 7 }
4,User登錄前浏覽的頁面地址url該怎麼獲得
yii2中提供了一個方法,下面的方法就可以獲得登錄前的頁面url。
Yii::$app->user->returnUrl;
5,yii2是如何實現4中的方法和功能的呢?
4中的方法是在yii\web\User中定義的:
1 public function getReturnUrl($defaultUrl = null) 2 { 3 $url = Yii::$app->getSession()->get($this->returnUrlParam, $defaultUrl); 4 if (is_array($url)) { 5 if (isset($url[0])) { 6 return Yii::$app->getUrlManager()->createUrl($url); 7 } else { 8 $url = null; 9 } 10 } 11 12 return $url === null ? Yii::$app->getHomeUrl() : $url; 13 }
第3行yii\web\Session的get方法:
1 public function get($key, $defaultValue = null) 2 { 3 $this->open(); 4 return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue; 5 }
可以看到yii2是從session中獲取的 $this->returnUrlParam 作為登錄前的浏覽頁面地址。
那麼它是如何存儲的session,又是在什麼時間存儲的呢?答案都在yii\web\User和yii\webSession中。
yii\web\User中的setReturnUrl()方法:
1 public function setReturnUrl($url) 2 { 3 Yii::$app->getSession()->set($this->returnUrlParam, $url); 4 }
yii\web\User中的loginRequired()方法中調用了setReturnUrl()方法:
1 public function loginRequired($checkAjax = true, $checkAcceptHeader = true) 2 { 3 $request = Yii::$app->getRequest(); 4 $canRedirect = !$checkAcceptHeader || $this->checkRedirectAcceptable(); 5 if ($this->enableSession 6 && $request->getIsGet() 7 && (!$checkAjax || !$request->getIsAjax()) 8 && $canRedirect 9 ) { 10 $this->setReturnUrl($request->getUrl()); 11 } 12 // ......省略的代碼 13 }
然後在yii\filters\AccessControl中的denyAccess()中又調用了loginRequired()
1 protected function denyAccess($user) 2 { 3 if ($user->getIsGuest()) { 4 $user->loginRequired(); 5 } else { 6 throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.')); 7 } 8 }
然後在yii\filters\AccessControl中的beforeAction()中又調用了denyAccess()。
AccessControl可以配置在控制器中,也可以配置在yii2應用的配置文件main.php中,如果配置了,那麼在每一次調用控制器的action之前都會執行這個beforeAction(),也就會觸發session的存儲。
yii\web\User中設置了默認要存儲的url鍵名
public $returnUrlParam = '__returnUrl';
可以看下session中的內容驗證下:
6,再來看下3中的$this->goBack()方法
yii\web\Contorller中
1 public function goBack($defaultUrl = null) 2 { 3 return Yii::$app->getResponse()->redirect(Yii::$app->getUser()->getReturnUrl($defaultUrl)); 4 }
可以看到它也是跳轉到從 Yii::$app->getUser()->getReturnUrl() 獲取的url,其實也就是從session中獲取的url地址。
7,疑問
那麼上面的討論都是基於登錄操作(涉及到了登錄前後的邏輯),如果一個網站沒有登錄功能,也用不到權限控制(AccessControl),就更不用session存儲url了。嗯,應該是這麼回事。不用登錄操作的話,也就不會涉及到剛開始提到的頁面跳轉了。
完