Django 遇见 "多说"

瞳人


发布于 Jan. 9, 2015, 11:57 a.m.

3 个评论

Django Python 多说


多说评论框, (JWT)本地身份登录, 多说SSO登录, SSO用户同步

Django 是什么

Django 是一个巨好用的 Python Web Framework. 本博客使用 Django 所搭.

多说是什么

多说 是一个第三方评论系统. 详情见其官网.

本文目的

虽然多说有 官方的文档, 也配有 Python SDK 和 Django App. 但是实际使用过程中, 难免有一些问题是文档中没有提及的. 而且我对其 SDK 作了一点修改, 修改的多说 python sdk 所以本文就是把我在使用多说和 Django 时的经验分享给大家. 主要包括:

  1. 多说评论框
  2. (JWT)本地身份登录
  3. 多说SSO登录, SSO用户同步

多说评论框

安装 修改的多说 python sdk

主要修改的功能是:

  1. 添加了评论框中需要的信息: data_thread_key, data_title, data_url. 参数意思详见多说文档, 不做赘述.
  2. 添加了 SSO 登录的支持

使用方法:

  1. 下载代码后, 执行 python setup.py install (可能需要 sudo)
  2. 在 django 的 project 的 settings.py 中添加一些信息:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    ...
    INSTALLED_APPS = (
        ...
        'duoshuo',
        ...
    )
    ...
    # 多说设置
    DUOSHUO_SECRET = '你的多说secret,在多说管理后台 - 设置 - 密钥'
    DUOSHUO_SHORT_NAME = '你的多说short name,比如你注册了example.duoshuo.com,short name就是example'
    # 若不使用 sso, 可以不设置下述两项
    SSO_LOGIN_URL = '你处理登录的url 例如 http://127.0.0.1:8000/accounts/login/'
    SSO_LOGOUT_URL = '你处理登出的url 例如 http://127.0.0.1:8000/accounts/logout/'
    
  3. 然后在你的 django template 中, 头部添加 {% load duoshuo_tags %} 来载入多说 tag. 然后更具需要选择代码:

    1
    2
    3
    4
    5
    6
    7
    <!-- 无SSO 的 评论框 -->
    {% my_duoshuo_comments article.id article.title request.build_absolute_uri %}
    <!-- 有SSO 的 评论框 最后一个参数可选 -->
    <!-- 用来记录登陆后的跳转页面 可以再 view.py 中获取 -->
    {% my_sso_duoshuo_comments 'blog-'|addstr:article.id '博客-'|addstr:article.title request.build_absolute_uri request.path|escape %}
    <!-- SSO 登录框 next 参数为页面跳转使用-->
    {% my_sso_duoshuo_login next|escape %}
    

(JWT)本地身份登录

多说官方文档中关于JWT的说明

其实就是设置 cookie, 那么主要就是对 app 中的 view.py 进行修改. 我们添加如下函数, 用来给 login_view 来返回对应的 response 对象.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import jwt
def set_jwt_and_response(user, response):
    # For duoshuo jwt login
    if user is not None and user.is_authenticated() and user.is_active:
        user_profile = User_Profile.objects.filter(user=user)
        if not user_profile: # 本地的没有 多说 User_Profile
            # 则使用 jwt 来创建一个多说账户
            # For duoshuo jwt login
            duoshuo_jwt_token = None
            username = user.get_full_name()
            if not username:
                username = user.username
            token = {
                "short_name": settings.DUOSHUO_SHORT_NAME,
                "user_key": user.id,
                "name": username
            }
            duoshuo_jwt_token = jwt.encode(token, settings.DUOSHUO_SECRET)
            response.set_cookie('duoshuo_token', duoshuo_jwt_token)
    return response

其中 User_Profile 是我们本地定义的一个 model, 用来存储 User 对应的多说账户信息:

1
2
3
4
5
6
7
8
class User_Profile(models.Model):
    user = models.OneToOneField(User, related_name='profile')
    duoshuo_id = models.IntegerField(default=0)
    token = models.IntegerField(default=0)
    avatar = models.URLField(blank=True, null=True)

    def __unicode__(self):
        return self.user.username

但是, 需要注意的是, 我们要在用户登出的时候删掉这个 cookie:

1
2
3
4
5
def logout_view(request):
    auth.logout(request)
    response = HttpResponseRedirect(reverse('blog:login_view'))
    response.delete_cookie('duoshuo_token')
    return response

多说 SSO 登录, SSO 用户同步

登录的流程可以参考官方文档 多说单点登录说明

