如何在PHP CodeIgniter工具中实现Tao Core LTI 1.3?求示例及注册指导
关于lib-lti1p3-core嵌入CodeIgniter及注册问题的解决方案
一、lib-lti1p3-core的示例资源
- 官方仓库内包含基础示例代码,可查看仓库中的
example目录,涵盖平台注册、发起LTI请求、工具接收验证等核心流程 - 针对CodeIgniter的适配,可参考以下简化演示片段
二、CodeIgniter适配演示片段
1. 核心配置文件(application/config/lti1p3.php)
<?php defined('BASEPATH') OR exit('No direct script access allowed'); $config['lti1p3'] = [ 'tool' => [ 'client_id' => '你的工具Client ID', 'redirect_uri' => base_url('lti/callback'), 'name' => '你的工具名称', 'description' => '工具描述' ], 'platform' => [ 'moodle' => [ 'issuer' => 'https://你的Moodle域名', 'auth_login_url' => 'https://你的Moodle域名/mod/lti/auth.php', 'auth_token_url' => 'https://你的Moodle域名/mod/lti/token.php', 'keyset_url' => 'https://你的Moodle域名/mod/lti/certs.php', 'client_id' => 'Moodle分配给工具的Client ID' ] ], 'key_chain' => [ 'private_key' => file_get_contents(APPPATH . 'lti_keys/private.key'), 'public_key' => file_get_contents(APPPATH . 'lti_keys/public.key'), 'kid' => '你的密钥ID' ] ];
2. LTI核心控制器(application/controllers/Lti.php)
<?php defined('BASEPATH') OR exit('No direct script access allowed'); use OAT\Library\Lti1p3Core\Registration\Registration; use OAT\Library\Lti1p3Core\Security\KeyChain\KeyChain; use OAT\Library\Lti1p3Core\Registration\InMemoryRegistrationRepository; use OAT\Library\Lti1p3Core\Security\KeyChain\InMemoryKeyChainRepository; class Lti extends CI_Controller { private $registrationRepo; private $keyChainRepo; public function __construct() { parent::__construct(); $this->load->config('lti1p3'); $this->initRepositories(); } private function initRepositories() { // 初始化密钥链 $keyChainConfig = $this->config->item('key_chain', 'lti1p3'); $keyChain = new KeyChain( $keyChainConfig['kid'], $this->config->item('tool', 'lti1p3')['client_id'], $keyChainConfig['private_key'], $keyChainConfig['public_key'] ); $this->keyChainRepo = new InMemoryKeyChainRepository([$keyChain]); // 初始化Moodle平台注册 $platformConfig = $this->config->item('platform', 'lti1p3')['moodle']; $registration = new Registration( 'moodle_reg', $platformConfig['issuer'], $platformConfig['client_id'], $this->config->item('tool', 'lti1p3')['client_id'], $this->config->item('tool', 'lti1p3')['redirect_uri'], null, $platformConfig['auth_login_url'], $platformConfig['auth_token_url'], $platformConfig['keyset_url'], $keyChain->getKid() ); $this->registrationRepo = new InMemoryRegistrationRepository([$registration]); } // 处理Moodle自动注册请求 public function register() { try { $requestData = json_decode(file_get_contents('php://input'), true); // 验证平台发行者合法性 if (empty($requestData['iss']) || $requestData['iss'] !== $this->config->item('platform', 'lti1p3')['moodle']['issuer']) { throw new Exception('无效的平台发行者'); } // 生产环境建议将注册信息持久化到数据库,替代内存存储 // 示例:$this->db->insert('lti_registrations', $registrationData); // 返回工具注册响应 $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'client_id' => $this->config->item('tool', 'lti1p3')['client_id'], 'redirect_uris' => [$this->config->item('tool', 'lti1p3')['redirect_uri']], 'response_types' => ['id_token'], 'grant_types' => ['authorization_code', 'client_credentials'], 'subject_type' => 'public', 'id_token_signed_response_alg' => 'RS256' ])); } catch (Exception $e) { $this->output ->set_status_header(400) ->set_content_type('application/json') ->set_output(json_encode(['error' => $e->getMessage()])); } } // LTI登录回调处理 public function callback() { try { $authCode = $this->input->get('code'); $state = $this->input->get('state'); // 验证state(需提前将state存储到session/缓存,此处省略存储逻辑) if (empty($state) || $state !== $this->session->userdata('lti_state')) { throw new Exception('无效的state参数'); } // 获取平台注册信息 $registration = $this->registrationRepo->findByIssuerClientId( $this->config->item('platform', 'lti1p3')['moodle']['issuer'], $this->config->item('platform', 'lti1p3')['moodle']['client_id'] ); // 请求访问令牌与ID Token $tokenClient = new \OAT\Library\Lti1p3Core\Security\OAuth2\Client\OAuth2Client(); $tokenResponse = $tokenClient->requestAccessToken( $registration->getPlatform()->getAuthTokenUrl(), $registration->getTool()->getClientId(), $registration->getKeyChain()->getPrivateKey(), $authCode, $registration->getTool()->getRedirectUri() ); // 解析ID Token提取用户与课程信息 $idToken = $tokenResponse->getIdToken(); $userData = [ 'user_id' => $idToken->getClaim('sub'), 'full_name' => $idToken->getClaim('name'), 'email' => $idToken->getClaim('email'), 'course_context' => $idToken->getClaim('https://purl.imsglobal.org/spec/lti/claim/context') ]; // 业务逻辑处理:用户登录、课程关联等 $this->session->set_userdata('lti_authenticated', true); $this->session->set_userdata('lti_user', $userData); redirect('/dashboard'); } catch (Exception $e) { $this->output ->set_status_header(401) ->set_output('LTI登录失败:' . $e->getMessage()); } } }
三、lib-lti1p3-core代码流程与结构
- 核心组件分层:
- Registration:管理平台与工具的注册元数据,包含双方Client ID、端点URL、密钥关联关系
- KeyChain:封装密钥对,负责JWT令牌的签名与验证
- OAuth2Client:实现OAuth2授权码流程,处理令牌请求与交换
- Security模块:提供JWT解析、签名验证等安全核心逻辑
- 注册流程:
- 平台(Moodle)向工具发送注册请求,携带自身发行者、端点等信息
- 工具验证请求合法性,返回工具的注册配置(Client ID、回调URL等)
- 双方持久化彼此的注册信息,为后续LTI请求提供验证依据
- LTI启动流程:
- 平台携带授权码、state参数跳转至工具回调URL
- 工具验证state,使用授权码向平台请求访问令牌与ID Token
- 工具验证ID Token的签名与内容,提取用户/上下文信息,完成业务逻辑
四、注册环节常见问题排查
- 密钥不匹配:确保Moodle中配置的工具公钥与本地
public.key完全一致,需保留完整的PEM格式头部与尾部 - 端点URL错误:Moodle中配置的注册回调、登录回调URL必须与CodeIgniter的
redirect_uri完全一致,包括协议(HTTP/HTTPS)、域名、路径 - 请求验证失败:检查Moodle发送的注册请求中
iss字段是否与配置的平台发行者一致,确保工具能正确解析JSON格式的请求体 - 内存存储限制:示例使用
InMemoryRegistrationRepository仅用于演示,生产环境需替换为数据库存储,避免重启后注册信息丢失
内容的提问来源于stack exchange,提问作者Anubhab Mondal
相关产品推荐
相关产品推荐

