django开发之oauth


类别:web开发   

发布时间:2019/09/01 11:59:57   更新时间:2020/07/31 19:50:17


本文讲述的环境:ubuntu、python3、virtualenv。

参考官网:http://django-oauth-toolkit.herokuapp.com/

 

Oauth概念介绍

OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动。

 

用户:被授权者。

用户代理:用户使用的浏览器。

四种授权方式:授权码模式(authorization code)、简化模式(implicit)、密码模式(resource owner password credentials)、客户端模式(client credentials)

客户端、应用(Application):应用代表一个对应认证服务器上的客户端。又叫第三方应用。是由用户打开的。

认证服务器:管理和颁发OAuth2规范支持的所有授权流程所需的访问令牌。

访问令牌:访问受OAuth2保护的资源所需的令牌。它有寿命期限。

授权码(Authorization Code):它用于验证客户端并授予访问令牌的传输。

授权令牌(Authorization Token):授权服务器像客户端发出的令牌,客户在很短时间内可交换为访问令牌。

刷新令牌(Refresh Token):授权服务器可以向客户端发出的令牌,可以交换为全新的访问令牌,而无需重复授权过程。它没有过期时间。

资源服务器:通过遵循OAuth2规范受保护的API提供对其自身资源的访问。就是用drf建立的用restful api交换的资源集。

服务商:认证服务器和资源服务器的提供者。

Oauth基本流程

摘自RFC 6749。

 

1、客户端、用户之间:

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权(四种方式)。

2、客户端、认证服务器之间:

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

3、客户端、资源服务器之间:

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

 

django开发者的应用模式

1、自己提供认证服务器、资源服务器。

如果自己开发的网站(django网站)是服务商,那么就同时自己提供认证服务器和资源服务器。

有了认证服务器、有了资源服务器、还需要注册客户端。

 

2、使用别人的认证服务器,甚至资源服务器。

如果自己开发的网站(django网站)不是服务商,那么就只是一个第三方应用,会向服务商的认证服务器和资源服务器进行交互。

这种场景,比如使用微信登录等(用social-oauth2),本文暂不介绍。本文着力先介绍完整的django实现oauth2流程。

认证(授权)服务器搭建

1、安装

pip install django-oauth-toolkit

pip install django-cors-middleware

pip install django-oauth2_provider

 

 

注意,在env环境下安装。

2、设置setting、urls

 

oauth2_provider添加到您的INSTALLED_APPS

INSTALLED_APPS = (

    ...

    'oauth2_provider',

)

 

如果您需要OAuth2提供商,则需要将以下内容添加到您的urls.py中

urlpatterns = [

    ...

    url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),

]

 

#django-oauth-toolkit配置的地方
OAUTH2_PROVIDER = {
    # this is the list of available scopes
    'SCOPES': {'read': 'Read scope', 'write': 'Write scope'},

    #默认设置地址:pyfield\backend\env\lib\python3.6\site-packages\oauth2_provider\settings.py
    #访问令牌保持有效的秒数。1小时。
    #'ACCESS_TOKEN_EXPIRE_SECONDS': 3600,
    #cleartokens管理命令从数据库中删除刷新令牌之前的秒数。24小时。
    #'REFRESH_TOKEN_EXPIRE_SECONDS': 86400,
}

 

3、同步数据库。

$ python manage.py migrate oauth2_provider

数据库中可以看到相应表象。

 

注册客户端,并让授权服务器产生认证令牌

http://www.yourwebsite.com/o/applications/

(需要登录admin账户后再进行)

 

名称(Name):这是服务器上客户端应用程序的名称,将显示在授权请求页面上,用户可以在该页面上允许/拒绝访问其数据。

客户端id(Client id):自己生成。

客户端密钥(Client secret):自己生成。

客户端类型(Client type):此值影响执行客户端应用程序和授权服务器之间的某些通信的安全级别。这里选择Confidence。

授权代理类型(Authorization grant type):选择Authorization code。

(第一次必须使用Authorization code,后续在改为用Resource owner password-based。否则,第一次授权的时候,不能完成。)

重定向uris(Redirect uris):应用程序必须在使用授权端点之前注册至少一个重定向端点。当客户端指定了一个经过验证的重定向uris时,Authorization Server才会将访问令牌传递给客户端。这里请用:http://django-oauth-toolkit.herokuapp.com/consumer/exchange/

构建授权链接:grants表

授权链接的产生进入网址

http://django-oauth-toolkit.herokuapp.com/consumer/

