配置SAML单点登录环境

在和其他网站共享用户账号时,需要实现用户账号对接和单点登录的功能。而比较多的第三方账号登录功能多以OAuth的方式实现,而OAuth需要进行服务器之间的通信来认证授权并获取用户信息,会给服务器带来一些额外的开销和不确定性。而SAML则是一种完全不存在服务器直接通信的认证方式。

1. SAML 认证方式

SAML的认证方式包括客户端Client,服务提供者SP和认证提供者IdP,用户访问SP,SP会返回给用户一个302重定向,将用户重定向到对应的IdP页面,用户输入登录信息后,IdP会根据用户的来源返回给用户一个POST到之前SP的表单,SP确认信息完成登录过程。其流程图如下所示

可以看到SP和IdP之间只通过引导用户重定向来交换数据,并不直接进行通信。为了确保通信的安全,建议SP和IdP都是用HTTPS协议。

2. 配置 SimpleSAMLphp

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',

3. 配置SP并增加Idp配置

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的密码。

4. 代码中即成SAML认证

在代码中需要调用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();