bounty将在2天后过期。回答此问题可获得+300声望奖励。Artur Müller Romanov希望引起更多关注此问题。
我已经通过Pusher
设置了一个实时聊天的Vue3/Laravel
应用程序,它可以通过非私人通道chat
工作。在下一步中,我想使用私人通道,但奇怪的事情发生了。试图向/api/pusher/auth
发送请求的pusher.subscribe
函数似乎无法正确处理sanctum authorization
,导致:
POST http://localhost:8000/api/pusher/auth 401 (Unauthorized)
ajax @ pusher-js.js?v=1974b27b:676
(anonymous) @ pusher-js.js?v=1974b27b:3548
authorize @ pusher-js.js?v=1974b27b:1860
subscribe @ pusher-js.js?v=1974b27b:1828
subscribe @ pusher-js.js?v=1974b27b:3960
subscribeAll @ pusher-js.js?v=1974b27b:3951
(anonymous) @ pusher-js.js?v=1974b27b:3868
emit @ pusher-js.js?v=1974b27b:1230
updateState @ pusher-js.js?v=1974b27b:2341
connected @ pusher-js.js?v=1974b27b:2281
callback @ pusher-js.js?v=1974b27b:2176
cb @ pusher-js.js?v=1974b27b:2619
tryNextStrategy @ pusher-js.js?v=1974b27b:2459
(anonymous) @ pusher-js.js?v=1974b27b:2507
(anonymous) @ pusher-js.js?v=1974b27b:3399
finish @ pusher-js.js?v=1974b27b:1752
onMessage @ pusher-js.js?v=1974b27b:1729
emit @ pusher-js.js?v=1974b27b:1230
onMessage @ pusher-js.js?v=1974b27b:1327
socket.onmessage @ pusher-js.js?v=1974b27b:1343
Show 20 more frames
Show less
pusher-js.js?v=1974b27b:979 Pusher : : ["Error: Unable to retrieve auth string from channel-authorization endpoint - received status: 401 from http://localhost:8000/api/pusher/auth. Clients must be authorized to join private or presence channels. See: https://pusher.com/docs/channels/server_api/authorizing-users/"]
字符串
这个问题是pusher
路由特有的,所有其他API路由都可以正常工作。
前端设置
pusher.js:
import Pusher from 'pusher-js'
Pusher.logToConsole = true
const pusher = new Pusher('bf29be46d8eb2ea8ccd4', {
cluster: 'eu',
forceTLS: true,
authEndpoint: 'http://localhost:8000/api/pusher/auth',
withCredentials: true,
wsPort: 443,
wssPort: 443,
enableStats: false,
enabledTransports: ['ws', 'wss'],
auth: {
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
}
})
export default pusher
型
xiaos.js:
import axios from 'axios'
axios.defaults.withCredentials = true
if (import.meta.env.DEV) {
axios.defaults.baseURL = 'http://localhost:8000'
}
型
用户在登录组件中被认证:
const signIn = () => {
axios.get('/sanctum/csrf-cookie').then(() => {
axios
.post('/login', form)
.then(() => {
store.auth = sessionStorage.auth = 1
store.signInModal = false
})
.catch((er) => {
state.errors = er.response.data.errors
})
})
}
型
并且该应用尝试在聊天组件中订阅推送器,只有经过身份验证的用户才能访问:
const inquireChatSession = () => {
axios
.get(`/api/chat/${props.id}`)
.then((res) => {
state.chatSessionId = res.data.id
state.messages = res.data.messages
state.loadingSession = false
const channel = pusher.subscribe(`private-chat.${state.chatSessionId}`) // 401 happens here
channel.bind('App\\Events\\ChatMessageSent', (data) => {
state.messages.push(data.chatMessage)
})
})
.catch((er) => {
state.errors = er.response.data.errors
state.loadingSession = false
})
}
型
后台设置
/routes/API.php:
Route::post('/pusher/auth', function (Request $request) {
\Log::info('test');
$user = $request->user();
if (!$user) {
abort(403, 'Unauthorized');
}
$pusher = new Pusher(
env('PUSHER_APP_KEY'),
env('PUSHER_APP_SECRET'),
env('PUSHER_APP_ID'),
['cluster' => env('PUSHER_APP_CLUSTER')]
);
$channelName = $request->channel_name;
$socketId = $request->socket_id;
$auth = $pusher->socket_auth($channelName, $socketId);
return response()->json(['auth' => $auth]);
})->middleware('auth:sanctum');
型
pusher正在尝试连接到此路由,但授权失败,导致'test'
未被记录并将上述错误返回给客户端。这似乎是一个pusher + sanctum
问题,因为连接到此诊断路由可以正常工作:
Route::post('/pusher/auth', function (Request $request) {
\Log::info(var_export($request, true));
\Log::info('Request headers: ', $request->header());
\Log::info('Request cookies: ', $request->cookies->all());
\Log::info('Session data: ', $request->session()->all());
\Log::info('User: ', $request->user());
})
型
但是$request->cookies->all()
是空的,而$request->user()
是null
。由于某种原因,没有auth cookies
到达推送器路由。要检查sanctum
是否自己工作,连接到以下路由,返回授权用户:
Route::middleware('auth:sanctum')->get('/test-auth', function (Request $request) {
return $request->user();
});
型
相关的**.env**条目:
APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
SANCTUM_STATEFUL_DOMAINS=localhost:3000
SESSION_DOMAIN=localhost
PUSHER_APP_ID=1728518
PUSHER_APP_KEY=bf29be46d8eb2ea8ccd4
PUSHER_APP_SECRET=...
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=eu
型
就像我说的,应用程序的整个身份验证工作正常,除了推送器路由。
浏览器网络页签
验证请求#1:
概述:
Request URL: http://localhost:8000/api/pusher/auth
Request Method: OPTIONS
Status Code: 204 No Content
Remote Address: 127.0.0.1:8000
Referrer Policy: strict-origin-when-cross-origin
型
响应标题:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-requested-with
Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Max-Age: 0
Cache-Control: no-cache, private
Connection: close
Content-Type: text/html; charset=UTF-8
Date: Wed, 27 Dec 2023 02:25:13 GMT
Host: localhost:8000
Vary: Access-Control-Request-Method, Access-Control-Request-Headers
X-Powered-By: PHP/8.3.1
型
请求标题:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,de;q=0.8
Access-Control-Request-Headers: x-requested-with
Access-Control-Request-Method: POST
Cache-Control: no-cache
Connection: keep-alive
Host: localhost:8000
Origin: http://localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
型
然后是auth request #2:
概述:
Request URL: http://localhost:8000/api/pusher/auth
Request Method: POST
Status Code: 401 Unauthorized
Remote Address: 127.0.0.1:8000
Referrer Policy: strict-origin-when-cross-origin
型
响应标题:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:3000
Cache-Control: no-cache, private
Connection: close
Content-Type: application/json
Date: Wed, 27 Dec 2023 02:25:13 GMT
Host: localhost:8000
Set-Cookie: XSRF-TOKEN=eyJpdiI6Inl5T2ZLbndpZG1OUTV5MmxNdDlNNWc9PSIsInZhbHVlIjoiUzdJYVkzZzJvM3FnaUlIUGxVWFBDTTZYeHQveTBWOWoxSEsvcThGM00wVDh6WExmK2RYWVBldTNxK2xKS1RrV1JSTHA2b0NEMVFtQzlzSmxyVVVRbmlrSmNRdmJQaW00cWpIQVFyZkhYM0RwampuMDZWVzJsV3NUZjVJZ1kxaG0iLCJtYWMiOiI4MzFlZjBjYWZkNDZkZDBhMGYxZDgwMDQ5YTgzY2ExNDg1NDMyNjFlNTNmZDg5NGJmZTI4MDMxNzAzMjVlNjZjIiwidGFnIjoiIn0%3D; expires=Wed, 27 Dec 2023 04:25:13 GMT; Max-Age=7200; path=/; domain=localhost; samesite=lax
Set-Cookie: soul_meatcom_session=eyJpdiI6Ii8wTktnTVFMZUZBYXVTeTFTRjd4dmc9PSIsInZhbHVlIjoicDUzNjFJVEYyTVR5cXdrTGZQZWZ1NzF4UEZ6QUJXSWF3YUsya0lUZy9qb0IwNk0rM0cwa3RwV1YyZ1Q0T0JqWW90cjZKd2d3OXNqOW13aGswc2tPMGw0d0hPRkxDZDdqamFUQWpKSktVd2ZpS1c2b3NqQm5WMVhoK2VsLzJWeEkiLCJtYWMiOiI4YjM3ZWEwZWMwNzlhNDIxMWNhNjBhMjAzNzcxNDM0NGMxNTczOTU1YWQ0ZGFjNzEyYWJkNDI2ZWJiNjI2ZTZkIiwidGFnIjoiIn0%3D; expires=Wed, 27 Dec 2023 04:25:13 GMT; Max-Age=7200; path=/; domain=localhost; httponly; samesite=lax
X-Powered-By: PHP/8.3.1
型
请求标题:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,de;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 87
Content-Type: application/x-www-form-urlencoded
Host: localhost:8000
Origin: http://localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/
Sec-Ch-Ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
X-Requested-With: XMLHttpRequest
型
浏览器应用页签
/storage/cookies/http://localhost:3000中有两个cookie:
1 soul_meatcom_session:
eyJpdiI6ImI4dUNWL0pCRmRibFMzNUdyT0JrL3c9PSIsInZhbHVlIjoiQTN4L0djZm52bjlCSFV4TmM0QU1oNUFoT0JBUlFGcGNWMFVwSDEvTFZBWmZwVi9kSEFnUitHcit6MzRLNXVkNkxLU1o5a0VhWmJ2OTNvYUdxMkpyVDVUcVZoQWRzckVlVi84Tis3UTdxazhkR0ozU1EyeldnaFowcStTRFFJYjgiLCJtYWMiOiJkNjc3MGM1ODc2MWM1NWFiMDBlNjYzMTg0OWI3M2RiZmNmZGU5NzU4Y2QzZDA0NmViZDQzZjIzODBiMWZiYWM1IiwidGFnIjoiIn0%3D
型
2 XSRF-TOKEN:
eyJpdiI6Ik1xc0tCckEzS2RNNURFVWJ5aGc2Z0E9PSIsInZhbHVlIjoidVE4TElScENjRFlEbUFtVk1sVzZ1MGU5WDI2NXk2b214aEpWbU10K1hJUGZtQzdFOFBHV3JYblZiYmlFSmQvaSt2ZWQ5cWtjOXhtZXJQTmQ0NUNSVjAvQ2xmVDNwcUw0dkRFMHZnclRSc08wanVqaHdlbGFWeE5JMk1pTzRXOFgiLCJtYWMiOiJkNzQwODAwYzU2ZGE0OTVjNzQ0MjQxNzAwZDIxMGVkNGNkZTJjNWI2NjQ2YjMzZjk4NGM1YzI4MWJhOWZmMGI2IiwidGFnIjoiIn0%3D
型
我在阅读网络头方面不是很有经验。你知道pusher
路由的问题是什么吗?或者我如何进一步调试它?提前谢谢你
编辑:
我试过@suxgri的方法:
import Pusher from 'pusher-js'
Pusher.logToConsole = true
const pusher = new Pusher('bf29be46d8eb2ea8ccd4', {
cluster: 'eu',
forceTLS: true,
authEndpoint: 'http://localhost:8000/api/pusher/auth',
withCredentials: true,
wsPort: 443,
wssPort: 443,
enableStats: false,
enabledTransports: ['ws', 'wss'],
auth: {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Authorization':
'Bearer eyJpdiI6ImpCdC96YjVqNmh3VXhic0tkeXlYQ3c9PSIsInZhbHVlIjoiN0YzMzB3QkVySDdyeFdIK0JLaXM1cWVaTDZSd3FTUkNoWHdDSEFVaTQ3ZktYMi94ak5yQVpMRkNNNjBNbWswamM0emJGa2RVRktuNU4yUXBLMUxoMU90UGxyZk94bzdUSythMlFFNmNGdFlydjdhYVI4WmlIRXk4dEdKQU9kRnYiLCJtYWMiOiIwOWI3MDg4MGZmYTAzYWY3N2QzOGM5ZmQ4MjNkMjIyNDg5OGRjZTk5YjNjNTAwZjE5MWY2YjIxZTMyMGQ3NWU0IiwidGFnIjoiIn0=',
},
},
})
export default pusher
型
但我还是得到了401
。
2条答案
按热度按时间xzlaal3s1#
Laravel返回401错误代码,因为auth:sanctum中间件停止了请求,因为您没有发送auth参数(访问令牌),请参阅下面auth -> header中的新行。然后它应该工作或至少到达控制器并记录'test'
字符串
请注意,上述答案适用于移动的+API应用程序,但不适用于spa+API应用程序。
编辑:
来自Laravel文档(sanctum#spa authentiction)
在此请求期间,Laravel将设置一个包含当前CSRF令牌的XSRF-TOKEN cookie。然后,该令牌将在后续请求的X-XSRF-TOKEN头中传递,某些HTTP客户端库(如Axios和Angular HttpClient)将自动为您执行此操作。如果您的JavaScript HTTP库没有为您设置值,您需要手动设置X-XSRF-TOKEN标头以匹配此路由设置的XSRF-TOKEN cookie的值。
看起来axios没有在你发布的任何请求头中传递XSRF-TOKEN,所以我会修改如下:
型
接下来,我将检查http://localhost:8000/api/pusher/auth请求,以确保令牌通过,如果问题仍然存在,我将遵循以下步骤:
Laravel docs -> Sanctum ->授权私人广播频道
dvtswwa32#
Pusher JS代码的反射
如果使用身份验证来访问API路由,则对于涉及访问API路由的任何请求,都需要存在并传输适当的令牌。
字符串
Laravel Echo解决方案
我使用了文档推荐的Laravel Echo客户端包来建立广播通信。
Laravel文档还涵盖了通道侦听所需的身份验证方法。
将
laravel-echo
包作为开发依赖项安装,与pusher-js
包一起安装。型
在客户端所需的位置创建必要的
Echo
类,以建立连接。型
在此之后,通过调用全局
window.Echo
,您可以连接到通道。型
我不知道你的意图,但我必须指出,一旦执行
then()
函数,在代码中监听channel
将立即停止。因此,建议定义一个外部channel
变量,并在执行then()
分支时将监听通道写入其中。型
额外
在Laravel中创建channel
建议根据文档定义事件。之后,您可以在应用程序中的任何位置在定义的通道上创建事件。
event()
辅助程序的事件- Laravelapp/Events/ChatMessageSent.php
型
ChatController.php
型