Home>Article>PHP Framework> Sharing on the pit: Laravel integration process of phpCAS

Sharing on the pit: Laravel integration process of phpCAS

藏色散人
藏色散人 forward
2021-09-19 16:53:49 1759browse

The following tutorial column ofLaravelwill share with you a pitfall of Laravel integrated phpCAS. I hope it will be helpful to friends who need it!

Laravel integrates phpCAS. Notes on pitfalls

CAS is currently a popular single sign-on protocol. The official provides the php version of client-side phpCAS. So far, So far, its coding style has remained in the PEAR era, without even using namespaces. Fortunately, phpCAS supports the introduction of composer, and I have done several Laravel project introductions without any problems. However, in the past two days, a project needs to be deployed from a single machine to a multi-machine deployment. I never expected to step on some pitfalls here. I will record them here. one time.

Callback pit

When jumping to the CAS Server for authentication, it was found that port 8080 was added to the incoming callback address. Because it is a multi-machine deployment, the access request will first pass through the load balancer (Alibaba Cloud SLB) and then reach the web server, and this 8080 is the listening port of the web server.

So I traced the logic of phpCAS to generate the callback address and found this piece of code:

if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) { $server_port = $_SERVER['SERVER_PORT']; } else { $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']); $server_port = $ports[0]; }

And Alibaba Cloud's SLB will not be passed to the back-end serverX-FORWARDED-PORTThis http header, so phpCAS will get$_SERVER['SERVER_PORT']which is the port 8080 of nginx.

Fortunately, phpCAS provides thesetFixedServiceURLfunction, which allows us to manually set the callback address:

phpCAS::setFixedServiceURL($request->url());

The callback address is normal now, but the callback address is returned from the CAS Server. The client is informed that the ticket is invalid.

Continuing to check the logs and codes, I found that I was negligent here. When the CAS Server returned to the client, the url of the page washttp://client/login?ticket=xxxxx, and When the client uses a ticket to exchange user information with the server, it also needs to bring the callback address (service) when applying for the ticket. The server will verify whether the ticket and service are consistent, and the service when applying for the ticket should behttp:/ /client/login, so we need to remove the ticket parameter in the url.

phpCAS::setFixedServiceURL($this->getUrlWithoutTicket($request));

getUrlWithoutTicketThe function is as follows:

private function getUrlWithoutTicket(Request $request) { $query = parse_query($request->getQueryString()); unset($query['ticket']); $question = $request->getBaseUrl().$request->getPathInfo() == '/' ? '/?' : '?'; return $query ? $request->url().$question.http_build_query($query) : $request->url(); }

Session pit

This is a combination pit of phpCAS Laravel, which makes you lose your temper.

PHP defaults to session storage as a file, so a very important point when converting a single machine into multiple machines is to handle session sharing. The solution is also very simple, which is to change the Session storage method from file to redis/memecache/database, etc.

Laravel provides these drivers by default, so I excitedly changed the.envfile and changedSESSION_DRIVERtoredis. I tried it online and found that it didn't work. The changes made by phpCAS to the$_SESSIONvariable were not written to redis. What's going on!

So I followed Laravel's Session implementation and found that it was not the imagined use ofsession_set_save_handlerto register the Session read and write logic. In other words, Laravel's Session did not actually modify php's ## For the read and write logic of #$_SESSION, directly operate$_SESSIONor follow the default behavior (read and write local files).

Well, fortunately, several SessionDrivers in Laravel have implemented the

SessionHandlerInterfaceinterface. We can call it ourselvessession_set_save_handler

session_set_save_handler(app(StartSession::class)->getSession($request)->getHandler());
万I never expected an error!

session_write_close(): Session callback expects true/false return value
After chasing the Laravel code, I found that the

writemethod of the redis driver's parent classIlluminate\Session\CacheBasedSessionHandlerreturnsvoid. So I submitted a PR to fix it, but I didn't expect it to be rejected. It turned out that someone had fixed it before and then reverted it, saying that it would cause the server to get stuck. However, I didn't find the specific issue.

Well, memcache and redis both inherit this parent class, so I will have to try database instead.

This time

session_write_closeNo error is reported, but there is still a problem with CAS login, and it keeps jumping between the CAS server and the callback url. So I chased all the logs and codes and found that thedestroymethod of the database driver classIlluminate\Session\DatabaseSessionHandlerdid not remove$this->exists## after destroying the Session. # The attribute is marked asfalse, and phpCAS has a logic that isrenameSession

$old_session = $_SESSION; session_destroy(); $session_id = preg_replace('/[^a-zA-Z0-9\-]/', '', $ticket); session_id($session_id); session_start(); $_SESSION = $old_session;
The consequence is

$_SESSION = $old_session;

The corresponding operation session The sql of the table executes update instead of insert, which means that the session data cannot be written to the session table!There is really no other way but to write a Session Wrapper yourself to handle it.

From the above two situations, the redis driver is easier to handle, as long as it can return true when calling the write method. So the code is as follows

namespace App\Services; use SessionHandlerInterface; class MySession implements SessionHandlerInterface { /** * @var SessionHandlerInterface */ protected $realHdl; /** * Session constructor. * @param SessionHandlerInterface $realHdl */ public function __construct(SessionHandlerInterface $realHdl) { $this->realHdl = $realHdl; } public function close() { return $this->realHdl->close(); } public function destroy($session_id) { return $this->realHdl->destroy($session_id); } public function gc($maxlifetime) { return $this->realHdl->gc($maxlifetime); } public function open($save_path, $name) { return $this->realHdl->open($save_path, $name); } public function read($session_id) { return $this->realHdl->read($session_id) ?: ''; } public function write($session_id, $session_data) { $this->realHdl->write($session_id, $session_data); return true; // 这里 } }

and then calls

session_set_save_handler

to become

session_set_save_handler(new MySession(app(StartSession::class)->getSession($request)->getHandler()));
Done!

The above is the detailed content of Sharing on the pit: Laravel integration process of phpCAS. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete