• client api


    当时处理这部分的动机是将edx与微信对接

    如果你在处理与edx API相关的工作,这篇文章可能对你也有帮助。好比你在编译edx移动端(android和iOS), 这部分工作应该也是最主要的工作之一

    思路

    我们首先简要做一下任务陈述:允许edx用户通过微信公众平台访问edX,登录以及请求相关的数据

    这里假设读者们已经基本了解了OAuth2,包括它的一些基本概念和通信流程,如果还不了解,请先阅读OAuth2相关的材料。

    在我们的任务中,我们先识别出OAuth中的参与实体,RO(resource owner),RS (resource server)和Client,至于AS(authorization server)在edx中和RS可以认为一体。

    很显然我们的任务中,edx平台作为RS,而edx user是RO,而我们自己写的微信公众号后台便是Client。

    由于微信后端和平台拥有者是相同的,所以我就不采用redirect的方式了。而假设Client是受信任的。

    那么通信的流程是这样的,edx user在微信给微信公众号中给Client发送账号和密码,而后Client携带用户账号和密码去换取授权令牌(Access Token),且存下授权令牌,如此一来,概念上,用户在微信中便已经保持登录edX的状态了。

    而后Client根据用户请求,携带Access Token去服务器请求资源返回给微信用户。

    这里不应当混淆的是,使用微信账户登录edx,和在微信中以edx user身份访问edx,是两个完全不同的过程,使用微信账户登录edx本质上是个第三方社交账号登录edx的问题,RS是微信,而edx user在微信中访问edx,RS是edX。

    好了,思路基本清晰了。

    先前的经验

    之前写过一篇博客:让edx为手机端提供接口

    本打算按照之前的经验,却发现,采用TokenAuthentication的解决方案除了侵入性太强,不够优雅之外,安全性也得不到保证

    EdX API Authentication中有一句话,

    OAuth 2.0 is an open standard used by many systems that require secure user authentication

    我开始以为,secure只是个建议,稍后我们会发现,这是个强制要求。

    无论是OAuth2Authentication, SessionAuthentication还是TokenAuthentication,本质都是个认证问题,而认证过程在django中间件里实现,对关注业务逻辑的开发者而言是透明的,而edx的api使用的统一是OAuth2Authentication和SessionAuthentication。

    可选的路线只有一条,开始折腾OAuth2.

    目标定位

    经过一番跟踪和分析,我们发现了edx/edx-oauth2-providerdjango-oauth2-provider与OAuth关系最大

    而他们的关系是edx/edx-oauth2-provider依赖于edx/django-oauth2-provider

    edx/django-oauth2-providerfork自caffeinehit/django-oauth2-provider

    caffeinehit/django-oauth2-provider文档对我们很有助益,

    实验

    定位到这两个关键库,其实接下来的工作就轻松多了。
    首先做些试探性的实验。
    先去/edx/app/edxapp/lms.env.json,在FEATURES里加上"ENABLE_OAUTH2_PROVIDER": true,以及"ENABLE_VIDEO_ABSTRACTION_LAYER_API":true,,而后去admin里获取一个受信任的Client和Access Token,对应的地址分别是是/admin/oauth2_provider/trustedclient//admin/oauth2/accesstoken/add/,过期时间(Expires)可以设得远些,使其不易生效,你也通过设置OAUTH_ID_TOKEN_EXPIRATION来控制失效时间,这个数值衡量的是用户两次登录的时间间隔,好比你要求用户每七天需要登录一次。

    那么激动人心的时刻来啦,我们开始请求接口

    curl -k -H "Authorization: Bearer Your_Access_Token” http://example.com/api/user/v1/accounts/wwj

    {"username": "wwj", "bio": null, "requires_parental_consent": true, "name": "wwj", "country": null, "is_active": true, "profile_image": {"image_url_full": "http://example.com/static/images/default-theme/default-profile_500.de2c6854f1eb.png", "image_url_large": "http://example.com/static/images/default-theme/default-profile_120.33ad4f755071.png", "image_url_medium": "http://example.com/static/images/default-theme/default-profile_50.5fb006f96a15.png", "image_url_small": "http://example.com/static/images/default-theme/default-profile_30.ae6a9ca9b390.png", "has_image": false}, "year_of_birth": null, "level_of_education": null, "goals": null, "language_proficiencies": [], "gender": null, "mailing_address": null, "email": "wwj@example.com", "date_joined": "2015-05-13T09:42:45Z"}
    

    如果你使用httpie(推荐),那么返回的内容将以更易于阅读的形式(缩进高亮),返回给你.之后我们都只要httpie

    http http://example.com/api/user/v1/accounts/wwj "Authorization: Bearer 1a17079824f66bfa5116bd8780b5a119e603a79c" (实际上是header参数)

    {
        "bio": null,
        "country": null,
        "date_joined": "2015-05-13T09:42:45Z",
        "email": "wwj@qq.com",
        "gender": null,
        "goals": null,
        "is_active": true,
        "language_proficiencies": [],
        "level_of_education": null,
        "mailing_address": null,
        "name": "wwj",
        "profile_image": {
            "has_image": false,
            "image_url_full": "http://example.com/static/images/default-theme/default-profile_500.de2c6854f1eb.png",
            "image_url_large": "http://example.com/static/images/default-theme/default-profile_120.33ad4f755071.png",
            "image_url_medium": "http://example.com/static/images/default-theme/default-profile_50.5fb006f96a15.png",
            "image_url_small": "http://example.com/static/images/default-theme/default-profile_30.ae6a9ca9b390.png"
        },
        "requires_parental_consent": true,
        "username": "wwj",
        "year_of_birth": null
    }
    

    再演示一个使用requests的做法

    import requests
    headers = {"Authorization": "bearer 1a17079824f66bfa5116bd8780b5a119e603a79c", "User-Agent": "ChangeMeClient/0.1 by YourUsername"}
    response = requests.get("http://127.0.0.1/api/user/v1/accounts/wwj", headers=headers)
    response.json()
    

    得到

    {u'bio': None,
     u'country': None,
     u'date_joined': u'2015-05-13T09:42:45Z',
     u'email': u'wwj@qq.com',
     u'gender': None,
     u'goals': None,
     u'is_active': True,
     u'language_proficiencies': [],
     u'level_of_education': None,
     u'mailing_address': None,
     u'name': u'wwj',
     u'profile_image': {u'has_image': False,
      u'image_url_full': u'http://127.0.0.1/static/images/default-theme/default-profile_500.de2c6854f1eb.png',
      u'image_url_large': u'http://127.0.0.1/static/images/default-theme/default-profile_120.33ad4f755071.png',
      u'image_url_medium': u'http://127.0.0.1/static/images/default-theme/default-profile_50.5fb006f96a15.png',
      u'image_url_small': u'http://127.0.0.1/static/images/default-theme/default-profile_30.ae6a9ca9b390.png'},
     u'requires_parental_consent': True,
     u'username': u'wwj',
     u'year_of_birth': None}
    

    下边演示请求Access Token的过程

    使用requests

    import requests
    import requests.auth
    client_auth = requests.auth.HTTPBasicAuth('dc107056a5335b3a7c74', '4e3f1fad6e0583fc80d78541f2ca6cfad8a93bed')
    post_data = {"grant_type": "password", "username": "wwj", "password": "wwjtest"}
    response = requests.post("http://127.0.0.1/oauth2/access_token", auth=client_auth, data=post_data)
    response.json()
    

    得到{u'error': u'invalid_request', u'error_description': u'A secure connection is required.'}

    网站需要使用https,nmap查看443端口是close状态。

    配置nginx。

    启用https

    Remember that you should always use HTTPS for all your OAuth 2 requests otherwise you won’t be secured.

    OAuth2要求使用https。所以我们为edx做https支持

    生成证书

    cd /edx/app/nginx/
    mkdir conf
    chown -R 777 conf #好像不大好
    cd conf
    #创建服务器私钥,命令会让你输入一个口令
    openssl genrsa -des3 -out server.key 1024
    #创建签名请求的证书(CSR)
    openssl req -new -key server.key -out server.csr
    #在加载SSL支持的Nginx并使用上述私钥时除去必须的口令:
    cp server.key server.key.org
    openssl rsa -in server.key.org -out server.key
    

    配置nginx

    openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

    /edx/app/nginx/sites-enabled里,将lms复制为lms_https

    sudo diff lms lms_https
    1,3c1
    < upstream lms-backend {
    <             server 127.0.0.1:8000 fail_timeout=0;
    <     }server {
    ---
    > server {
    12,13c10,13
    <
    <     listen 80 default;
    ---
    >     listen 443;
    >     ssl on;
    >     ssl_certificate /edx/app/nginx/conf/server.crt;
    >     ssl_certificate_key /edx/app/nginx/conf/server.key;
    

    /edx/app/nginx/sites-enabled/lms的server结尾里加上

      # Forward to HTTPS if we're an HTTP request...
      if ($http_x_forwarded_proto = "http") {
        set $do_redirect "true";
      }
    
      # Run our actual redirect...
      if ($do_redirect = "true") {
        rewrite ^ https://$host$request_uri? permanent;
      }
    

    重启nginx,https方面的设置就好了,你可以访问,https://example.com 啦

    https下请求Access Token

    import requests
    import requests.auth
    client_auth = requests.auth.HTTPBasicAuth('dc107056a5335b3a7c74', '4e3f1fad6e0583fc80d78541f2ca6cfad8a93bed')
    post_data = {"grant_type": "password", "username": "wwj", "password": "wwjtest"}
    response = requests.post("https://127.0.0.1/oauth2/access_token", auth=client_auth, data=post_data, verify=False)
    response.json()
    

    ``

    ok

    {u'access_token': u'e751c317435986b2a00425ed7a93a789fbcbeccd',
     u'expires_in': 2591999,
     u'scope': u'',
     u'token_type': u'Bearer'}
    

    微信后端

    暂不方便公开源码

    todo

    • 将mobile api相关的请求全部redirect倒https
    • https证书相关

    2015.07.15更新

    开发群里有小伙伴提到在用android客户端去访问服务器时,会出现这样的错误。javax.net.ssl.SSLPeerUnverifiedException: No peer certificate (文后评论中也有人提到)

    这是ssl证书的问题,我此前的做法是不验证。这只是绕过了问题,而没有解决它,在此正面解决它,分以下步骤:

    • 申请ssl证书,我用的是免费的startssl。可参考www.startssl.com
    • 将申请来的证书加入到lms_https里:
    ssl on;
    ssl_certificate /etc/nginx/conf/your-ssl-unified.crt;
    ssl_certificate_key /etc/nginx/conf/your-ssl.key;
    
    • sudo killall -HUP nginx
  • 相关阅读:
    使用vue-cookies操作cookie
    JavaScript如何友好的操作的cookie
    揭秘——TCP的三次握手和四次挥手
    python学习之【第八篇】:Python中的函数基础
    python学习之【第七篇】:Python中的集合及其所具有的方法
    python学习之【第六篇】:Python中的字典及其所具有的方法
    php判断是不是手机端访问
    php判断浏览器还是微信打开
    php无限极分类方法
    javascript/jquery获取url地址栏参数的方法
  • 原文地址:https://www.cnblogs.com/zhaojianwei/p/4667864.html
Copyright © 2020-2023  润新知