對於一個有登錄限制(權限限制)的網站,用戶輸入身份驗證信息以後,驗證成功後跳轉到登錄前的頁面是一項很人性化的功能。那麼獲取登錄前的頁面地址就很關鍵,今天在做一個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了。嗯,應該是這麼回事。不用登錄操作的話,也就不會涉及到剛開始提到的頁面跳轉了。
完