Предлагаемый метод для пересылки параметров POST через декоратор login_required? - PullRequest
2 голосов
/ 19 февраля 2009

У меня сейчас проблема с тем, что когда я использую декоратор login_required из django.contrib.auth.decorators в любом из моих представлений, мои параметры POST не приходят в защищенное представление, когда декоратор действительно перенаправляет (на страницу входа в систему). ) и вернитесь к защищенному виду. Предложения о том, как обойти это (желательно поддерживая удобство декоратора login_required и метода POST), приветствуются!

Эта страница представляется оспариваемым билетом Django по этому вопросу. Хотя ошибка / расширение было сформулировано в терминах логики шаблонов и представлений вместо того, чтобы просто сделать параметры доступными для представления, что является моей проблемой.

Ответы [ 2 ]

2 голосов
/ 14 марта 2009

Я разработал следующее решение, используя сеансы, которые я считаю приемлемыми. Работать с перенаправлениями и заменой представлений довольно сложно, и этот метод, по-видимому, является наилучшим балансом, если не возиться с платформой и не бороться с протоколом HTTP при получении требуемой функциональности. Негативным аспектом этого метода является дополнительная работа, необходимая в каждом защищенном представлении для проверки переменных сеанса.

  1. Создайте собственный декоратор (login_required2, ниже), который возвращает запрошенное представление, если пользователь прошел аутентификацию, и представление входа в систему проекта в противном случае.
  2. Вид входа в систему:
    1. Сохраняет исходные параметры POST в переменной сеанса.
    2. Сохраняет оригинал HTTP_REFERER в переменной сеанса
    3. Если пользователь правильно аутентифицируется, возвращает представление, соответствующее запрошенному пути (запрошенный путь остается идентичным на протяжении всего процесса входа в систему и идентичен пути, запрошенному пользователем, когда ему первоначально передали представление входа в систему вместо этого.)
  3. Любые защищенные представления должны проверять переменные сеанса, прежде чем они будут использовать POST или META['HTTP_REFERER']

Код следует:

def login_view(request):    
    from django.conf import settings
    from django.core.urlresolvers import resolve

    USERNAME_FIELD_KEY = 'username'
    PASSWORD_FIELD_KEY = 'password'

    message = '' #A message to display to the user
    error_message = '' #An error message to display to the user

    #If the request's path is not the login URL, the user did not explicitly request 
    # the login page and we assume this view is protecting another.
    protecting_a_view = request.path != settings.LOGIN_URL

    post_params_present = bool(request.POST)

    #Any POST with username and password is considered a login attempt, regardless off what other POST parameters there may be
    login_attempt = request.POST and request.POST.has_key(USERNAME_FIELD_KEY) and request.POST.has_key(PASSWORD_FIELD_KEY)

    if protecting_a_view:
        message = 'You must login for access.'
        if not request.session.has_key(ACTUAL_REFERER_KEY):
            #Store the HTTP_REFERER if not already
            request.session[ACTUAL_REFERER_KEY] = request.META.get(HTTP_REFERER_KEY)

    if protecting_a_view and post_params_present and not login_attempt: 
        #Store the POST parameters for the protected view
        request.session[FORWARDED_POST_PARAMS_KEY] = request.POST

    if login_attempt:
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data[USERNAME_FIELD_KEY]
            password = form.cleaned_data[PASSWORD_FIELD_KEY]
            user = auth.authenticate(username=username, password=password)
            if user is not None:
                if user.is_active:
                    auth.login(request, user)
                    if protecting_a_view:
                        actual_view, actual_args, actual_kwargs = resolve(request.path) #request.path refers to the protected view
                        return actual_view(request, *actual_args, **actual_kwargs)
                    else:
                        HttpResponseRedirect('/')
                else:
                    message = 'That account is inactive.'
            else:
                error_message = 'That username or password is incorrect.'
    else:
        form = LoginForm()

    context_dict = {
        'form': form,
        'message': message,
        'error_message': error_message,
    }
    return render_to_response2('my_app/login.html', context_dict)

@login_required2
def protected_view(request):
    post_params = {}

    if request.POST:
        post_params = request.POST
    elif request.session.has_key(FORWARDED_POST_PARAMS_KEY):
        post_params = request.session[FORWARDED_POST_PARAMS_KEY]
        del request.session[FORWARDED_POST_PARAMS_KEY]
    if post_params:
        #Process post_params as if it were request.POST here:
        pass

    #assuming this view ends with a redirect.  Otherwise could render view normally
    if request.session.has_key(ACTUAL_REFERER_KEY):
        redirect_location = request.session.get(ACTUAL_REFERER_KEY)
    elif request.META.get(HTTP_REFERER_KEY) != request.path:
        redirect_location = request.META.get(HTTP_REFERER_KEY)
    else:
        redirect_location = ROOT_PATH
    return HttpResponseRedirect(redirect_location)

def login_required2(view_func):
    """
    A decorator that checks if the request has an authenticated user.
    If so it passes the request to the view.
    Otherwise, it passes the request to the login view, which is responsible
    for recognizing that the request was originally for another page and forwarding
    state along (GET, POST).

    See django.contrib.auth.decorators for how Django's auth decorators mesh 
    using _CheckLogin.  This decorator bypasses that for my ease of creation.
    """
    def login_required_decoration(request, *args, **kwargs):
        if request.user.is_authenticated():
            return view_func(request, *args, **kwargs)
        else:
            from django.conf import settings
            from django.core.urlresolvers import resolve

            login_url = settings.LOGIN_URL
            login_view, login_args, login_kwargs = resolve(login_url)
            #Here the user gets a login view instad of the view they requested
            return login_view(request, *login_args, **login_kwargs)
    return login_required_decoration
1 голос
/ 19 февраля 2009

Нет простого способа сделать это, не говоря уже о том, что вы хотите использовать декоратор login_required. Вы можете сделать свое собственное представление, которое проверяет метод is_authenticated и делает правильные вещи, например, сериализацию данных POST и передачу, но это очень подвержено ошибкам.

Простой обходной путь - изменить форму для выполнения GET вместо POST.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...