【问题标题】:Are sessions needed for python-social-authpython-social-auth 是否需要会话
【发布时间】:2015-01-05 14:08:25
【问题描述】:

我正在构建一个带有 API 后端(使用 DRF 构建)和 angularjs 客户端的 django 应用程序。我的目标是使用 JWT 代替会话完全解耦服务器和客户端。我正在尝试将python-social-auth(PSA) 与django-rest-framework-jwt(DRFJWT) 集成,所以我的目标是让身份验证流向这个:

用户通过 Angular 客户端使用电子邮件/facebook 登录 -> 客户端将表单发布到 PSA 的 url -> PSA 登录/创建用户 ->[!] DRFJWT 创建令牌,然后将其发送回客户端 -> 客户端将令牌存储在本地存储中然后在每个请求中使用令牌

[!]:这是我目前正在苦苦挣扎的地方。我的想法是我可以像这样修改PSA中的do_complete方法

from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler


def do_complete(backend, login, user=None, redirect_name='next',
            *args, **kwargs):
  # pop redirect value before the session is trashed on login()
  data = backend.strategy.request_data()
  redirect_value = backend.strategy.session_get(redirect_name, '') or \
                 data.get(redirect_name, '')

  is_authenticated = user_is_authenticated(user)
  user = is_authenticated and user or None

  partial = partial_pipeline_data(backend, user, *args, **kwargs)
  if partial:
      xargs, xkwargs = partial
      user = backend.continue_pipeline(*xargs, **xkwargs)
  else:
      user = backend.complete(user=user, *args, **kwargs)

  if user_is_active(user):
      # catch is_new/social_user in case login() resets the instance
      is_new = getattr(user, 'is_new', False)
      social_user = user.social_user
      login(backend, user, social_user)

  payload = jwt_payload_handler(user)
  return { 'token': jwt_encode_handler(payload) }

这是我想要完成的唯一方法吗?

我还想知道,从最佳实践的角度来看,是否可以使用会话来管理管道和 JWT 进行身份验证?

【问题讨论】:

  • login(backend, user, social_user) 调用 Django login 方法(来自 django.contrib.auth 应用程序),该方法使用用户会话来跟踪用户状态。你我可能会将SOCIAL_AUTH_LOGIN_REDIRECT_URL 设置为/auth-payload,然后在那个视图中我会返回当前用户的有效负载。

标签: python django jwt python-social-auth


【解决方案1】:

我还使用python-social-authdjango-rest-framework-jwt 进行用户身份验证。

我能够将两个身份验证系统集成在一起的方法是创建一个自定义视图,该视图接受 oAuth 提供者提供的“access_token”并尝试用它创建一个新用户。创建用户后,我不会返回经过身份验证的用户/会话,而是返回 JWT 令牌。

下面的代码sn-ps解释解决办法。

后端

在我的 views.py 文件中,我包含以下内容:

@psa()
def auth_by_token(request, backend):
    """Decorator that creates/authenticates a user with an access_token"""
    token = request.DATA.get('access_token')
    user = request.user
    user = request.backend.do_auth(
            access_token=request.DATA.get('access_token')
        )
    if user:
        return user
    else:
        return None

