我正在使用ActionCable在Rails API / React项目中设置聊天功能,并试图找到最佳解决方案以进行连接身份验证。
该项目使用devise-jwt gem,因此我的授权是作为标头传递的。由于Web Sockets不允许头部,因此我一直在寻找最佳替代方案。我找到的解决方案建议采用以下方法之一(具有各种注意事项):
- 将JWT作为查询参数发送到url中
- 劫持HTTP_SEC_WEBSOCKET_PROTOCOL并通过这种方式发送
这两种解决方案都不太好,但我尝试使用第二个解决方案,因为在url中发送敏感信息(即使是编码后的JWT)感觉不太妥当。
这是我的connection.rb代码:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
begin
jwt = request.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
decoded_jwt = JWT.decode(jwt, Rails.application.credentials.devise_jwt_secret_key)[0]
id = decoded_jwt['sub']
case decoded_jwt['scp']
when 'admin'
Admin.where(id: id).first
when 'hero'
Hero.where(id: id).first
when'villain'
Villain.where(id: id).first
else
reject_unauthorized_connection
end
rescue
reject_unauthorized_connection
end
end
end
end
我有三个不同的用户模型:管理员,(我们称之为)英雄和反派,因为某些原因。
现在,我的实际问题是:在服务器上,我可以获取JWT,解码它并找到我的管理员,但是在chrome控制台中,当我尝试创建套接字时,会出现以下错误:
WebSocket connection to 'ws://localhost:3000/cable' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
这是对在chrome控制台中运行以下代码的响应:token = "jwt.etc.blah..."
socket = new WebSocket(url, [token]);
服务器本身似乎没有问题:
Started GET "/cable" for ::1 at 2020-07-22 18:26:02 +0100
Started GET "/cable/" [WebSocket] for ::1 at 2020-07-22 18:26:02 +0100
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for ::1 at 2020-07-22 18:26:02 +0100
Admin Load (0.2ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" = $1 ORDER BY "admins"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/channels/application_cable/connection.rb:19:in `find_verified_user'
Registered connection (Z2lkOi8vYXBpNTUtbWFpcy9BZG1pbi8x)
错误提示似乎在告诉我,我需要一些有关额外协议的确认或信息,这些协议是我已经将JWT hackily混入其中的...但我不知道如何去做。
任何帮助都将不胜感激!使用rails ActionCable是否可以发送附加协议?还是我必须使用查询参数?还是有更简洁的方法来解决这个问题?
这是我第一次尝试使用Web Sockets,所以提前感谢您的帮助!