在和其他网站共享用户账号时,需要实现用户账号对接和单点登录的功能。而比较多的第三方账号登录功能多以OAuth的方式实现,而OAuth需要进行服务器之间的通信来认证授权并获取用户信息,会给服务器带来一些额外的开销和不确定性。而SAML则是一种完全不存在服务器直接通信的认证方式。
SAML的认证方式包括客户端Client,服务提供者SP和认证提供者IdP,用户访问SP,SP会返回给用户一个302重定向,将用户重定向到对应的IdP页面,用户输入登录信息后,IdP会根据用户的来源返回给用户一个POST到之前SP的表单,SP确认信息完成登录过程。其流程图如下所示
可以看到SP和IdP之间只通过引导用户重定向来交换数据,并不直接进行通信。为了确保通信的安全,建议SP和IdP都是用HTTPS协议。
SimpleSAMLphp是一个使用PHP实现的SAML类库,同时提供了一个可配置为SP或Idp的Web服务。其Web服务的Nginx配置如下
root /srv/http/simplesamlphp/www/; location / { index index.html index.htm index.php; } location ~ \.php { fastcgi_param PHP_VALUE "post_max_size=16M"; fastcgi_pass 127.0.0.1:9000; fastcgi_split_path_info ^(.+?\.php)(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_index index.php; include fastcgi.conf; }
其中主要两个问题,由于是用了path_info形式的URL风格,PHP的location不能配置成为\.php$,同时需要增加fastcgi_split_path_info的配置。
完成Web服务配置后,修改config/config.php,其中auth.adminpassword需要更改,否则一些功能会不能使用。将enable.saml20-idp设置为true。
'auth.adminpassword' => '123456', 'enable.saml20-idp' => true,
另外SimpleSAMLphp是用了PHP的Session作为会话保持,在使用多台服务器做负载均衡的情况下会存在不能共享的问题,这里可以开启SimpleSAMLphp使用MySQL存储Session的功能,其会在配置的数据库中创建三个表来存储Session信息
'store.sql.dsn' => 'mysql:host=localhost;port=3306;dbname=test', 'store.sql.username' => 'root', 'store.sql.password' => '', 'store.sql.prefix' => 'SimpleSAMLphp',
SimpleSAMLphp的SP配置在config/authsources.php文件中参考其中default-sp进行配置即可,其中需要注意有些Idp要求证书的签名算法必须使用SHA256,则此时使用SHA256生成签名证书,并将signature.algorithm设置为
'signature.algorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
访问SimpleSAMLphp的后台工具/module.php/core/frontpage_federation.php,可以查看到配置的SP,点击Show metadata可以看到此SP对应的metadata信息的XML文本,此XML可以用于某些Idp的配置。
增加Idp时,首先获得对方Idp的metadata信息,其中主要包括SingleSignOnService和SingleLogoutService以及签名证书的一些信息。将这些信息添加到metadata/saml20-idp-remote.php文件中的$metadata数组中即可。
而对于提供了metadata的XML的Idp,则可以使用SimpleSAMLphp提供的工具将XML转换为PHP数组,工具地址为/admin/metadata-converter.php,需要输入auth.adminpassword的密码。
在代码中需要调用SAML认证的逻辑时,可以通过引用SimpleSAMLphp的代码来实现,注意这里必须引用提供Web服务的SimpleSAMLphp代码,因为需要使用到配置文件。
//加载autoload require_once(SAML_ROOT . 'lib/_autoload.php'); //实例化认证对象,并指定使用的SP $as = new SimpleSAML_Auth_Simple($conf['sp']); //指定使用的Idp进行认证,如果没有认证会进行SAML的跳转 $as->requireAuth(array( 'saml:idp' => $conf['idp'], )); //如果已经认证成功则获取用户信息 $user_attributes = $as->getAttributes();