简化 scorm window.open(scormUrl)

Simplify-scorm window.open(scormUrl)

我正尝试在一个新的 window 中打开我的 SCORM 教育,但是当我这样做时,所有报告都停止工作(如果我在 iframe 中这样做也是如此)。 当我只是做 location.href = scormURL;然后它工作正常。我的 scorm 文件与 LMS 所在的域不同。

似乎在对我的 scorm 响应执行 loadFromJSON(response) 后,SCORM api 没有被初始化。但是这个问题只有在我打开一个新的 window 或在 iframe 中显示 scorm 教育时才会出现。

我用的是SCORM2004

编辑: 这是 XSS 问题,通过 ocelot 网关路由到 azure CDN 以获取相同的域来解决。

My scorm file is in a different domain then the LMS.

这是你的问题。

SCORM 软件包对您的 LMS(因此对您的报告)唯一的 link 是 SCORM API. Modern browsers do not allow CORS (cross-origin resource sharing) by default because it opens you to XSS(跨站点脚本)攻击。

如果外部域中存在该程序包,则该程序包可能 find the API,但无法直接与 LMS 通信。

我解决这个问题的方法是在托管 SCORM API 的外部域上添加一个包装器 .js 文件,并使用 Window.postMessage 将调用传回 LMS。

然后您使用 window.open 或您的 iframe 在外部域上打开一个页面,该页面可以访问 scorm_wrapper.js 和它自己的 iframe 来托管您的包。


scorm_wrapper.js

因为Window.opener方法不可靠,而且当前window是从不同来源打开的时候功能不全,我们将直接使用域名。

因为我们控制了 LMS,所以我们知道它所在的域 运行 并且因为我们刚刚打开了包裹,所以我们知道它应该是活动的。我们可以postMessage它来请求回复。

function receiveMessage(data) { 
    // We will return to this later...
};

// Be ready for the reply from the LMS.
window.addEventListener('message', receiveMessage(event));

// Ping the domain your LMS is running on.
window.parent.postMessage('ping', 'https://example.com');

当我们的 LMS 收到消息时,它应该用自己的 postMessage 回复到 event.origin,这是我们的包 运行 所在的域。

现在,当我们收到 LMS 的回复时,我们就知道我们可以访问了。

var connected = false;

function receiveMessage(data) {
    // ...

    // Do not do anything unless the message was from
    // a domain we trust.
    if (event.origin !== 'https://example.com') return;

    switch (event.data) {
        case 'pong':
            connected = true;
            break;
        // We will return to this later...
    }
}

现在,只要从包中调用包装器的 API 对象,我们就可以将消息传递给 LMS。

function sendMessage(args) {
    if (!connected) return;
    window.parent.postMessage(args, 'https://example.com');
}

const apiWrapper = {
    Initialize: function (args) {
        sendMessage({ method: 'Initialize', data: args });
    },
    // More SCORM endpoints...
};

window.API = apiWrapper;
window.API_1484_11 = apiWrapper;

根据需要将案例添加到 receiveMessage

function receiveMessage(data) {
    // ...
    switch (event.data) {
        case 'pong':
            connected = true;
            break;
        case 'initialize-acknowledge`:
            //...
            break;
    }
}

这种方法不是很好,但它可以让您安全地解决跨域问题,而无需访问服务器配置。