class FacebookView(views.APIView):
    """View to authenticate users through Facebook."""

    permission_classes = (permissions.AllowAny,)

    def post(self, request, format=None):
        auth_token = request.DATA.get('access_token', None)
        backend = request.DATA.get('backend', None)
        if auth_token and backend:
            try:
                # Try to authenticate the user using python-social-auth
                user = auth_by_token(request, backend)
            except Exception,e:
                return Response({
                        'status': 'Bad request',
                        'message': 'Could not authenticate with the provided token.'
                    }, status=status.HTTP_400_BAD_REQUEST)
            if user:
                if not user.is_active:
                    return Response({
                        'status': 'Unauthorized',
                        'message': 'The user account is disabled.'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # This is the part that differs from the normal python-social-auth implementation.
                # Return the JWT instead.

                # Get the JWT payload for the user.
                payload = jwt_payload_handler(user)

                # Include original issued at time for a brand new token,
                # to allow token refresh
                if api_settings.JWT_ALLOW_REFRESH:
                    payload['orig_iat'] = timegm(
                        datetime.utcnow().utctimetuple()
                    )

                # Create the response object with the JWT payload.
                response_data = {
                    'token': jwt_encode_handler(payload)
                }

                return Response(response_data)
        else:
            return Response({
                    'status': 'Bad request',
                    'message': 'Authentication could not be performed with received data.'
            }, status=status.HTTP_400_BAD_REQUEST)

在我的 urls.py 中,我包含了以下路线:

urlpatterns = patterns('',
    ...
    url(r'^api/v1/auth/facebook/', FacebookView.as_view()),
    ...
)

前端

现在后端身份验证已连接,您可以使用任何前端库发送 access_token 并对用户进行身份验证。在我的例子中,我使用了 AngularJS

在控制器文件中,我这样调用 API:

/**
* This function gets called after successfully getting the access_token from Facebook's API.
*/
function successLoginFbFn(response) {
    var deferred = $q.defer();
    $http.post('/api/v1/auth/facebook/', {
        "access_token": response.authResponse.accessToken, 
        "backend": "facebook"
    }).success(function(response, status, headers, config) {
        // Success
        if (response.token) {
            // Save the token to localStorage and redirect the user to the front-page.
            Authentication.setToken(response.token);
            window.location = '/';
        }
        deferred.resolve(response, status, headers, config);
    }).error(function(response, status, headers, config) {
        // Error
        console.error('Authentication error.');
        deferred.reject(response, status, headers, config);
    });
}

通过这种方法,您可以混合使用这两个插件。所有发送的令牌都将来自 django-rest-framework-jwt,即使用户仍然可以使用 Facebook、Google、Twitter 等网站提供的令牌进行身份验证。

我只展示了通过 Facebook 进行身份验证的方法,但是您可以对其他提供商采用类似的方法。

【讨论】:

    【解决方案2】:

    不,您不需要将会话(标准 Django 登录系统)与 python-social-auth 一起使用。使 JWT 和 PSA 协同工作所需的是 DRF。

    这是我的解决方案:

    我使用标准 PSA 的 url 来发出请求太社交 /login/(?P<backend>[^/]+)/$,更改了 urls.py 中的 url 以匹配从 Facebook/Twitter 到我自己的重定向。

    url(r'^complete/(?P<backend>[^/]+)/$', views.SocialAuthViewComplete.as_view()),
    

    使用 API 的目的是在 PSA 正在执行的请求中访问用户数据。如果您在 DEFAULT_AUTHENTICATION_CLASSES 中有 JWT 身份验证,DRF 允许您这样做

    REST_FRAMEWORK = {
              'DEFAULT_AUTHENTICATION_CLASSES': (
                  'rest_framework.authentication.SessionAuthentication',
                  'rest_framework.authentication.TokenAuthentication',
                  'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),}
    

    views.py

    from social.apps.django_app.views import complete
    
    class SocialAuthViewComplete(APIView):
        permission_classes = ()
    
        def post(self, request, backend, *args, **kwargs):
            try:
                #Wrap up  PSA's `complete` method.    
                authentication = complete(request, backend, *args, **kwargs)
            except Exception, e:
                exc = {
                    'error': str(e)
                }
                return Response(exc, status=status.HTTP_400_BAD_REQUEST)
            return Response({'data': authentication}, status=status.HTTP_202_ACCEPTED)
    

    然后我修改了 PSA 中的do_complete 方法:

    def do_complete(backend, login, user=None, redirect_name='next',
                    *args, **kwargs):
        # pop redirect value before the session is trashed on login()
        data = backend.strategy.request_data()
        redirect_value = backend.strategy.session_get(redirect_name, '') or \
                         data.get(redirect_name, '')
    
        is_authenticated = user_is_authenticated(user)
        user = is_authenticated and user or None
    
        partial = partial_pipeline_data(backend, user, *args, **kwargs)
        if partial:
            xargs, xkwargs = partial
            user = backend.continue_pipeline(*xargs, **xkwargs)
        else:
            user = backend.complete(user=user, *args, **kwargs)
    
        user_model = backend.strategy.storage.user.user_model()
        if user and not isinstance(user, user_model):
            return user
    
        if is_authenticated:
            if not user:
                information =  'setting_url(backend, redirect_value, LOGIN_REDIRECT_URL'
            else:
                information =  'setting_url(backend, redirect_value, NEW_ASSOCIATION_REDIRECT_URL,LOGIN_REDIRECT_URL'
        elif user:
            # Get the JWT payload for the user.
            payload = jwt_payload_handler(user)
    
            if user_is_active(user):
                is_new = getattr(user, 'is_new', False)
                if is_new:
                    information = 'setting_url(backend, NEW_USER_REDIRECT_URL, redirect_value, LOGIN_REDIRECT_URL'
                else:
                    information = 'setting_url(backend, redirect_value, LOGIN_REDIRECT_URL'
            else:
                return Response({
                            'status': 'Unauthorized',
                            'message': 'The user account is disabled.'
                        }, status=status.HTTP_401_UNAUTHORIZED)
        else:
            information = 'setting_url(backend, LOGIN_ERROR_URL, LOGIN_URL'
    
    
        return { 'an information i may use in future': information,
                 'token': jwt_encode_handler(payload) # Create the response object with the JWT payload.
        }
    

    尝试了管道和用户关联,它工作正常。 如果您需要它与 JWT 一起使用,您也可以随时修改 PSA 的另一种方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      • 2015-07-31
      • 2011-01-22
      • 1970-01-01
      • 2017-05-17
      • 1970-01-01
      相关资源
      最近更新 更多