PHP-与所有主要提供商实施单点登录的最佳方法?

我已经对该主题进行了大量研究,并且自己实施了很多解决方案。

包括OpenID,Facebook Connect(使用旧的Rest API和新的Graph OAuth 2.0 API),使用twitter登录(据我所知,目前已升级为完全合格的OpenID),等等。

但是我仍然缺少的是完美的一站式解决方案。

在研究期间,我偶然发现了一些有趣的项目:

  • Janrain(以前称为RPX)-商业解决方案
  • Gigya-使用javascript和rest api的免费但外部托管的解决方案
  • AnyOpenID-客户免费解决方案,网站商业化

但是我不想依赖外部提供商,我也想要一个免费的解决方案,因此我在实施方面不受限制。

我还看到开发人员按照提供者的指示忠实地执行一项服务,并为所有内容设置模型和数据库表。

当然这可以工作,但是工作量很大,并且始终需要在应用程序中进行开发和更改等。

我正在寻找的是一个抽象层,它将所有服务都带到一个可以集成到我的网站中的标准。 出现新服务后,我只想添加一个处理该特定提供程序抽象的模型,以便将其无缝集成到我的应用程序中。

或者更好的是,找到一个我可以下载的现有解决方案。

理想情况下,此抽象服务应独立于我的应用程序托管,因此可以用于多个应用程序并独立升级。

上面的3个解决方案中的最后一个看起来很有希望。一切都只是移植到合成的OpenID,并且网站必须实现OpenID。

一段时间后,我找到了Django socialauth,这是Django Webframework的基于python的身份验证系统。 但是看起来它如上所述运行,我认为这是Stackoverflow使用的同一登录系统(或至少某些经过修改的fork ...)。

我下载了它并尝试进行设置,以查看是否可以将其设置为独立解决方案,但是我没有运气,因为我也不喜欢python。

我会喜欢基于PHP的解决方案。

因此,在这段长篇文章之后,我的问题恰恰是:

  • 除了移植所有内容并以OpenID为基础之外,您将如何实现SSO?
  • 优点和缺点是什么?
  • 您知道任何现有的解决方案吗? 最好是开源的。

我希望这个问题不是太主观,谢谢。

更新:我得出的结论是,构建代理/包装器或您可能为Facebook所称的名称,以将其移植到OpenID以便使其成为OpenID端点/提供程序将是最佳选择。所以这正是我所做的。

请在下面查看我的答案。

我添加了赏金以获取反馈/讨论。 Maby我的方法不如我目前认为的好!

3个解决方案
13 votes

作为此答案的原始作者,我想指出的是,我认为 过时的。 由于大多数提供商决定专门实现Oauth而不是Openid。 较新的Openid服务也可能会使用 openid connect,它基于oauth。 有很好的库,例如:[https://github.com/hybridauth/hybridauth]

在讨论已经存在的答案之后,我总结一下:

几乎每个主要提供商都是openid提供商/端点,包括Google,Yahoo,Aol。

其中一些要求用户指定用户名以构造openid端点。其中一些(上面提到的)确实具有发现URL,在该URL中会自动返回用户ID,因此用户只需单击即可。 (如果有人可以解释技术背景,我会很高兴)

但是,唯一的麻烦是Facebook,因为他们的Facebook连接在他们使用经过修改的OAuth版本进行身份验证的地方。

现在,我为我的项目所做的是建立一个openid提供程序,该提供程序使用我的facebook应用程序的凭据对用户进行身份验证-这样用户就可以连接到我的应用程序-并返回如下用户ID:

http://my-facebook-openid-proxy-subdomain.mydomain.com/?id=facebook-user-id

我还配置了它以获取电子邮件地址和名称并将其作为AX属性返回。

所以我的网站只需要实现开放ID,就可以了:)

