Django集成oauth2实现第三方授权登录

       最近在项目中需要用到oauth2来进行第三方的授权服务,在网上了解到django-oauth-toolkit已经集成了oauth2方法,将自己心得记录下来.

        首先要学习什么是oauth2,阮老师的介绍应该算是很详细了,网址为:理解oauth2.

        我用到的是oauth2中的密码模式 resource owner password credentials来进行访问授权的,与授权码模式有所不同,不过大致原理相似,可以自行尝试.

============================================================================

      用oauth中的password方法来获取令牌访问整体流程


(A)用户向客户端提供用户名和密码。

(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。

(C)认证服务器确认无误后,向客户端提供访问令牌。

在django-oauth-toolkit中获取令牌方法如下:
curl -X POST -d "grant_type=password&username=&password=" -u":" http://localhost:8000/o/token/

获取到token等信息,信息内容如下:
{
    "access_token": "", #获得的令牌
    "token_type": "Bearer", #表示令牌类型,大小写不敏感,可以是bearer类型或mac类型
    "expires_in": 36000, #过期时间
    "refresh_token": "", #刷新令牌,用来获取下一次的访问令牌,
    "scope": "read write groups" #表示权限范围,可选,设置时可以直接在上面获取token的参数中加入scope即可
}

即可用token进行访问:
curl -H "Authorization: Bearer " http://localhost:8000/api/

接下来用ajax以及跨域的cors方法来进行前端处理

============================================================================ 具体实现


1. 用cors实现跨域,需要在请求头中加入Origin: http://目标地址.com.同时,服务器端里也需要在响应头里加入
Access-Control-Allow-Origin,值是指向允许跨域访问的客户端地址,或者为*,表示允许所有地址的客户端进行
资源访问.

2. 在django中集成oauth2,可以使用一个方便的第三方库,django-oauth-toolkit,它实现了将oauth2集成到你的
web框架中.同时,需要安装django-cors-middleware,这个中间件是为了辅助帮助我们通过cors进行跨域访问,而不
需我们手动配置response的头信息,以及客户端发送的request头信息.

3. pip install 安装好这两个之后,在installed_apps里注册'oauth2_provider'以及'corsheaders'.同时,在你的
urls.py里加入认证路由,例如:url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
包括在中间件中加入'corsheaders.middleware.CorsMiddleware'.在setting.py中还需要设置cors允许接受
请求的范围,CORS_ORIGIN_ALLOW_ALL = True,这里设置的是允许所有的请求访问.

3.1. 还需要一个设置,用于通过访问令牌辅助Django验证用户的身份。即通过令牌就能识别用户,并可以登录.
具体需求看扩展中第二点.

4. 首选需要在服务器上注册并登陆一个用户,因为只有在登陆状态下,才允许创建新的授权应用程序,url指向
localhost:port/o/applications/然后根据信息创建注册程序,这个程序相当于第三方client在服务器上的注册
信息,为了以后服务器向该client提供授权,否则无法确认是哪个客户端发来的请求.

5. 我们先在终端中输入curl -X POST -d "grant_type=password&username=&password=" -u":" http://localhost:8000/o/token/.这里面的grant_type表示我们以密码模式来访问,
username和password是用户在服务器上的注册用户,client是为了获取服务器授权,得到用户信息,还有个参数是scope,
这个是一个权限设置,可以是read,write等等.注意是否返回一个键值对形式的json列表.里面有access_token,token_type,
expires_in,refresh_token,scope等信息.

6. 接下来用得到的acces_token去访问服务器上的资源curl -H "Authorization: Bearer " http://localhost:8000/api/  即可得到想要的数据.

扩展:
1. 之前项目中用的django是1.8.19,如果直接安装django-oauth-toolkit会无法使用,因为最新版django-oauth-toolkit已经
不在支持django1.8,我使用的是0.12.0版本,具体支持版本请移步 点击打开链接

2. 在服务器端临时设置了三个view,代码如下:

	class ApiEndpoint(ProtectedResourceView):
    	    def get(self, request, *args, **kwargs):
    		return HttpResponse('this is oauth2')

	def index(request):
            return HttpResponse('hello world!')

	@login_required()
	def secret_tag(request):
            return HttpResponse('this is secret!',status=200)
    一般情况下,当跨域访问index时,会被拒绝,实质是被浏览器阻挡.如果上面的设置成功,在主机上9000端口的客户端访问8000端口的服务器,则可以正常访问index

    当设置好oauth后可以访问ApiEndpoint这个视图函数,该函数继承自ProtectedResourceView,这是最快捷的方法用于保护服务器的api函数不被任何人随意获取到资源.如果是跨域访问这个资源,会被拒绝.但是如果获取到了access_token,用access_token来访问ApiEndpoint即可正常返回结果

    第三个函数是需要用户登录后才可以看到的函数secret_tag.即使你完成了跨域访问,完成oauth2的设置,可以访问ApiEndpoint了,但是依然无法访问secret_tag,原因是这个函数是受django认证机制保护的,必须要用户登录后才可以访问.但是此时客户端是已经得到了用户的授权,如何访问就需要一个认证中间件来进行配置.配置信息如下:

    在setting.py中加入如下信息:

    	AUTHENTICATION_BACKENDS = (
		    'oauth2_provider.backends.OAuth2Backend',
		    # 上面这个就可以,不过建议加上下面这个,否则最后无法登录服务器上django后台admin
		    'django.contrib.auth.backends.ModelBackend'
		    '...',
		)

同时,在middleware中加入如下中间件:

		'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    	        'oauth2_provider.middleware.OAuth2TokenMiddleware',
    这两个有先后顺序,区别在于:
    If you put the OAuth2 backend after the AuthenticationMiddleware and request.user is valid, the backend will do nothing; if request.user is the Anonymous user it will try to authenticate the user using the OAuth2 access token.

If you put the OAuth2 backend before AuthenticationMiddleware, or AuthenticationMiddleware is not used at all, it will try to authenticate user with the OAuth2 access token and set request.user and request._cached_user fields so that AuthenticationMiddleware (when active) will not try to get user from the session.

If you use SessionAuthenticationMiddleware, be sure it appears before OAuth2TokenMiddleware. However SessionAuthenticationMiddleware is NOT required for using django-oauth-toolkit.

这样处理后的效果就是,利用curl -H "Authorization: Bearer " http://localhost:8000/secret_tag/,通过access_token就可以直接访问到django中需要登录后才可以访问的资源,oauth在后台已经帮你进行登录,debug调试发现你用access_token去访问这个secret_tag时,会自动将request中的user字段设置为你授权登录的用户,而不是Anonymous user.如果你用令牌去访问ApiEndpoint,也可以访问,但是不会自动设置request中的user为授权你登录 的user,你仍然是以Anonymous user来访问的,只不过你是被授权访问的..不过,换个角度思考一下, 能否可以在服务器上通过获得的access_token来获取到注册的用户.通过查看oauth源码中token模型,发现与django中的user模型是关联关系,即可以通过access_token来得到对应的User用户,源码如下:

                        class AccessToken(models.Model):
			    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
                                         on_delete=models.CASCADE)
                            token = models.CharField(max_length=255, unique=True)
			    application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL,
			                 on_delete=models.CASCADE)
			    expires = models.DateTimeField()
			    scope = models.TextField(blank=True)
			    ...

