You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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解析、签名验证等安全核心逻辑
  • 注册流程:
    1. 平台(Moodle)向工具发送注册请求,携带自身发行者、端点等信息
    2. 工具验证请求合法性,返回工具的注册配置(Client ID、回调URL等)
    3. 双方持久化彼此的注册信息,为后续LTI请求提供验证依据
  • LTI启动流程:
    1. 平台携带授权码、state参数跳转至工具回调URL
    2. 工具验证state,使用授权码向平台请求访问令牌与ID Token
    3. 工具验证ID Token的签名与内容,提取用户/上下文信息,完成业务逻辑

四、注册环节常见问题排查

  • 密钥不匹配:确保Moodle中配置的工具公钥与本地public.key完全一致,需保留完整的PEM格式头部与尾部
  • 端点URL错误:Moodle中配置的注册回调、登录回调URL必须与CodeIgniter的redirect_uri完全一致,包括协议(HTTP/HTTPS)、域名、路径
  • 请求验证失败:检查Moodle发送的注册请求中iss字段是否与配置的平台发行者一致,确保工具能正确解析JSON格式的请求体
  • 内存存储限制:示例使用InMemoryRegistrationRepository仅用于演示,生产环境需替换为数据库存储,避免重启后注册信息丢失

内容的提问来源于stack exchange,提问作者Anubhab Mondal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 18:15:59