Open edX 目前支持CAS、SSL Client Certificates、Shibboleth三种认证扩展以及多种社会化登录(博客先前有过介绍),
默认支持列表可以参照官方WIki
CAS干嘛用的,看官比我清楚,没我清楚的请立即谷歌,故下面直接操作:
1.环境:
ubuntu 12.04
edx-devstack
<td class="content">
<code class="functions">sudo</code> <code class="functions">su</code> <code class="plain">edxapp</code>
</td>
</tr>
</table>
</div>
2.更换django-cas
edX默认安装mitx的版本base.txt#L145略旧,我们需要换掉
<td class="content">
<code class="plain">pip unstall django-cas</code>
</td>
</tr>
</table>
</div>
请先确认您已经安装mercurial
<td class="content">
<code class="functions">sudo</code> <code class="plain">apt-get </code><code class="functions">install</code> <code class="plain">mercurial</code>
</td>
</tr>
</table>
</div>
克隆cpcc的最新版本:
<td class="content">
<code class="plain">hg clone <a href="https://bitbucket.org/cpcc/django-cas">https://bitbucket.org/cpcc/django-cas</a></code>
</td>
</tr>
</table>
</div>
3.CAS Server要求:
你可以安装官方标准CAS 1.x 2.x 或者3.X理论上都支持,但必须保证CAS提供username 和 email信息,建议不要使用1.x,推荐3.x
3.x添加别的字段可以参考官方wiki配置
2.x默认只提供username,需要添加别的信息就要硬编码,然后django-cas也需要相应的修改
我们测试使用2.x,只是因为学校请的攻城狮在闭源的道路上越走越远,楼主挽救不了。。。苦恼啊
4.Django-cas修改:
编辑 django_cas/backends.py 修改方法_verify_cas2
<td class="content">
<code class="keyword">def</code> <code class="plain">_verify_cas2(ticket, service):</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>2</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="comments">"""Verifies CAS 2.0+ XML-based authentication ticket.</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>3</code>
</td>
<td class="content">
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>4</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="comments">Returns username on success and None on failure.</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>5</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="comments">"""</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>6</code>
</td>
<td class="content">
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>7</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">try</code><code class="plain">:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>8</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">from</code> <code class="plain">xml.etree </code><code class="keyword">import</code> <code class="plain">ElementTree</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>9</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">except</code> <code class="plain">ImportError:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>10</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">from</code> <code class="plain">elementtree </code><code class="keyword">import</code> <code class="plain">ElementTree</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>11</code>
</td>
<td class="content">
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>12</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">params </code><code class="keyword">=</code> <code class="plain">{</code><code class="string">'ticket'</code><code class="plain">: ticket, </code><code class="string">'service'</code><code class="plain">: service}</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>13</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">url </code><code class="keyword">=</code> <code class="plain">(urljoin(settings.CAS_SERVER_URL, </code><code class="string">'proxyValidate'</code><code class="plain">) </code><code class="keyword">+</code> <code class="string">'?'</code> <code class="keyword">+</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>14</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">urlencode(params))</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>15</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">page </code><code class="keyword">=</code> <code class="plain">urlopen(url)</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>16</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">try</code><code class="plain">:</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>17</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">response </code><code class="keyword">=</code> <code class="plain">page.read()</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>18</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">elements </code><code class="keyword">=</code> <code class="plain">{}</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>19</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">tree </code><code class="keyword">=</code> <code class="plain">ElementTree.fromstring(response)</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>20</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">if</code> <code class="plain">tree[</code><code class="value"></code><code class="plain">].tag.endswith(</code><code class="string">'authenticationSuccess'</code><code class="plain">):</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>21</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">for</code> <code class="plain">element </code><code class="keyword">in</code> <code class="plain">tree[</code><code class="value"></code><code class="plain">]:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>22</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">elements[element.tag.split(</code><code class="string">"}"</code><code class="plain">).pop()] </code><code class="keyword">=</code> <code class="plain">element.text</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>23</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">return</code> <code class="plain">tree[</code><code class="value"></code><code class="plain">][</code><code class="value"></code><code class="plain">].text, elements</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>24</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">else</code><code class="plain">:</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>25</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">return</code> <code class="color1">None</code><code class="plain">, </code><code class="color1">None</code>
</td>
</tr>
</table>
</div>
然后修改CASBackend(object)类中的authenticate方法:
<td class="content">
<code class="keyword">def</code> <code class="plain">authenticate(</code><code class="color1">self</code><code class="plain">, ticket, service, request):</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>2</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="comments">"""Verifies CAS ticket and gets or creates User object"""</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>3</code>
</td>
<td class="content">
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>4</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">username, attributes </code><code class="keyword">=</code> <code class="plain">_verify(ticket, service)</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>5</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">if</code> <code class="plain">attributes:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>6</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">request.session[</code><code class="string">'attributes'</code><code class="plain">] </code><code class="keyword">=</code> <code class="plain">attributes</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>7</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">if</code> <code class="keyword">not</code> <code class="plain">username:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>8</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">return</code> <code class="color1">None</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>9</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">try</code><code class="plain">:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>10</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">user </code><code class="keyword">=</code> <code class="plain">User.objects.get(username</code><code class="keyword">=</code><code class="plain">username)</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>11</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">except</code> <code class="plain">User.DoesNotExist:</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>12</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="comments"># user will have an "unusable" password</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>13</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">user </code><code class="keyword">=</code> <code class="plain">User.objects.create_user(username,attributes[</code><code class="string">'email'</code><code class="plain">],'')</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>14</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="plain">user.save()</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>15</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="keyword">return</code> <code class="plain">user</code>
</td>
</tr>
</table>
</div>
然后安装:
<td class="content">
<code class="plain">python django_cas/setup.py </code><code class="functions">install</code>
</td>
</tr>
</table>
</div>
5.配置Open edX:
编辑 edx-platform/lms/envs/devstack.py 加入:
<td class="content">
<code class="plain">FEATURES[</code><code class="string">'AUTH_USE_CAS'</code><code class="plain">] = True</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>2</code>
</td>
<td class="content">
<code class="plain">CAS_SERVER_URL = </code><code class="string">"<a href="http://cas/login">http://cas/login</a>"</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>3</code>
</td>
<td class="content">
<code class="plain">AUTHENTICATION_BACKENDS = (</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>4</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="string">'django.contrib.auth.backends.ModelBackend'</code><code class="plain">,</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>5</code>
</td>
<td class="content">
<code class="spaces"> </code><code class="string">'django_cas.backends.CASBackend'</code><code class="plain">,</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>6</code>
</td>
<td class="content">
<code class="plain">)</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>7</code>
</td>
<td class="content">
<code class="plain">INSTALLED_APPS += (</code><code class="string">'django_cas'</code><code class="plain">,)</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>8</code>
</td>
<td class="content">
<code class="plain">MIDDLEWARE_CLASSES += (</code><code class="string">'django_cas.middleware.CASMiddleware'</code><code class="plain">,)</code>
</td>
</tr>
</table>
</div>
6.测试:
<td class="content">
<code class="functions">cd</code> <code class="plain">edx-platform</code>
</td>
</tr>
</table>
</div>
<div class="line alt2">
<table>
<tr>
<td class="number">
<code>2</code>
</td>
<td class="content">
<code class="plain">paver update_assets</code>
</td>
</tr>
</table>
</div>
<div class="line alt1">
<table>
<tr>
<td class="number">
<code>3</code>
</td>
<td class="content">
<code class="plain">paver devstack lms</code>
</td>
</tr>
</table>
</div>