Laravel Auth::attempt() 返回 false

2021-12-18 00:00:00 authentication php laravel laravel-5

我是家庭爱好者,正在学习 Laravel,目前版本为 5.3.我使用的是 Mac,既不是 homestead 也不是 vagrant.

I am a home hobbyist and am studying Laravel, currently in version 5.3. I am using a Mac, neither homestead nor vagrant.

我目前正在开发一个使用登录名和注册系统来创建用户的网站.

I'm currently working on a website that uses a login and a register system to create users.

我使用 php artisan migrate 在本地操作我的数据库.

I've used php artisan migrate to manipulate my database locally.

如下所示,它包含三个字段,即:

As listed below, it has three fields, namely:

  • 电子邮件
  • 用户名
  • 密码

我有一个 User 模型 (users.php):

I have a User model (users.php):

<?php

namespace blog;

use IlluminateNotificationsNotifiable;
use IlluminateDatabaseEloquentModel;
use IlluminateContractsAuthAuthenticatable;

class User extends Model implements Authenticatable {
    use IlluminateAuthAuthenticatable;

    use Notifiable;

    protected $fillable = [
        'username', 'email', 'password',
    ];

}

还有一个 UserController 类(UserController.php):

And also, a UserController class (UserController.php):

<?php

namespace blogHttpControllers;

use Auth;
use blogUser;
use IlluminateHttpRequest;

class UserController extends Controller {

    public function postRegister(Request $request) {
        $username = $request['username'];
        $email = $request['email'];
        $password = bcrypt($request['password']);

        $user = new User();
        $user->email = $email;
        $user->username = $username;
        $user->password = $password;

        $user->save();

        return redirect()->route('login');        
    }

    public function postLogin(Request $request) {

        $credentials = [
            'username' => $request['username'],
            'password' => $request['password'],
        ];

        if(Auth::attempt($credentials)) {
            return redirect()->route('dashboard');       
        }

        return 'Failure'; 
    }
}

?>

如您所见,我使用 bcrypt() 作为我的散列方法.

As you can see, I am using bcrypt() as my hashing method.

然而,这个问题是,它总是会导致失败.

However, this problem is, it will always result to a failure.

我检查了以下链接:

  • 为什么是 Auth::attempt总是返回 false

Laravel authattempt 返回 false

附言这些链接似乎很难理解,因为我没有使用 Input 类.

P.S. These links seem very hard to follow as I do not utilize the Input class.

推荐答案

问题在于您在注册后将用户重定向到 login 路由的方式.您错误地假设 $request 数据将伴随重定向.

The problem is with the way you're redirecting the user to login route after the registration. You're falsely assuming that the $request data will be accompanied with the redirect.

让我们假设这个场景:一个请求被分派给 postRegister 方法,带有 nameemailpassword领域.控制器创建用户并将其保存到数据库中.然后它将尚未通过身份验证的用户重定向到 login 路由.postLogin 方法被触发,但这次没有请求数据.结果,Auth::attempt($credentials) 失败,你会在屏幕上看到令人讨厌的 Failure.

Let's assume this scenario: A request gets dispatched to the postRegister method with name, email and password fields. The controller creates the user and saves it into the database. Then it redirects the user, who is not yet authenticated, to the login route. The postLogin method gets triggered, but this time with no request data. As a result, Auth::attempt($credentials) fails and you get that nasty Failure on screen.

如果在创建数组后立即添加 dd($credentials),您将看到它没有值:

If you add a dd($credentials) right after you create the array, you'll see that it has no values:

public function postLogin(Request $request)
{
    $credentials = [
        'username' => $request['username'],
        'password' => $request['password'],
    ];

    // Dump data
    dd($credentials);

    if (Auth::attempt($credentials)) {
        return redirect()->route('dashboard');
    }

    return 'Failure';
}

它会返回如下内容:

array:2 [
  "username" => null
  "password" => null
]

您不能使用自定义请求数据重定向(除非使用作为 URL 一部分的查询字符串),无论怎样.这不是 HTTP 的工作方式.除了请求数据,您甚至无法使用自定义标头进行重定向.

You cannot redirect with custom request data (unless with querystring which is part of the URL), not matter what. It's not how HTTP works. Request data aside, you can't even redirect with custom headers.

既然您知道问题的根源是什么,让我们看看有哪些选项可以解决它.

