当然可以,非常简单。
在你的视图中:
=link_to "Log in with Facebook", omniauth_authorize_path(:user, :facebook), :class => "popup", :"data-width" => 600, :"data-height" => 400
在你的 application.js 文件中:
function popupCenter(url, width, height, name) {
var left = (screen.width/2)-(width/2);
var top = (screen.height/2)-(height/2);
return window.open(url, name, "menubar=no,toolbar=no,status=no,width="+width+",height="+height+",toolbar=no,left="+left+",top="+top);
}
$("a.popup").click(function(e) {
popupCenter($(this).attr("href"), $(this).attr("data-width"), $(this).attr("data-height"), "authPopup");
e.stopPropagation(); return false;
});
然后在你的回调视图中:
:javascript
if(window.opener) {
window.opener.location.reload(true);
window.close()
}
这将在一个居中的600x400弹出窗口中显示您的Facebook授权界面,然后当用户从认证返回时,视图将关闭弹出窗口并刷新父页面。如果用户Ctrl +单击链接或未启用Javascript,则会优雅地降级。
window.opener.location.whatever
不起作用。您需要将该代码放在父窗口中,并将其作为函数调用,例如 window.opener.my_goto_function(myurl)
。 - Dex如果你正在使用OmniAuth和Devise,那么Chris Heald的解决方案存在问题。问题在于,当你重新加载窗口(即在登录页面上),Devise会将你带到root_path,完全忽略你尝试访问的URL,并显示错误消息“您已经登录”。这是有道理的,因为Devise通过重定向到主页来保护已登录用户免于访问登录页面。在登录后立即重新加载登录页面,就会出现这个问题。
因此,对于使用Devise的人,我的解决方案如下:
# add this wherever needed in your_authentications_or_callbacks_controller.rb
sign_in user
@after_sign_in_url = after_sign_in_path_for(user)
render 'callback', :layout => false
通常,使用某个提供程序(Facebook、Twitter等)返回的哈希值找到或创建用户后,我们会调用Devise函数sign_in_and_redirect
。但此时我们不能立即重定向(请记住,现在用户当前在弹出窗口中),因此我们只需简单地登录用户 sign_in
。
接下来,我们需要将用户试图访问的URL传递给视图,我们可以使用Devise的方法after_sign_in_path_for
来获取该URL。
最后,我们需要呈现这个视图。由于我们只会使用视图来调用一些JavaScript,所以没有必要渲染布局,我们关闭它以不拖慢速度。下面是那个视图:
# views/your_authentications_or_callbacks/callback.html.erb
<script type="text/javascript">
window.opener.location = '<%= @after_sign_in_url %>';
window.close();
</script>
使用这种方式,用户在登录后将被重定向到正确的URL,并显示正确的提示信息。
经过一些测试,我意识到这种解决方案无法在没有JavaScript的情况下进行身份验证,因此我想添加一个附注。
function popupCenter(linkUrl, width, height, name) {
var separator = (linkUrl.indexOf('?') !== -1) ? '&' : '?',
url = linkUrl + separator + 'popup=true',
left = (screen.width - width) / 2,
top = (screen.height - height) / 2,
windowFeatures = 'menubar=no,toolbar=no,status=no,width=' + width +
',height=' + height + ',left=' + left + ',top=' + top;
return window.open(url, name, windowFeatures);
}
这里的修改是通过JavaScript向URL添加一个简单的名为popup
的参数。 OmniAuth会很好地存储添加到请求URL中的任何查询参数。 最后我们在控制器中检查该参数是否存在。 如果存在,那么说明JavaScript已启用:
if request.env['omniauth.params']['popup']
render 'callback', :layout => false
else
redirect_to @after_sign_in_url
end
同时,别忘了对于failure
操作也做同样的处理,这个操作会在用户不接受登录时被调用。
如果没有Chris Heald的解决方案,我可能无法完成这个任务,所以...非常感谢!
link_to
。他在链接中添加了一些数据参数。如果你使用Rails的新哈希语法,它会看起来更好。像这样:data: {width: '640', height: '330'}
- Ashitaka分享一下以帮助他人。我正在使用Chris Heald的答案,但在最后一个javascript片段中遇到了问题,因为它会关闭任何新窗口链接。例如,如果我将我的网站链接发布到Facebook上,当用户单击链接时,由于条件仅检查“if(window.opener)”,在Chrome中新窗口将自动关闭。
最终我使用了全局变量(popupValue)来解决这个问题。可能有更优雅的解决方案,但是我想与其他人分享以防他们遇到相同的问题:
function popupCenter(url, width, height, name) {
var left = (screen.width/2)-(width/2);
var top = (screen.height/2)-(height/2);
popupValue = "on";
return window.open(url, name, "menubar=no,toolbar=no,status=no,width="+width+",height="+height+",toolbar=no,left="+left+",top="+top );
}
$(document).ready(function(){
$("a.popup").click(function(e) {
popupCenter($(this).attr("href"), $(this).attr("data-width"), $(this).attr("data-height"), "authPopup");
e.stopPropagation(); return false;
});
if(window.opener && window.opener.popupValue === 'on') {
delete window.opener.popupValue;
window.opener.location.reload(true);
window.close()
}
});
我最终使用了Facebook的JS SDK,因为它更容易操作。
# In facebook.js.coffee
jQuery ->
$('body').prepend('<div id="fb-root"></div>')
$.ajax
url: "#{window.location.protocol}//connect.facebook.net/en_US/all.js"
dataType: 'script'
cache: true
window.fbAsyncInit = ->
FB.init(appId: 'YOUR-APP-ID', cookie: true)
$('#sign_in').click (e) ->
e.preventDefault()
FB.login (response) ->
window.location = '/auth/facebook/callback' if response.authResponse
$('#sign_out').click (e) ->
FB.getLoginStatus (response) ->
FB.logout() if response.authResponse
true
那么在您的视图中:
<%= link_to "Sign in with Facebook", "/auth/facebook", id: "sign_in" %>
<%= link_to "Sign out", signout_path, id: "sign_out" %>
:display => "popup"
选项的目的 - 这个选项是告诉 Facebook 我们想要页面的“显示样式”适合弹出窗口的大小/格式。这个选项并不表示“将它作为浏览器窗口弹出” - 弹出操作由你来执行,就像 Chris Heald 展示的那样。尝试使用 Chris 的答案,并在带有和不带有:display => "popup"
选项的情况下查看登录页面,在每次更改选项之间重新启动服务器,你会发现不同之处。 - Zabba