+021 34205990    canvas@sjtu.edu.cn

首页 > 新技术应用指南

LTI 工具开发:启动信息验证

  • 作者: 何晴 于 4年前 发布

开发LTI工具的基础步骤是对传入的启动信息进行验证。相当于常规网站对登陆用户提供的用户名及密码进行身份验证。如果LTI工具本身具有验证用户身份的代码模块,则这部分代码在开发LTI应用时可以复用。

第一步:验证是否是LTI启动请求

确认LTI启动请求包含以下属性:

1. 是 HTTP POST 请求

2. POST参数(必填项)

lti_message_type=basic-lti-launch-request

lti_version=LTI-1p0

oauth_consumer_key非空

resource_link_id非空

虽然LTI启动请求的必须参数很少,但是只要有缺失,就会被认定非法LTI请求,系统应拒绝连接。


Sample PHP Code   验证LTI Launch Request

// Check it is a POST request,OK初始值设置为true
  $ok = $ok && $_SERVER['REQUEST_METHOD'] === 'POST';

  // Check the LTI message type
  $ok = $ok && isset($_POST['lti_message_type']) && ($_POST['lti_message_type'] === 'basic-lti-launch-request');

  // Check the LTI version
  $ok = $ok && isset($_POST['lti_version']) && ($_POST['lti_version'] === 'LTI-1p0');

  // Check a consumer key exists
  $ok = $ok && !empty($_POST['oauth_consumer_key']);

  // Check a resource link ID exists
  $ok = $ok && !empty($_POST['resource_link_id']);



第二步:验证是否是有效的LTI启动请求

LTI启动请求使用OAuth 1进行签名,确保LTI工具收到的数据与LMS发送的数据相同。同时,确保该消息不是已经接收和处理的消息副本。

OAuth签名过程,将下列参数插入到启动请求传递的数据中:

oauth_callback:about:blank

oauth_consumer_key:LTI工具颁发给LMS的consumer_key,用于匹配secret

oauth_nonce:

oauth_signature:

oauth_signature_method:HMAC-SHA1

oauth_timestamp:

oauth_version:1.0

检查签名消息所涉及的大部分工作都由OAuth库处理,可用于大多数编程语言。Oauth库根据consumer_key来匹配secret,并提供了确认nonce值不重复的方法。它执行的任务是:

  1. 检查时间戳是否在任一侧当前服务器时间的指定时间间隔内(通常为±5分钟);
  2. 检查nonce与另一个收到的消息的值是否相同(此检查可以仅限于那些在允许的时间间隔内且来自同一LTI工具的消息);
  3. 使用LTI工具的secret生成消息的签名,并将该值与接收的值进行比较。

Sample PHP Code

// Check the consumer key is recognised,For example, a tool consumer with a key of imsglobal.org and a secret of ThisIsABigSecret!
  $ok = $ok && array_key_exists($_POST['oauth_consumer_key'], $tool_consumer_secrets);

  // Check the OAuth credentials (nonce, timestamp and signature)
  if ($ok) {
    require_once('lib.php');  // load class to act as an OAuth data store
    try {
      $store = new ImsOAuthDataStore($_POST['oauth_consumer_key'], $CONSUMERS_TABLE[$_POST['oauth_consumer_key']]);
      $server = new OAuthServer($store);
      $method = new OAuthSignatureMethod_HMAC_SHA1();
      $server->add_signature_method($method);
      $request = OAuthRequest::from_request();
      $server->verify_request($request);
    } catch (Exception $e) {
      $ok = FALSE;
    }
  }

ImsOAuthDataStore Class   ImsOAuthDataStore库


class ImsOAuthDataStore extends OAuthDataStore {

    private $consumer_key = NULL;
    private $consumer_secret = NULL;

    public function __construct($consumer_key, $consumer_secret) {

      $this->consumer_key = $consumer_key;
      $this->consumer_secret = $consumer_secret;

    }

    function lookup_consumer($consumer_key) {

      return new OAuthConsumer($this->consumer_key, $this->consumer_secret);

    }

    function lookup_token($consumer, $token_type, $token) {

      return new OAuthToken($consumer, '');

    }

    function lookup_nonce($consumer, $token, $nonce, $timestamp) {

      return FALSE;  // If a persistent store is available nonce values should be retained for a period and checked here

    }
    function new_request_token($consumer, $callback = null) {

      return NULL;

    }

    function new_access_token($token, $consumer, $verifier = null) {

      return NULL;

    }

  }


https://oc.sjtu.edu.cn


第三步:验证是否包含LTI启动请求的所需参数

Sample PHP Code

此示例代码假定LTI工具需要知道用户是谁(User_id)以及角色是教师还是学生(role)。此外,唯一被认可的角色是InstructorLearner ;

// Check for a user ID
  $ok = $ok && !empty($_POST['user_id']);

  // Check for a role
  $ok = $ok && !empty($_POST['roles']);

  if ($ok) {
    // Check that the user is either a Learner or an Instructor
    $roles = parseRoles($_POST['roles']);
    $ok = in_array('urn:lti:role:ims/lis/Learner', $roles) || in_array('urn:lti:role:ims/lis/Instructor', $roles);}



第四步:建立用户会话

此过程可能与其他处理登录页面的代码模块具有相似性。但对于LTI启动请求,除登录过程外,还需要下列操作; 例如:

  1. 取消此LTI工具的任何现有会话。
  2. 检查用户ID是否已知:
    • 如果没有,为他们提供一个新帐户;
    • 如果是,请更新任何已更改的详细信息(例如名称)。
  3. 检查资源链接ID是否已知:
    • 如果没有,请提供新链接可能需要的任何工作空间;
    • 如果是,请更新任何已更改的详细信息(例如标题)。
  4. 初始化用户的登录会话,获取下列信息,例如:
    • 用户身份;
    • 角色;
    • 资源ID;
    • 返回网址。

建立的用户会话通常将被赋予一个ID,该ID通过cookie或查询参数与来自用户浏览器的请求一起传递; 主要目标是允许LTI工具在从用户的浏览器接收到HTTP请求时知道用户是谁,而无需再次验证它们并验证他们授权访问所请求的资源。

Sample PHP Code


// Cancel any existing session
  session_start();
  $_SESSION = array();
  session_destroy();
  session_start();

  // Initialise the user session
  $_SESSION['userId'] = $_POST['user_id'];
  $_SESSION['isInstructor'] = in_array('urn:lti:role:ims/lis/Instructor', $roles);
  $_SESSION['firstname'] = $_POST['lis_person_name_given'];
  $_SESSION['lastname'] = $_POST['lis_person_name_family'];
  $_SESSION['consumerKey'] = $_POST['oauth_consumer_key'];
  $_SESSION['resourceLinkId'] = $_POST['resource_link_id'];
  if (!$_SESSION['isInstructor'] && 
      isset($_POST['lis_outcome_service_url']) && isset($_POST['lis_result_sourcedid'])) {
    $_SESSION['outcomesUrl'] = $_POST['lis_outcome_service_url'];
    $_SESSION['resultSourcedId'] = $_POST['lis_result_sourcedid'];
  }


第五步:重定向用户