Now that you know what's the root of your problem, let's see what are the options to fix it.

如果你想保留这个结构,你需要将 postRegister() 的请求数据刷入会话(在请求之间是持久的),然后在 postLogin 中检索它() 方法使用 Session 门面、session() 助手或实际的 IlluminateSessionSessionManager 类.

In case you want to preserve this structure, you need to flash the request data of postRegister() into the session (which is persistent between requests) and then retrieve it in the postLogin() method using Session facade, session() helper or the actual IlluminateSessionSessionManager class.

我的意思是:
(我稍微修改了您的代码;删除了额外的变量,使其更简洁,等等.)

public function postRegister(Request $request)
{
    // Retrieve all request data including username, email & password.
    // I assume that the data IS validated.
    $input = $request->all();

    // Hash the password
    $input['password'] = bcrypt($input['password']);

    // Create the user
    User::create($input);

    // Redirect
    return redirect()
        // To the route named `login`
        ->route('login')

        // And flash the request data into the session,
        // if you flash the `$input` into the session, you'll
        // get a "Failure" message again. That's because the 
        // password in the $input array is already hashed and 
        // the attempt() method requires user's password, not 
        // the hashed copy of it. 
        //
        ->with($request->only('username', 'password'));
}

public function postLogin(Request $request)
{
    // Create the array using the values from the session
    $credentials = [
        'username' => session('username'),
        'password' => session('password'),
    ];

    // Attempt to login the user
    if (Auth::attempt($credentials)) {
        return redirect()->route('dashboard');
    }

    return 'Failure';
}

我强烈建议您不要使用这种方法.这样,应该负责登录用户的 postLogin() 方法的实现与会话数据耦合在一起,这是不好的.这样,您就无法独立于 postRegister 使用 postLogin.

I strongly recommend you against using this approach. This way the implementation of postLogin() method which is supposed to be responsible to login users gets coupled with session data which is not good. This way, you're not able to use postLogin independently from the postRegister.

这是一个稍微好一点的解决方案;如果您决定需要在注册后立即登录用户,为什么不这样做呢?

This is a slightly better solution; If you decided that you need to log in the user right after the registration, why not just doing that?

注意 Laravel 自己的身份验证控制器 noreferr="自动执行.

Note that Laravel's own authentication controller does it automatically.

顺便说一下,这就是我的意思:
(理想情况下,这应该分解为多个方法,就像 Laravel 自己的身份验证控制器一样.但这只是帮助您入门的示例.)

By the way, here's what I mean:
(Ideally this should be broken down into multiple methods, just like Laravel's own authentication controller. But it's just an example to get you started.)

public function postRegister(Request $request)
{
    $input = $request->all();

    $input['password'] = bcrypt($input['password']);

    User::create($input);

    // event(UserWasCreated::class);

    if (Auth::attempt($request->only('username', 'password'))) {
        return redirect()
            ->route('dashboard')
            ->with('Welcome! Your account has been successfully created!');
    }

    // Redirect
    return redirect()
        // To the previous page (probably the one generated by a `getRegister` method)
        ->back()
        // And with the input data (so that the form will get populated again)
        ->withInput();
}

但是,它仍然远非完美!有很多其他方法可以解决这个问题.一个可能是使用 events,抛出 异常 失败和使用自定义异常重定向.但我不打算探索它们,因为已经有为此完美设计的解决方案.

But still, it's far from perfect! There are many other ways to tackle this. One could be using events, throwing exceptions on failure and redirecting using custom exceptions. But I'm not gonna explore them as there's already a solution perfectly designed for this.

如果您想编写自己的身份验证控制器,那很好.一路上你会学到很多东西.但我强烈建议阅读 Laravel 自己的认证代码,尤其是 Users.php" rel="noreferrerRegistersUsersAuthenticatesUsers 特征以便从中学习.

If you want to write your own authentication controller, that's fine. You'll learn a lot along the way. But I strongly suggest reading Laravel's own authentication code, especially RegistersUsers and AuthenticatesUsers traits in order to learn from it.

还有一张纸条;你不需要 IlluminateAuthAuthenticatable 特性在你的 User 模型中,因为它已经扩展了 Authenticatable 使用该特征.

And one more note; you don't need that IlluminateAuthAuthenticatable trait in your User model as it's already extending Authenticatable which use that trait.

相关文章