为此,可以在服务器上修改ApiEndpoint,获取授权用户,从而进行更多操作,如下:

	                class ApiEndpoint(ProtectedResourceView):
			    def get(self, request, *args, **kwargs):
			        token_id=AccessToken.objects.get(token=request.GET['access_token'])
			        user=token_id.user
			        if user:
			            email = user.email
			            return HttpResponse(email)
			        return HttpResponse('you can access ,but no userinfo')

用此方法可以直接确认到该用户信息(这都是在服务器上进行的,不是客户端).通过debug调试发现一个有趣的事情, 当你用access_token来访问ApiEndpoint时,虽然是用匿名用户访问的,但是,在request中,oauth-toolkit会加入一个resource_owner字段,该字段指向该令牌的所有者user,也就是说我们可以用request.resource_owner来获取到user!,效果跟request.user一样.这样一来可以进行一次优化,不用访问数据库资源也可以确认用户,修改代码如下:

			 class ApiEndpoint(ProtectedResourceView):	    
			    def get(self, request, *args, **kwargs):
			        user = request.resource_owner
			        if user:
			            email = user.email
			            return HttpResponse(email)
			        return HttpResponse('you can access ,but no userinfo')

3. 客户端方面,用了一个前端页面来模拟客户端的请求授权,用到了ajax,跨域方法用cors来实现,而不是JSONP来进行的跨域请 求.前端代码由于是花一天左右时间学习并完成的,只是实现了访问获取的流程.对于client获取到资源后该如何使用,获取到的access_token以及过期时间,refresh_token如何保存等等,需要写专门的view来进行业务处理.前端代码如下:

	
	
	
	    
	    client
	    
	
	
	    

This is http_webapp, a client

you can login or regist

login regist

if you don't do that ,you can also tell us your username and password to access fk server

username:
password:

url:








你可能感兴趣的:(Django学习,Python)