主要步骤为:

  1. 根据本文之前所说的 多说使用方法, 在登录模板中使用多说登陆框:

    1
    2
    <!-- SSO 登录框 next 参数为页面跳转使用-->
    {% my_sso_duoshuo_login next|escape %}
    
  2. 点击多说的登录按钮, 成功登录多说之后, 会带 code 参数来 GET 请求你填写的 SSO_LOGIN_URL. 所以我们可以在 login_view 当中处理后续登录过程. 关键部分如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    code = request.GET.get('code', '')
    next_url = decide_next_url(request.GET.get('next', ''))
    if len(code) > 0: # 多说登录
        api = DuoshuoAPI(settings.DUOSHUO_SHORT_NAME, settings.DUOSHUO_SECRET)
        response = api.get_token(code=code)
        if response.has_key('user_key'): # 这个多说账号已经绑定过本地账户了
            user = User.objects.get(pk=int(response['user_key']))
            user.backend = 'django.contrib.auth.backends.ModelBackend'
            auth.login(request, user)
            user_profile = User_Profile.objects.filter(user=user)
            if not user_profile: # 手动绑定了多说账号和本地账号, 但是本地没有对应的 user_profile
                user_profile = User_Profile(user=user,duoshuo_id=int(response['user_id']), avatar=response['avatar_url'])
                user_profile.save()
        else: # 这个多说账户还没有绑定
            access_token = response['access_token']
            user_profile = User_Profile.objects.filter(duoshuo_id=int(response['user_id']))
            if user_profile: #此多说账号在本站已经注册过了, 但是没有绑定, 则先绑定, 然后直接登录
                user = user_profile.first().user
                user.backend = 'django.contrib.auth.backends.ModelBackend'
                auth.login(request, user)
            else: # 此多说账号在本站未注册, 添加一个用户
                print 'api.users.profile user_id %s' % response['user_id']
                response = api.users.profile(user_id=response['user_id'])['response']
                print response
                username = 'duoshuo_%s' % response['user_id']
                while User.objects.filter(username=username).count():
                    username = username + str(random.randrange(1,9)) #如果多说账号用户名和本站用户名重复,就加上随机数字
                tmp_password = ''.join([random.choice('abcdefg&#%^*f') for i in range(8)]) #随机长度8字符做密码
                new_user = User.objects.create_user(username=username, email='user@example.com', password=tmp_password, first_name=response['name']) #默认密码和邮箱,之后让用户修改
                user_profile = User_Profile.objects.get_or_create(user=new_user)[0]
                user_profile.duoshuo_id = int(response['user_id']) #把返回的多说ID存到profile
                user_profile.avatar = response['avatar_url']
                user_profile.save()
    
                user = auth.authenticate(username=username, password=tmp_password)
                auth.login(request, user)
            # SSO 同步多说账户
            sync_sso_duoshuo(access_token, request.user)
        response = HttpResponseRedirect(next_url)
        return set_jwt_and_response(request.user, response)
    

    特别注意 其中 sync_sso_duoshuo 函数是用于将本地用户同步到多说. 可参考官方文档 SSO登录用户同步到多说

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def sync_sso_duoshuo(access_token, user):
        '''将SSO本地用户同步到已有多说账户中
        '''
        url = 'http://api.duoshuo.com/sites/join.json'
        username = user.get_full_name()
        if not username:
            username = user.username
        email = user.email
        if not email:
            email = 'user@example.com'
        params = {
            'short_name': settings.DUOSHUO_SHORT_NAME,
            'secret': settings.DUOSHUO_SECRET,
            'access_token': access_token,
            'user[user_key]': user.id,
            'user[name]': username,
            'user[email]': user.email,
        }
        data = urllib.urlencode(params)
        request = urllib2.Request(url, data=data)
        response = urllib2.urlopen(request)
        result = response.read()
    

哎呦, 不错哦!

3 Comments

刘锐奇 Jan. 18, 2015, 4:52 p.m. | Reply

32个赞

屁啊ok Sept. 19, 2016, 5:24 p.m. | Reply

博主这个评论怎么搞得

瞳人 Sept. 28, 2016, 3:47 p.m. | Reply

主要用了 django-threadedcomments,然后根据 django-fluent-comments 添加了 ajax 的代码。


Leave a Comment:

博客搜索

友情链接

公告

本博客代码已经公布在 Github 上,欢迎交流指正。

QQ 邮箱对 mailgun 不太友好, 所以使用 QQ 邮箱的评论, 可能会无法及时收到邮件。我会尽快寻找其他解决方案的。

本人现在独自使用 linode vps, 20 美元/月, 感觉压力大, 如果有意一起合租, 可以联系我. 在我的任意一篇文章下面留言即可. 关于使用方式, 现在倾向于使用 docker.