我目前正在尝试让PHP登录CAS单点登录服务器并进行身份验证,这很困难。
官方网站在这里有一些基本的源代码,用来处理用户的身份验证和登录。据我所见,在我的测试中,它完成了流程中的步骤1和2(基本流程见图)。一旦我登录到测试服务器,我就可以完成第3步,并从将我送回页面的URL中检索服务票证。似乎没有任何例子可以完成流程的第4步和第5步。我需要编写自己的代码才能做到这一点,这正确吗?
我曾试图取回票证,然后使用我自己的一些带有cURL或fsockopen的代码将其发送到验证服务,但没有成功。
if (isset($_GET['ticket']))
{
$currentProtocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ? 'https://' : 'http://';
$requestUri = explode('?', $_SERVER['REQUEST_URI']);
$requestUri = $requestUri[0];
$ticket = $_GET['ticket'];
$port = ($_SERVER['SERVER_PORT'] != 80) ? ':8080' : '';
$currentUrl = urlencode($currentProtocol . $_SERVER['SERVER_NAME'] . $port . $requestUri);
$validateUrl = 'ssl://server.com/cas/serviceValidate?service=' . $currentUrl . '&ticket=' . $ticket;
$errno = 0;
$errstr = '';
$fp = fsockopen($validateUrl, 443, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />'n";
}
else {
var_dump($fp);
$out = "GET / HTTP/1.1'r'n";
$out .= "Host: www.example.com'r'n";
$out .= "Connection: Close'r'n'r'n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
}
如果我直接通过浏览器访问,我可以从服务中获得合法的响应,例如:
https://server.com/cas/serviceValidate?service=http%3A%2F%2Flocalhost%3A8080%2Ftestcas%2Fcas-client.php&ticket=ST-35717-XLiWQ2ucCCuks2wsVNMJ-cas
返回包含Active Directory用户ID:的XML响应
<cas:serviceResponse xmlns:cas='http://www.server.com/cas'>
<cas:authenticationSuccess>
<cas:user>c314317</cas:user>
</cas:authenticationSuccess>
</cas:serviceResponse>
但我真的认为我需要能够使用PHP直接从服务器端访问该URL,然后一旦我有了用户ID,我就可以将其链接回我们的系统,并将其登录到网站中。
我的问题是似乎没有任何代码来处理票证和验证方面的问题。有人能给我指正确的方向吗?
非常感谢。
好吧,我想我解决了cURL的问题。我没有将CURLOPT_SSL_VERIFYPEER设置为false,这就是它失败的原因。我现在可以用PHP获取XML响应,处理XML响应并检索用户ID
// Get the current server address we are executing the PHP from
$currentProtocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ? 'https://' : 'http://';
$requestUri = explode('?', $_SERVER['REQUEST_URI']);
$requestUri = $requestUri[0];
$ticket = $_GET['ticket'];
$port = ($_SERVER['SERVER_PORT'] != 80) ? ':' . $_SERVER['SERVER_PORT'] : ''; # Don't need the port if it's 80, but needed if for example test server is running port 8080
$currentUrl = $currentProtocol . $_SERVER['SERVER_NAME'] . $port . $requestUri;
// Setup the validation URL
$validateUrl = 'https://sso.server.com/cas/serviceValidate?service=' . strtolower(urlencode($currentUrl)) . '&ticket=' . $ticket;
// Send request to validate the URL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $validateUrl); # The URL to get the data from
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); # Return the value of curl_exec() instead of outputting it out directly.
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); # The number of seconds to wait while trying to connect
curl_setopt($ch, CURLOPT_TIMEOUT, 120); # The maximum number of seconds to allow cURL functions to execute
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); # Check the existence of a common name and also verify that it matches the hostname provided
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); # Stop cURL from verifying the peer's certificate
curl_setopt($ch, CURLOPT_HEADER, false); # Don't include the header in the output
// Execute the request and close the handle
$xml = curl_exec($ch);
curl_close($ch);
// Get the user ID from the XML using XPath
$xml = new SimpleXMLElement($xml);
$result = $xml->xpath('cas:authenticationSuccess/cas:user');
$userId = null;
while(list( , $node) = each($result))
{
$userId = (string) $node;
}
echo 'user: ' . $userId . "<br>";