填写客户id

填写授权网址

http://www.yourwebsite.com/o/authorize/

产生了授权链接,60s有效。

授权应用程序:

点击授权链接,进入授权过程。

点击同意授权。

产生令牌

接下来的两步,继续用官网进行令牌产生,但是不一定能成功。

如果不用官网产生令牌,直接用自己的网站等路,那么需要在这个时候,把Applications表Authorization code改为 Resource owner password-based,即可使用。

交换令牌:access token表

填写客户id

填写客户秘钥

填写令牌网址

http://www.yourwebsite.com/o/token/

 

刷新令牌:refresh token

填写客户id

填写客户秘钥

填写令牌网址

http://www.yourwebsite.com/o/token/

可以不断刷新。

 

撤销令牌,注销动作。

略。     

注意,注销以后,access token失效会从数据库删除。refresh token失效,但不删除。

一般来说,在没有注销的情况下,access过期时间短,refresh过期时间长。如果access过期,那么用refresh重新获取access。

 

清理过期token的django命令:

…/env/bin/python3 …/manage.py cleartokens

 

建设资源服务器

使用drf,即django-rest-framework。

drf应用中的权限

1、TokenHasScope

访问令牌被授权于,视图的required_scope字段,列出的的范围。

class SongView(views.APIView):

    authentication_classes = [OAuth2Authentication]

    permission_classes = [TokenHasScope]

    required_scopes = ['music']

 

如上,在所有music的范围内,TokenHasScope生效。

2、TokenHasReadWriteScope 

首先要认证通过。

基于setting中READ_SCOPE 和 WRITE_SCOPE来进行访问。

当访问get、head、options等安全方法中,需要需要有READ_SCOPE。当访问post、put、patch、delete等方法,需要有WRITE_SCOPE。

 

3、TokenHasResourceScope 

访问令牌被授权于,视图的required_scope字段,列出的的范围。

并且,同时需要基于setting中READ_SCOPE 和 WRITE_SCOPE来进行访问。

4、IsAuthenticatedOrTokenHasScope 

它还允许访问在django中经过身份验证但没有通过OAuth2Authentication类进行身份验证的用户。【一般不用这个。】

可以将它与DjangoModelPermission或DjangoObjectPermission结合使用。

 

5、TokenMatchesOASRequirements

略。

 

登录、退出程序中的使用

 

from oauthlib.oauth2.rfc6749.endpoints.pre_configured import Server

from oauth2_provider.oauth2_validators import OAuth2Validator



import logging

logger = logging.getLogger('django')



def create_token(username, password, client_id, client_secret):

    uri = '/o/token/'

    http_method = 'POST'

    body = r"grant_type=password&username=%s&password=%s&client_id=%s&client_secret=%s" %(username, password, client_id, client_secret)

    #logger.info(body)

    headers = {}

    extra_credentials = None

    [headers, token, status_code] = Server(OAuth2Validator()).create_token_response(uri, http_method, body, headers, extra_credentials)

    return [headers, token, status_code]



def refresh_token(refresh_token, client_id, client_secret):

    uri = '/o/token/'

    http_method = 'POST'

    body = r"refresh_token=%s&client_id=%s&client_secret=%s&grant_type=refresh_token" %(refresh_token, client_id, client_secret)

    headers = {}

    extra_credentials = None

    [headers, token, status_code] = Server(OAuth2Validator()).create_token_response(uri, http_method, body, headers, extra_credentials)

    return [headers, token, status_code]



def revoking_token(token, token_type, client_id, client_secret):

    uri = '/o/token/'

    http_method = 'POST'

    body = r"token=%s&token_type_hint=%s&client_id=%s&client_secret=%s" %(token, token_type, client_id, client_secret)

    headers = {}

    [headers, token, status_code] = Server(OAuth2Validator()).create_revocation_response(uri, http_method, body, headers)

    return [headers, token, status_code]

 

 

from oauth2_provider.models import Application

 

登录

#获取应用

application_obj = Application.objects.get(name='your_app_name')

result = create_token(username, password, application_obj.client_id, application_obj.client_secret)

 

退出

application_obj = Application.objects.get(name=' your_app_name')

result1 = revoking_token(access_token, 'access_token', application_obj.client_id, application_obj.client_secret)

result2 = revoking_token(refresh_token, 'refresh_token', application_obj.client_id, application_obj.client_secret)

 


本文网址:https://www.pyfield.com/blog/?id=23