0%

修改KodExplorer使之支持CasDoor登录

之前修改了两个Python的应用,使之支持CasDoor。踩过的一些坑已经记录了。非常感谢维护者,这些问题现在都已经修复了。不过由于KodExplorer使用PHP开发,同时也不是自己写的,比较复杂。故新开一篇文章来记录下修改过程吧。

由于PHP版本之间的兼容性问题,需要修改CasDoor-PHP-SDK中的一些代码。我这里的PHP版本是7.3。

KodExplorer中相关代码主要在/app/controller/user.class.php中,后文针对KodExplorer的修改都是在此文件。

其中我们需要用到Url,User,Token和Jwt四个模块。由于比较菜,没搞明白PHP中模块、命名空间和类之间的包含关系,这里改使用include方式引入SDK。

1
2
3
4
5
6
include ('casdoor/Auth/AuthConfig.php');
include ('casdoor/Exceptions/CasdoorException.php');
include ('casdoor/Auth/CasUrl.php');
include ('casdoor/Auth/CasUser.php');
include ('casdoor/Auth/CasToken.php');
include ('casdoor/Auth/CasJwt.php');

这里由于KodExplorer没有使用命名空间,故我也将SDK中的命名空间去掉了(我比较菜,这块没弄明白)。同时,需要修改这几个类的名称,分别改为CasUrl,CasUser,CasToken,CasJwt。

针对SDK引用了一些外部库,还要执行下面两个命令,安装对应的库:

1
2
composer require league/oauth2-client:2.5
composer require guzzlehttp/guzzle:^7.1

这里指定了版本,因为新版本不支持PHP7.3了。

对于CasToken类,还需要加一行代码,来加载这些库:

1
2
3
4
require './vendor/autoload.php';

use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Token\AccessTokenInterface;

在我使用的SDK版本中,CasUser类和AuthConfig类中声明变量使用了类型,会导致7.3版本的PHP报错,如果改用8.0版本,则KodExplorer又会报错。故这里选择比较简单的修改SDK代码的方式,将变量声明中的变量类型去掉即可。在最新版本的SDK中,已经不再有变量类型了。

在类声明的外部,使用CasUser类中的IninConfig方法初始化设置:

1
2
3
4
5
6
7
8
CasUser::initConfig(
'Cas的Url',
'clientID',
'clientSecret',
file_get_contents("/www/xxx.pem"), // 这里填证书公钥的地址
'Cas中的OrgName',
'Cas中的AppName'
);

还有最后一个更改,对于data/system/system_member.php中存储的用户信息,还需要增加一个字段来识别用户:

1
2
3
4
"createTime": 161140****,
"status": 1,
"lastLogin": 164767****,
"casID": "b2****27-****-****-****-041d****c3bc"

由于该工程是自用,我没有修改用户管理部分的代码,使之能够绑定CasID。

接下来就进入具体的登录逻辑修改环节了。

首先修改login函数代码,这里是登录页面的Controller。如果不想保留原有的登陆方法,直接将该函数内容修改为以下即可:

1
2
header('location: ' . CasUrl::getSigninUrl('https://your-kod-url/callback', CasUser::getConfig()));
exit;

这里使用了一个不存在的callback地址,该地址进行了伪静态。不过现在不需要了,最新版的CasDoor已经解决了这个Bug:https://github.com/casdoor/casdoor/issues/573。实际的请求地址应该是index.php?user/loginSubmit。

紧接着,修改LoginSubmit函数的代码,如果不需要保留密码登录方式,这里将password字段置空即可:

1
2
// $password = rawurldecode($this->in['password']);
$password = "";

然后要处理的就是接收到code和state之后,要解析并获取Cas返回的用户信息中的ID,并与KodExplorer用户数据中的CasID进行匹配:

1
2
3
4
5
6
7
8
9
10
11
12
$member = systemMember::loadData();
$user = $member->get('name',$name);

$isSsoLogin = false;
if($this->in['code'] && $this->in['state']){
$access_token = CasToken::getOAuthToken($this->in['code'], $this->in['state']);
$jwt = new CasJwt();
$jwt_data = $jwt->parseJwtToken($access_token, CasUser::$authConfig);

$user = $member->get('casID',$jwt_data['id']);
$isSsoLogin = true;
}

最后在下面的if中加一个判断即可,以最小的代价加入Cas支持:

1
2
3
if($apiLoginCheck && $user){//api自动登陆
}else if ($isSsoLogin && $user){//Cas登录
}

至此,大功告成。