无法在 Dusk 中使用 sanctum 进行身份验证
Cannot be authenticated in Dusk using sanctum
我正在尝试使用 Laravel sanctum 和 React 制作登录页面。
我可以在我的浏览器中登录(可以看到 'authenticated' 消息),但我无法在 Dusk 中登录。
确切地说,我可以在 Dusk 中登录一次,但在下次访问时我未通过身份验证。
我不明白为什么会这样。
如何使用 sanctum 通过 Dusk 进行身份验证?
环境
- Laravel 7.x
- laravel/sanctum^2.6
- 响应 16.13.1
Login.js
import React, { useState } from 'react';
const axios = window.axios;
export default function Login(props) {
const [strId, setStrId] = useState('');
const [password, setPassword] = useState('');
const [hoge, setHoge] = useState('ini state');
function normalUserLogin() {
axios
.get('/sanctum/csrf-cookie')
.then((response) => {
axios
.post('/api/login', {
strId: strId,
password: password,
})
.then((response) => {
setHoge(response.data);
authCheck();
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
alert('Error happened.');
});
}
function authCheck() {
axios
.get('/api/user/auth')
.then((response) => {
setHoge(response.data);
})
.catch((error) => {
console.log(error);
alert('error happened getting auth information');
});
}
return (
<>
<div className="row mt-5">
<div className="col-12">
<form>
<label htmlFor="user-id" className="d-block">
User ID
<input
name="user-id"
id="user-id"
className="d-block"
value={strId}
onChange={(e) => setStrId(e.target.value)}
/>
</label>
<label htmlFor="password" className="d-block">
Password
<input
type="password"
name="password"
id="password"
autoComplete="off"
className="d-block"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
</form>
<button
type="button"
id="normal-login"
className="btn btn-outline-success d-block"
onClick={normalUserLogin}
>
Login
</button>
</div>
</div>
<div className="col-12">
{hoge}
</div>
</>
);
}
MyCustomLoginController (app/Http/Api/LoginController.php)
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Sanctum\Sanctum;
use App\User;
class LoginController extends Controller
{
// routing is '/api/login'
public function login(Request $request)
{
$str_id = $request->input('strId');
$password = $request->input('password');
$credentials = compact('str_id', 'password');
if (Auth::attempt($credentials)) {
$hoge = Auth::check() ? 'OK' : 'NG';
// $hoge is OK in dusk.
return response($hoge);
} else {
return response('Cannot Authenticated', 401);
}
}
}
授权检查操作
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\User;
class UserController extends Controller
{
// routing is '/api/user/auth'
public function auth(Request $request)
{
$user = Auth::user();
if ($user) {
$params = User::getParamsForApp($user->str_id);
// in local environment, I can see 'Authenticated.'
return response('AUTHENTICATED');
} else {
// in dusk, i can see 'Not Authenticated.'
return response('Not Authenticated.');
}
}
}
Dusk
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use App\User;
class LoginTest extends DuskTestCase
{
use DatabaseMigrations;
public function testLogin()
{
$this->browse(function (Browser $browser) {
$user = Factory(User::class)->create();
$str_id = $user->str_id;
$password = config('app.guest_password');
$browser->visit('/login')
->waitFor('#user-id')
->type('user-id', $str_id)
->type('password', $password)
->press('Login')
->waitFor('#message');
});
}
}
设置文件
.env
web(APP_URL)是nginx的容器名
APP_URL=http://web
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:8000
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=****_test
DB_USERNAME=****_test
DB_PASSWORD=*********
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
phpunit.dusk.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Browser Test Suite">
<directory suffix="Test.php">./tests/Browser</directory>
</testsuite>
</testsuites>
<php>
<server name="SESSION_DRIVER" value="file"/>
</php>
</phpunit>
DuskTestCase.php
<?php
namespace Tests;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Laravel\Dusk\TestCase as BaseTestCase;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
public static function prepare()
{
// static::startChromeDriver();
}
protected function driver()
{
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
'--no-sandbox',
'--enable-file-cookies',
]);
// I use Docker, container name is chrome.
return RemoteWebDriver::create(
'http://chrome:4444',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
)
);
}
}
尝试将会话驱动程序设置为文件
我的应用程序的根目录和 php 部分
中有文件 phpunit.dusk.xml
<server name="SESSION_DRIVER" value="file"/>
在 DuskTestCase 中尝试 class 在方法 driver()
中设置选项 '--enable-file-cookies'
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
'--ignore-certificate-errors',
'--no-sandbox',
'--enable-file-cookies',
]);
我正在尝试使用 Laravel sanctum 和 React 制作登录页面。
我可以在我的浏览器中登录(可以看到 'authenticated' 消息),但我无法在 Dusk 中登录。
确切地说,我可以在 Dusk 中登录一次,但在下次访问时我未通过身份验证。
我不明白为什么会这样。
如何使用 sanctum 通过 Dusk 进行身份验证?
环境
- Laravel 7.x
- laravel/sanctum^2.6
- 响应 16.13.1
Login.js
import React, { useState } from 'react';
const axios = window.axios;
export default function Login(props) {
const [strId, setStrId] = useState('');
const [password, setPassword] = useState('');
const [hoge, setHoge] = useState('ini state');
function normalUserLogin() {
axios
.get('/sanctum/csrf-cookie')
.then((response) => {
axios
.post('/api/login', {
strId: strId,
password: password,
})
.then((response) => {
setHoge(response.data);
authCheck();
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
alert('Error happened.');
});
}
function authCheck() {
axios
.get('/api/user/auth')
.then((response) => {
setHoge(response.data);
})
.catch((error) => {
console.log(error);
alert('error happened getting auth information');
});
}
return (
<>
<div className="row mt-5">
<div className="col-12">
<form>
<label htmlFor="user-id" className="d-block">
User ID
<input
name="user-id"
id="user-id"
className="d-block"
value={strId}
onChange={(e) => setStrId(e.target.value)}
/>
</label>
<label htmlFor="password" className="d-block">
Password
<input
type="password"
name="password"
id="password"
autoComplete="off"
className="d-block"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
</form>
<button
type="button"
id="normal-login"
className="btn btn-outline-success d-block"
onClick={normalUserLogin}
>
Login
</button>
</div>
</div>
<div className="col-12">
{hoge}
</div>
</>
);
}
MyCustomLoginController (app/Http/Api/LoginController.php)
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Sanctum\Sanctum;
use App\User;
class LoginController extends Controller
{
// routing is '/api/login'
public function login(Request $request)
{
$str_id = $request->input('strId');
$password = $request->input('password');
$credentials = compact('str_id', 'password');
if (Auth::attempt($credentials)) {
$hoge = Auth::check() ? 'OK' : 'NG';
// $hoge is OK in dusk.
return response($hoge);
} else {
return response('Cannot Authenticated', 401);
}
}
}
授权检查操作
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\User;
class UserController extends Controller
{
// routing is '/api/user/auth'
public function auth(Request $request)
{
$user = Auth::user();
if ($user) {
$params = User::getParamsForApp($user->str_id);
// in local environment, I can see 'Authenticated.'
return response('AUTHENTICATED');
} else {
// in dusk, i can see 'Not Authenticated.'
return response('Not Authenticated.');
}
}
}
Dusk
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use App\User;
class LoginTest extends DuskTestCase
{
use DatabaseMigrations;
public function testLogin()
{
$this->browse(function (Browser $browser) {
$user = Factory(User::class)->create();
$str_id = $user->str_id;
$password = config('app.guest_password');
$browser->visit('/login')
->waitFor('#user-id')
->type('user-id', $str_id)
->type('password', $password)
->press('Login')
->waitFor('#message');
});
}
}
设置文件
.env
web(APP_URL)是nginx的容器名
APP_URL=http://web
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:8000
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=****_test
DB_USERNAME=****_test
DB_PASSWORD=*********
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
phpunit.dusk.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Browser Test Suite">
<directory suffix="Test.php">./tests/Browser</directory>
</testsuite>
</testsuites>
<php>
<server name="SESSION_DRIVER" value="file"/>
</php>
</phpunit>
DuskTestCase.php
<?php
namespace Tests;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Laravel\Dusk\TestCase as BaseTestCase;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
public static function prepare()
{
// static::startChromeDriver();
}
protected function driver()
{
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
'--no-sandbox',
'--enable-file-cookies',
]);
// I use Docker, container name is chrome.
return RemoteWebDriver::create(
'http://chrome:4444',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
)
);
}
}
尝试将会话驱动程序设置为文件
我的应用程序的根目录和 php 部分
中有文件 phpunit.dusk.xml<server name="SESSION_DRIVER" value="file"/>
在 DuskTestCase 中尝试 class 在方法 driver()
中设置选项 '--enable-file-cookies'
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
'--ignore-certificate-errors',
'--no-sandbox',
'--enable-file-cookies',
]);