如何在Ruby on Rails单页应用中配置CSRF令牌实现Google登录
解决Rails + Devise Omniauth谷歌登录的CSRF令牌验证失败问题
方法一:自定义谷歌登录回调,手动携带CSRF令牌(推荐)
谷歌GSI默认通过data-login_uri发起的跨域POST请求无法自动携带Rails的CSRF令牌,导致验证失败。通过自定义回调函数,我们可以手动获取并携带令牌发起请求:
- 修改React登录组件
移除data-login_uri,添加回调函数处理登录请求:
import React, { useEffect } from 'react'; const SignInWithGoogle = () => { useEffect(() => { // 挂载回调函数到window,供谷歌GSI调用 window.handleCredentialResponse = (response) => { // 从页面meta标签获取Rails生成的CSRF令牌 const csrfToken = document.querySelector('meta[name="csrf-token"]').content; // 发起带CSRF令牌的POST请求到Omniauth回调地址 fetch('/users/auth/google_oauth2/callback', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRF-Token': csrfToken }, body: new URLSearchParams({ credential: response.credential, client_id: response.clientId }) }).then(res => { // 根据响应处理后续逻辑,比如跳转用户面板 if (res.ok) { window.location.href = '/dashboard'; } }).catch(err => { console.error('谷歌登录失败:', err); }); }; }, []); return ( <div className='sign-in-with-google'> <div id="g_id_onload" data-client_id="xxxxx.apps.googleusercontent.com" data-context="signup" data-ux_mode="popup" data-callback="handleCredentialResponse" data-auto_prompt="false"> </div> <div className="g_id_signin" data-type="standard" data-shape="rectangular" data-theme="outline" data-text="signin_with" data-size="large" data-logo_alignment="left"> </div> </div> ); }; export default SignInWithGoogle;
- 确保页面包含CSRF meta标签
检查application.html.erb的<head>区域,确认存在Rails自动生成的CSRF标签(默认已添加,若缺失则手动加入):
<head> <%= csrf_meta_tags %> <!-- 其他头部内容 --> <script src="https://accounts.google.com/gsi/client" async defer></script> </head>
方法二:跳过Omniauth回调的CSRF验证(不推荐)
此方法会降低应用安全性,仅建议测试环境临时使用:
修改Omniauth回调控制器,跳过谷歌登录路由的CSRF验证:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController # 仅跳过谷歌回调的CSRF验证 skip_before_action :verify_authenticity_token, only: [:google_oauth2] def google_oauth2 # 你的谷歌登录回调逻辑,例如创建/查找用户 @user = User.from_omniauth(request.env["omniauth.auth"]) if @user.persisted? sign_in_and_redirect @user, event: :authentication set_flash_message(:notice, :success, kind: "Google") if is_navigational_format? else session["devise.google_data"] = request.env["omniauth.auth"].except(:extra) redirect_to new_user_registration_url end end end
内容的提问来源于stack exchange,提问作者StaticMethod
相关产品推荐
相关产品推荐

