Laravel API 端点在服务器上“401 未经授权”,但在本地主机上运行良好

Gor*_*fex 1 apache authentication laravel laravel-passport raspberry-pi4

背景

我开发了一个使用 Laravel 作为 API 的 React 应用程序。我已经通过 Passport 添加了登录信息,并且非常成功地使用了个人访问客户端方法。我可以添加新用户和令牌,我可以撤销令牌,我可以重置密码...所有 API 调用(登录和注册除外)都由 API 中间件保护并且可以正常工作。如果我从任何这些调用的标头中删除 ,Bearer ${token}由于 ->middleware('auth:api') 包装器,它会返回 401 unauthenticated。

问题

一切都完全按预期进行......直到我将所有内容移至我的 Raspberry Pi 服务器。当我移动所有东西时,问题就开始了。我可以登录并且可以注册,但是一旦我在流程中后续的任何端点调用上使用新的不记名令牌(我从登录或注册调用中收到的),它就会失败并显示 401 未经身份验证,立即地。我运行了php artisan passport:client --personal命令并像往常一样成功地将 id 和密钥输入到我的 .env 文件中。我安装了所有的作曲家和供应商包。我安装了所有的 Passport 包和 CLI 命令。

它仅在使用 auth 中间件的调用上失败

我做了一些挖掘,似乎我能发现的唯一(显着的)变化是 Pi 运行 32 位 PHP,而我的本地主机运行 64 位 PHP。除此之外,它的代码、数据库、Laravel 和 PHP 版本都相同。

我尝试使用该命令php artisan passport:client --personal --name="app-name" --redirect_uri="http://192.168.1.1/"将记录放入“oauth_clients”表中,但将重定向显示为http://localhost/. 然后,我尝试使用 SQL 手动将名为“redirect”的列的值更改为http://localhost/......但更改又没有任何作用。调用仍会返回 401 未经身份验证。

我能发现的唯一可能存在问题的其他事情是:

  1. 事实上,数据库表“oauth_access_tokens”中“redirect”列下的所有令牌都是使用 of 创建redirect_urihttp://localhost。无论我做什么,它始终是 localhost,而不是我的服务器域或 IP 地址(这是有关的)。正如我所说,手动更改 SQL 没有任何作用,但我知道 Laravel 使用一些“只读”列进行身份验证,所以我想知道这是否是其中之一......也许个人访问令牌只能在本地主机上工作?
  2. 我的email_verified_at“用户”表中的列(由护照命令生成)为空,因为我无法在本地主机上设置 Passport 的“忘记密码”流程,因为电子邮件不会在本地主机上发送。

我的设置是这样的:

public function boot()
{
    $this->registerPolicies();

    Passport::pruneRevokedTokens();
    Passport::tokensExpireIn(Carbon::now()->addDays(1));
    Passport::refreshTokensExpireIn(Carbon::now()->addDays(14));
    Passport::personalAccessTokensExpireIn(Carbon::now()->addDays(1));
}
Run Code Online (Sandbox Code Playgroud)

AuthServiceProvider类

public function register(Request $request) {
    $validatedData = $request->validate([
        'image_url' => 'required',
        'last_name' => 'required|max:55',
        'image_url' => 'required|max:250',
        'first_name' => 'required|max:55',
        'password' => 'required|confirmed',
        'email' => 'email|required|unique:users',
    ]);

    $validatedData['password'] = bcrypt($request->password);


    if ($request->hasFile('image_url')) {
        $imageFile = $request->file('image_url');
        $imageExtension = $imageFile->extension();

        if (strtolower($imageExtension) === 'png' || strtolower($imageExtension) === 'jpg') {
            $validatedData['image_url'] = Storage::url( $request->file('image_url')->store('user_pics', 'public') );
        }
    
        $user = User::create($validatedData);
        
        date_default_timezone_set('UTC');
        $date = new \DateTime( date('Y-m-d H:i:s') );
        $user->email_verified_at = $date->format('c');

        $accessToken = $user->createToken('authToken-'.$user->id, ['*'])->accessToken;

        return response([ 'user' => $user, 'access_token' => $accessToken ]);

    } else {
        abort(404, 'Cannot register user without a user image!');
    }
}


public function login(Request $request) {
    $loginData = $request->validate([
        'email' => 'email|required',
        'password' => 'required'
    ]);

    if (!auth()->attempt($loginData)) {
        return response()->json(['statusText' => 'Unauthorized'], 401);
    }

    $user = auth()->user();
    $accessToken = auth()->user()->createToken('authToken-'.$user->id, ['*'])->accessToken;

    return response([ 'user' => $user, 'access_token' => $accessToken ]);
}


public function logout(Request $request) {
    if (auth()->guard('api')->check()) {
        auth()->guard('api')->user()->OauthAcessToken()->delete();

        return response()->json([ 'msg' => 'Successfully logged out!' ]);
    
    } else {
        return abort(404, 'Must be logged in to log a user out');
    }
}


public function refreshToken(Request $request) {
    if (auth()->guard('api')->check()) {
        $user = auth()->user();
        $accessToken = auth()->user()->createToken('authToken-'.$user->id, ['*'])->accessToken;

        return response([ 'user' => $user, 'access_token' => $accessToken ]);
    
    } else {
        return abort(404, 'Must be logged in to refresh a token!');
    }
}
Run Code Online (Sandbox Code Playgroud)

AuthController类

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users'
    ],
],
Run Code Online (Sandbox Code Playgroud)

配置/Auth.php

APP_NAME=MyName
APP_ENV=dev
APP_DEBUG=true
APP_URL=http://192.168.1.1
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="1"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="[SOME LONG HASH]"
Run Code Online (Sandbox Code Playgroud)

.env 文件

Gor*_*fex 9

终于解决了!!

原来是阿帕奇在Raspberry Pi服务器上的 Apache 阻止了授权标头。这终于解除了我的封锁并解决了我的问题。

对于来自 Google 搜索的其他人,您可以进入您的/etc/apache2/apache2.conf文件并在最底部粘贴:

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

我使用的是带有 32 位 PHP 和 Apache2 的 Raspberry Pi 4。

另外,我在帖子中没有提到我一直在为我的 apache 服务器 root htaccess 使用以下内容:

# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Run Code Online (Sandbox Code Playgroud)

.htaccess 文件,服务器根目录