我以您可以在此处找到的类为基础:[http://gitorious.org/lightopenid]

在我的index.php文件中,我只是这样称呼它:

<?php
require 'LightOpenIDProvider.php';
require 'FacebookProvider.php';
$op = new FacebookProvider;
$op->appid = 148906418456860; // your facebook app id
$op->secret = 'mysecret'; // your facebook app secret
$op->baseurl = 'http://fbopenid.2xfun.com'; // needs to be allowed by facebook
$op->server();
?>

并且FacebookProvider.php的源代码如下:

<?php
class FacebookProvider extends LightOpenIDProvider
{
    public $appid = "";
    public $appsecret = "";
    public $baseurl = "";

    // i have really no idea what this is for. just copied it from the example.
    public $select_id = true;

    function __construct() {

        $this->baseurl = rtrim($this->baseurl,'/'); // no trailing slash as it will be concatenated with
                                                    // request uri wich has leading slash

        parent::__construct();

        # If we use select_id, we must disable it for identity pages,
        # so that an RP can discover it and get proper data (i.e. without select_id)
        if(isset($_GET['id'])) {
            // i have really no idea what happens here. works with or without! just copied it from the example.
            $this->select_id = false;
        }
    }

    function setup($identity, $realm, $assoc_handle, $attributes)
    {
        // here we should check the requested attributes and adjust the scope param accordingly
        // for now i just hardcoded email
        $attributes = base64_encode(serialize($attributes));    

        $url = "https://graph.facebook.com/oauth/authorize?client_id=".$this->appid."&redirect_uri=";

        $redirecturl = urlencode($this->baseurl.$_SERVER['REQUEST_URI'].'&attributes='.$attributes);
        $url .= $redirecturl;
        $url .= "&display=popup";
        $url .= "&scope=email";
        header("Location: $url");
        exit();        

    }

    function checkid($realm, &$attributes)
    {
        // try authenticating
        $code = isset($_GET["code"]) ? $_GET["code"] : false;
        if(!$code) {
            // user has not authenticated yet, lets return false so setup redirects him to facebook
            return false;
        }

        // we have the code parameter set so it looks like the user authenticated
        $url = "https://graph.facebook.com/oauth/access_token?client_id=148906418456860&redirect_uri=";

        $redirecturl = ($this->baseurl.$_SERVER['REQUEST_URI']);
        $redirecturl = strstr($redirecturl, '&code', true);
        $redirecturl = urlencode($redirecturl);     
        $url .= $redirecturl;
        $url .= "&client_secret=".$this->secret;
        $url .= "&code=".$code;
        $data = $this->get_data($url);

        parse_str($data,$data);

        $token = $data['access_token'];

        $data = $this->get_data('https://graph.facebook.com/me?access_token='.urlencode($token));
        $data = json_decode($data);

        $id = $data->id;
        $email = $data->email;
        $attribute_map = array(
            'namePerson/friendly' => 'name', // we should parse the facebook link to get the nickname
            'contact/email' => 'email',
        );

        if($id > 0) {

            $requested_attributes = unserialize(base64_decode($_GET["attributes"]));

            // lets be nice and return everything we can
            $requested_attributes = array_merge($requested_attributes['required'],$requested_attributes['optional']);
            $attributes = array();
            foreach($requested_attributes as $requsted_attribute) {
                if(!isset($data->{$attribute_map[$requsted_attribute]})) {
                    continue; // unknown attribute
                }
                $attributes[$requsted_attribute] = $data->{$attribute_map[$requsted_attribute]};    
            }

            // yeah authenticated!
            return $this->serverLocation . '?id=' . $id ;
        }
        die('login failed'); // die so we dont retry bouncing back to facebook
        return false;
    }
    function get_data($url) { 
      $ch = curl_init();
      $timeout = 5;
      curl_setopt($ch,CURLOPT_URL,$url);
      curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
      curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);
      $data = curl_exec($ch);
      curl_close($ch);
      return $data;
    }    

}

它只是第一个工作版本(快速又脏)一些动态的东西被硬编码为我的需求。它应该表明如何以及可以做到这一点。如果有人捡起并改进它或重写它,或者其他任何东西,我感到很高兴:)

好吧,我认为这个问题已经回答

但我只是为了讨论而添加了一笔赏金。 我想知道您如何看待我的解决方案。

除此以外,我将奖励最佳答案/评论。

The Surrican answered 2020-08-12T00:01:42Z
5 votes

OpenID将是此应用程序的最佳选择。 许多提供商都支持它:

  • 谷歌
  • 雅虎
  • MyOpenID
  • 美国在线

唯一的问题是,twitter尚未实现OpenID。 这可能是由于他们是一家所有权公司,所以他们想要自己的“解决方案”。

为了解决该解决方案,您可以编写包装器类以提供与OpenID的兼容性,但是,即使您的用户没有Twitter帐户,他们也可能拥有Facebook,Google或Yahoo帐户。

Facebook支持oauth,因此您必须将oauth移植到OpenID

可以在此处找到一些OpenID的PHP库。

现在,有人提出了有关Facebook成为oauth提供者的问题。

他们的oauth URL是“ [https://graph.facebook.com/oauth/authorize”]

如果您仍然不相信我,那么您可以看一下这个javascript文件,其中包含该URL。 如果您不相信该javascript文件,请注意该文件是由该站点的提供商stackexchange托管的。 现在,您必须相信这一点。

xaav answered 2020-08-12T00:02:51Z
2 votes

快进了两年,“ OpenID就是答案”的答案似乎已被许多大型提供商淘汰。 大多数主要的第三方集成网站似乎都已迁移到某种OAuth(通常是OAuth2)上。 另外,如果您不介意不使用OpenID / OAuth,那么现在有一个用PHP编写的完整的SSO解决方案(免责声明和完整披露:此产品由我自己在CubicleSoft的旗帜下开发和维护):

单一登录服务器/客户端

最初询问此问题时不存在。 它具有自由许可证(MIT或LGPL),并且满足您成为抽象层的要求。 该项目往往侧重于企业登录,但同时也有一些社交媒体登录(Google和Facebook)。

您可能还需要看一下HybridAuth,它只专注于社交媒体登录,但它更多的是一个库,而不是可以放到服务器上并使用它完成的预构建解决方案。 因此,设置它涉及更多的工作。 这确实取决于您所追求的。

如果您对自己的OpenID解决方案感到满意,那就太好了,但是今天比两年前有更多选择,并且人们仍然在寻找这个线索。

CubicleSoft answered 2020-08-12T00:03:30Z
translate from https://stackoverflow.com:/questions/4061537/best-way-to-implement-single-sign-on-with-all-major-providers