如何在Ruby on Rails中使用Azure托管标识访问Azure PubSub?
在Ruby on Rails应用中用Azure托管标识替代Primary Key连接Azure PubSub Service
一、Azure端配置
- 给ROR应用所在的Azure资源(如App Service、虚拟机)启用托管标识:
- 系统分配标识:直接在资源的"标识"选项卡启用,自动创建关联的AD标识。
- 用户分配标识:先创建用户分配标识,再关联到目标资源。
- 为托管标识分配Azure PubSub权限:
- 进入Azure Web PubSub实例的"访问控制(IAM)"选项卡,添加角色分配。
- 推荐分配Web PubSub Service Contributor角色(适用于完整权限场景),或更细粒度的角色如Web PubSub Token Contributor(仅用于生成客户端令牌)。
二、ROR应用代码改造
1. 依赖准备
在Gemfile中添加HTTP请求依赖:
gem 'faraday'
执行bundle install安装依赖。
2. 实现Azure AD令牌获取方法
通过Azure实例元数据服务(IMDS)获取针对Web PubSub的AD令牌(仅在Azure环境中可用,本地开发可使用Azure CLI临时令牌):
def get_azure_ad_token resource = "https://webpubsub.azure.com/" # 若使用用户分配标识,需追加&client_id=你的用户分配标识ID imds_url = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=#{URI.encode_www_form_component(resource)}" response = Faraday.get(imds_url) do |req| req.headers["Metadata"] = "true" end raise "获取Azure AD令牌失败: #{response.status} - #{response.body}" unless response.success? JSON.parse(response.body)["access_token"] end
3. 替换原令牌生成逻辑,调用PubSub API生成客户端令牌
不再使用Primary Key签名JWT,而是用AD令牌认证后调用PubSub的REST API生成客户端访问令牌:
def get_auth_token(hub) pubsub_service_name = "<pubsub-service>" # 替换为你的PubSub服务名称 pubsub_endpoint = "https://#{pubsub_service_name}.webpubsub.azure.com" ad_token = get_azure_ad_token # 调用PubSub的生成令牌API api_url = "#{pubsub_endpoint}/api/hubs/#{hub}/:generateToken?api-version=2023-07-01-preview" # 可根据业务需求调整payload参数,如userId、roles、过期时间等 token_payload = { "userId" => current_user&.id, # 可选,绑定客户端用户ID "roles" => ["webpubsub.sendToGroup", "webpubsub.joinLeaveGroup"], # 可选,设置客户端权限 "expiresIn" => 3600 # 可选,令牌过期时间(秒) } response = Faraday.post(api_url) do |req| req.headers["Authorization"] = "Bearer #{ad_token}" req.headers["Content-Type"] = "application/json" req.body = token_payload.to_json end raise "生成PubSub客户端令牌失败: #{response.status} - #{response.body}" unless response.success? JSON.parse(response.body)["token"] end
4. 保持连接URL生成逻辑不变
原get_connection_url方法无需大幅修改,只需确保传入hub参数:
def get_connection_url(hub) pubsub_service_name = "<pubsub-service>" # 替换为你的PubSub服务名称 "wss://#{pubsub_service_name}.webpubsub.azure.com/client/hubs/#{hub}?access_token=#{get_auth_token(hub)}" end
三、优化建议
- 令牌缓存:Azure AD令牌默认有效期1小时,可通过
Rails.cache缓存令牌,避免频繁调用IMDS:def get_azure_ad_token cache_key = "azure_ad_token_webpubsub" cached_token = Rails.cache.read(cache_key) return cached_token if cached_token.present? # 原令牌获取逻辑... token = JSON.parse(response.body)["access_token"] expires_on = JSON.parse(response.body)["expires_on"].to_i expires_in = expires_on - Time.now.to_i Rails.cache.write(cache_key, token, expires_in: expires_in - 60) # 提前60秒过期,避免令牌失效 token end - 本地开发适配:本地开发时无法访问IMDS,可通过Azure CLI获取令牌并设置为环境变量:
修改# 执行Azure CLI命令获取令牌 az account get-access-token --resource https://webpubsub.azure.com/ --query accessToken -o tsv # 将令牌设置为环境变量,在代码中优先读取get_azure_ad_token方法添加本地 fallback:def get_azure_ad_token return ENV["AZURE_AD_TOKEN"] if Rails.env.development? && ENV["AZURE_AD_TOKEN"].present? # 原IMDS获取逻辑... end
内容的提问来源于stack exchange,提问作者Vansh
相关产品推荐
相关产品推荐

