如何使用 devise_token_auth 和 ActionCable 来验证用户?
How To use devise_token_auth with ActionCable for authenticating user?
我有一个 Rails 5 API 和 devise_token_auth gem 身份验证。
现在我想要经过身份验证的用户进行个人聊天。我没有资产,因为我正在使用 API 并且前端在本机应用程序中,我想要本机应用程序消息传递。
那么我如何通过 devise_token_auth gem
验证用户使用操作电缆进行个人消息传递
此处 auth_headers
设置在 cookie 中 ng-token-auth 用于确定请求是否通过身份验证,否则拒绝连接。
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
def find_verified_user # this checks whether a user is authenticated with devise_token_auth
# parse cookies for values necessary for authentication
auth_headers = JSON.parse(cookies['auth_headers'])
uid_name = DeviseTokenAuth.headers_names[:'uid']
access_token_name = DeviseTokenAuth.headers_names[:'access-token']
client_name = DeviseTokenAuth.headers_names[:'client']
uid = auth_headers[uid_name]
token = auth_headers[access_token_name]
client_id = auth_headers[client_name]
user = User.find_by_uid(uid)
if user && user.valid_token?(token, client_id)
user
else
reject_unauthorized_connection
end
end
end
end
Rails5 API 通常不支持任何 cookie。参见:http://guides.rubyonrails.org/api_app.html#creating-a-new-application .
如果您首先在您网站的某处执行一个常见的 HTTP-authentification(使用 devise_token_auth gem),那么您将获得 3 个身份验证 headers - access_token
, client
, uid
.
在这种情况下,您可以使用这 3 个身份验证 headers:
为您的 Websockets 连接使用基本身份验证(根据 https://devcenter.heroku.com/articles/websocket-security#authentication-authorization )
调用(我用Chrome Simple WebSocket Client):
ws://localhost:3000/cable/?access-token=ZigtvvcKK7B7rsF_20bGHg&client=TccPxBrirWOO9k5fK4l_NA&uid=client1@example.com
然后处理:
# Be sure to restart your server when you modify this file. Action Cable runs in an EventMachine loop that does not support auto reloading.
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
params = request.query_parameters()
access_token = params["access-token"]
uid = params["uid"]
client = params["client"]
self.current_user = find_verified_user access_token, uid, client
logger.add_tags 'ActionCable', current_user.email
end
protected
def find_verified_user token, uid, client_id # this checks whether a user is authenticated with devise
user = User.find_by email: uid
# http://www.rubydoc.info/gems/devise_token_auth/0.1.38/DeviseTokenAuth%2FConcerns%2FUser:valid_token%3F
if user && user.valid_token?(token, client_id)
user
else
reject_unauthorized_connection
end
end
end
end
这类似于常见的Websockets auth https://rubytutorial.io/actioncable-devise-authentication/
这样的身份验证可能就足够了。我相信 不需要 在频道订阅和发送到服务器的每条 Websocket 消息上额外进行身份验证:
http://guides.rubyonrails.org/action_cable_overview.html#server-side-components-connections
For every WebSocket accepted by the server, a connection object is instantiated. This object becomes the parent of all the channel subscriptions that are created from there on. The connection itself does not deal with any specific application logic beyond authentication and authorization.
因此,如果您的连接是 identified_by :current_user
,您以后可以在 FooChannel < ApplicationCable::Channel
中的任何地方访问 current_user
!示例:
class AppearanceChannel < ApplicationCable::Channel
def subscribed
stream_from "appearance_channel"
if current_user
ActionCable.server.broadcast "appearance_channel", { user: current_user.id, online: :on }
current_user.online = true
current_user.save!
end
end
def unsubscribed
if current_user
# Any cleanup needed when channel is unsubscribed
ActionCable.server.broadcast "appearance_channel", { user: current_user.id, online: :off }
current_user.online = false
current_user.save!
end
end
end
PS 我想在 Rails 5 API 中使用 cookie,你可以打开它:
http://guides.rubyonrails.org/api_app.html#other-middleware
config/application.rb
config.middleware.use ActionDispatch::Cookies
http://guides.rubyonrails.org/api_app.html#adding-other-modules
controllers/api/application_controller.rb
class Api::ApplicationController < ActionController::API
include ActionController::Cookies
...
我有一个 Rails 5 API 和 devise_token_auth gem 身份验证。
现在我想要经过身份验证的用户进行个人聊天。我没有资产,因为我正在使用 API 并且前端在本机应用程序中,我想要本机应用程序消息传递。
那么我如何通过 devise_token_auth gem
验证用户使用操作电缆进行个人消息传递此处 auth_headers
设置在 cookie 中 ng-token-auth 用于确定请求是否通过身份验证,否则拒绝连接。
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
def find_verified_user # this checks whether a user is authenticated with devise_token_auth
# parse cookies for values necessary for authentication
auth_headers = JSON.parse(cookies['auth_headers'])
uid_name = DeviseTokenAuth.headers_names[:'uid']
access_token_name = DeviseTokenAuth.headers_names[:'access-token']
client_name = DeviseTokenAuth.headers_names[:'client']
uid = auth_headers[uid_name]
token = auth_headers[access_token_name]
client_id = auth_headers[client_name]
user = User.find_by_uid(uid)
if user && user.valid_token?(token, client_id)
user
else
reject_unauthorized_connection
end
end
end
end
Rails5 API 通常不支持任何 cookie。参见:http://guides.rubyonrails.org/api_app.html#creating-a-new-application .
如果您首先在您网站的某处执行一个常见的 HTTP-authentification(使用 devise_token_auth gem),那么您将获得 3 个身份验证 headers - access_token
, client
, uid
.
在这种情况下,您可以使用这 3 个身份验证 headers:
为您的 Websockets 连接使用基本身份验证(根据 https://devcenter.heroku.com/articles/websocket-security#authentication-authorization )调用(我用Chrome Simple WebSocket Client):
ws://localhost:3000/cable/?access-token=ZigtvvcKK7B7rsF_20bGHg&client=TccPxBrirWOO9k5fK4l_NA&uid=client1@example.com
然后处理:
# Be sure to restart your server when you modify this file. Action Cable runs in an EventMachine loop that does not support auto reloading.
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
params = request.query_parameters()
access_token = params["access-token"]
uid = params["uid"]
client = params["client"]
self.current_user = find_verified_user access_token, uid, client
logger.add_tags 'ActionCable', current_user.email
end
protected
def find_verified_user token, uid, client_id # this checks whether a user is authenticated with devise
user = User.find_by email: uid
# http://www.rubydoc.info/gems/devise_token_auth/0.1.38/DeviseTokenAuth%2FConcerns%2FUser:valid_token%3F
if user && user.valid_token?(token, client_id)
user
else
reject_unauthorized_connection
end
end
end
end
这类似于常见的Websockets auth https://rubytutorial.io/actioncable-devise-authentication/
这样的身份验证可能就足够了。我相信 不需要 在频道订阅和发送到服务器的每条 Websocket 消息上额外进行身份验证:
http://guides.rubyonrails.org/action_cable_overview.html#server-side-components-connections
For every WebSocket accepted by the server, a connection object is instantiated. This object becomes the parent of all the channel subscriptions that are created from there on. The connection itself does not deal with any specific application logic beyond authentication and authorization.
因此,如果您的连接是 identified_by :current_user
,您以后可以在 FooChannel < ApplicationCable::Channel
中的任何地方访问 current_user
!示例:
class AppearanceChannel < ApplicationCable::Channel
def subscribed
stream_from "appearance_channel"
if current_user
ActionCable.server.broadcast "appearance_channel", { user: current_user.id, online: :on }
current_user.online = true
current_user.save!
end
end
def unsubscribed
if current_user
# Any cleanup needed when channel is unsubscribed
ActionCable.server.broadcast "appearance_channel", { user: current_user.id, online: :off }
current_user.online = false
current_user.save!
end
end
end
PS 我想在 Rails 5 API 中使用 cookie,你可以打开它:
http://guides.rubyonrails.org/api_app.html#other-middleware
config/application.rb
config.middleware.use ActionDispatch::Cookies
http://guides.rubyonrails.org/api_app.html#adding-other-modules
controllers/api/application_controller.rb
class Api::ApplicationController < ActionController::API
include ActionController::Cookies
...