Lifesign's Blog

Laravel auth 中的 viaRemember 方法探究

有网友在 phphub 上提问 Auth::viaRemember的用法,翻了一下源码,记录如下。

假定是如下的逻辑导致的 false :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Route::get('/', function(){
//这里就 hardcode 了
$credentials = [
'name' => 'admin',
'password' => 'admin'
];
if (Auth::attempt($credentials, true)) {
return Redirect::intended('/dashboard');
}
return Redirect::to('login')->withMessage('Wrong credentials');
});
Route::get('dashboard', function(){
return 'this is dashboard';
});
Route::get('checklogin', function(){
if (Auth::viaRemember()) {
return 'yes';
} else {
return 'no';
}
});

本意是想通过 Auth::viaRemember 方法来判断是否用户是通过 remember me 的形式登录的。但是始终返回 false 。

先来说一下 viaRemember 的用法,它的含义其实是检查一个用户的登录是否是通过 remember_xxxxxxxx也就是所谓的 remember mecookie 来登录的。

所以其实优先应该执行的是用户登录的判断,viaRemember方法返回只是一个简单的标识,它其实并不会去做验证的操作。
获取一个认证的用户的流程主要是如下两种途径:

通过 session。 这种情况下在 session 失效期内是可以获取到 user 对象的。

通过 remember me cookie 。如果 laravel_session 中没有用户的信息,而又设置了 remember me 的 cookie,laravel 便会尝试根据这个 cookie 来获取用户,同时写入到 laravel_session 中,这样下一次就会从 session 中读取用户信息了。
如下是 Auth 这个 facade 对应的 Illuminate\Auth\Guard

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
/**
* Get the currently authenticated user.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if ($this->loggedOut) return;
// If we have already retrieved the user for the current request we can just
// return it back immediately. We do not want to pull the user data every
// request into the method because that would tremendously slow an app.
if ( ! is_null($this->user))
{
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if ( ! is_null($id))
{
$user = $this->provider->retrieveById($id);
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->getRecaller();
if (is_null($user) && ! is_null($recaller))
{
$user = $this->getUserByRecaller($recaller);
if ($user)
{
$this->updateSession($user->getAuthIdentifier());
$this->fireLoginEvent($user, true);
}
}
return $this->user = $user;
}

在这个方法中可以看到 getUserByRecaller 只有当 session cookie 认证失败的时候会调用。

viaRemember 方法只是一个简单的 getter 来返回 viaRemember 这个属性,而 viaRemember 属性是在 getUserByRecaller 方法中设置的。

1
2
3
4
5
6
7
8
9
/**
* Determine if the user was authenticated via "remember me" cookie.
*
* @return bool
*/
public function viaRemember()
{
return $this->viaRemember;
}

因此影响 viaRemember 方法的返回值关键在于是否调用了 getUserByRecaller 方法。
所以在调用 viaRemember 方法之前,需要先调用 Auth::check() 才会隐式的去调用 Auth::user() 以及 getUserByRecaller() 了。

需注意的是只有 viaRemeber 的第一次才会生效,下一次请求由于已经设定了 session,故优先会从 session 中读取,所以此后的请求 如果在 session 时效时间内 (默认应该是 120 min) 都会是 false 。如果你在 config/session.php 中的 expire_on_close 设置为 true,那么每次重开浏览器的时候,第一次便会是 true,此后还会是 false 。