2017-01-18 27 views
0

Я знаю, что мы создали объект сеанса с уникальным идентификатором sessionID для ответа клиенту при первом входе пользователя в систему, а затем, когда пользователь запрашивает другие, они будут запрашивать с куки-файлом с этим ID, поэтому сервер может найти объект сеанса тем, что ID, который будет означать, что пользователь зарегистрировался!Как флажок-логин идентифицирует разных пользователей, которые вошли в систему по сеансу подписки?

Но это однопользовательская ситуация. Я обнаружил, что большинство блогов не говорят, если есть много пользователей для управления, если нам нужно создать много сеансов в памяти для каждого пользователя. Я думаю так!

Но когда я ищу исходный код флеш-входа, я не могу найти коллекцию сеансов для поддержки сеанса для каждого пользователя?

def login_user(user, remember=False, force=False, fresh=True): 
    ''' 
    Logs a user in. You should pass the actual user object to this. If the 
    user's `is_active` property is ``False``, they will not be logged in 
    unless `force` is ``True``. 

    This will return ``True`` if the log in attempt succeeds, and ``False`` if 
    it fails (i.e. because the user is inactive). 

    :param user: The user object to log in. 
    :type user: object 
    :param remember: Whether to remember the user after their session expires. 
     Defaults to ``False``. 
    :type remember: bool 
    :param force: If the user is inactive, setting this to ``True`` will log 
     them in regardless. Defaults to ``False``. 
    :type force: bool 
    :param fresh: setting this to ``False`` will log in the user with a session 
     marked as not "fresh". Defaults to ``True``. 
    :type fresh: bool 
    ''' 
    if not force and not user.is_active: 
     return False 

    user_id = getattr(user, current_app.login_manager.id_attribute)() 
    session['user_id'] = user_id 
    session['_fresh'] = fresh 
    session['_id'] = _create_identifier() 

    if remember: 
     session['remember'] = 'set' 

    _request_ctx_stack.top.user = user 
    user_logged_in.send(current_app._get_current_object(), user=_get_user()) 
    return True 

Существует один сеанс, чтобы сохранить пользователя, но что, если придет другой пользователь?

# -*- coding: utf-8 -*- 
""" 
    flask.globals 
    ~~~~~~~~~~~~~ 

    Defines all the global objects that are proxies to the current 
    active context. 

    :copyright: (c) 2011 by Armin Ronacher. 
    :license: BSD, see LICENSE for more details. 
""" 

from functools import partial 
from werkzeug.local import LocalStack, LocalProxy 


def _lookup_req_object(name): 
    top = _request_ctx_stack.top 
    if top is None: 
     raise RuntimeError('working outside of request context') 
    return getattr(top, name) 


def _lookup_app_object(name): 
    top = _app_ctx_stack.top 
    if top is None: 
     raise RuntimeError('working outside of application context') 
    return getattr(top, name) 


def _find_app(): 
    top = _app_ctx_stack.top 
    if top is None: 
     raise RuntimeError('working outside of application context') 
    return top.app 


# context locals 
_request_ctx_stack = LocalStack() 
_app_ctx_stack = LocalStack() 
current_app = LocalProxy(_find_app) 
request = LocalProxy(partial(_lookup_req_object, 'request')) 
session = LocalProxy(partial(_lookup_req_object, 'session')) 
g = LocalProxy(partial(_lookup_app_object, 'g')) 

Я нахожу сессия является глобальной переменной, и является localstack(), но я до сих пор не konw, как это работает?

class Local(object): 
    __slots__ = ('__storage__', '__ident_func__') 

    def __init__(self): 
     object.__setattr__(self, '__storage__', {}) 
     object.__setattr__(self, '__ident_func__', get_ident) 

    def __iter__(self): 
     return iter(self.__storage__.items()) 

    def __call__(self, proxy): 
     """Create a proxy for a name.""" 
     return LocalProxy(self, proxy) 

    def __release_local__(self): 
     self.__storage__.pop(self.__ident_func__(), None) 

    def __getattr__(self, name): 
     try: 
      return self.__storage__[self.__ident_func__()][name] 
     except KeyError: 
      raise AttributeError(name) 

    def __setattr__(self, name, value): 
     ident = self.__ident_func__() 
     storage = self.__storage__ 
     try: 
      storage[ident][name] = value 
     except KeyError: 
      storage[ident] = {name: value} 

    def __delattr__(self, name): 
     try: 
      del self.__storage__[self.__ident_func__()][name] 
     except KeyError: 
      raise AttributeError(name) 

Многие люди говорят, что он будет использовать другой идентификатор потока, чтобы определить, storage[ident][name] = value, но отключить резьб, она хорошо работает для нескольких пользователей? Я просто нахожу, что он использует переменную current_user для идентификации текущего пользователя, но current_user настолько волшебный! Он не поддерживает сборку пользовательской сессии, а только один current_user для решения проблемы! Я не знаю, как это работает?

def login_required(func): 
    ''' 
    If you decorate a view with this, it will ensure that the current user is 
    logged in and authenticated before calling the actual view. (If they are 
    not, it calls the :attr:`LoginManager.unauthorized` callback.) For 
    example:: 

     @app.route('/post') 
     @login_required 
     def post(): 
      pass 

    If there are only certain times you need to require that your user is 
    logged in, you can do so with:: 

     if not current_user.is_authenticated: 
      return current_app.login_manager.unauthorized() 

    ...which is essentially the code that this function adds to your views. 

    It can be convenient to globally turn off authentication when unit testing. 
    To enable this, if the application configuration variable `LOGIN_DISABLED` 
    is set to `True`, this decorator will be ignored. 

    .. Note :: 

     Per `W3 guidelines for CORS preflight requests 
     <http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0>`_, 
     HTTP ``OPTIONS`` requests are exempt from login checks. 

    :param func: The view function to decorate. 
    :type func: function 
    ''' 
    @wraps(func) 
    def decorated_view(*args, **kwargs): 
     if request.method in EXEMPT_METHODS: 
      return func(*args, **kwargs) 
     elif current_app.login_manager._login_disabled: 
      return func(*args, **kwargs) 
     elif not current_user.is_authenticated: 
      return current_app.login_manager.unauthorized() 
     return func(*args, **kwargs) 
    return decorated_view 

Итак, где происходит сравнение текущего пользователя sessionID с cookie с коллекцией сеансов, поддерживаемой сервером? Кто-нибудь может мне помочь?

ответ

0

Я смотрю на flask-login/flask_login/login_manager.py:_load_user()

Я думаю, вы говорите о SESSION_PROTECTION. В этом случае способ перезагрузки пользователя зависит от основных или сильных режимов auth. Если у вас нет защиты сеанса, флажок пытается загрузить пользователя из запроса, заголовка или файлов cookie, если у вас есть обработчики для этого.

class LoginManager(object): 
... 

def _load_user(self): 
    '''Loads user from session or remember_me cookie as applicable''' 
    user_accessed.send(current_app._get_current_object()) 

    # first check SESSION_PROTECTION 
    config = current_app.config 
    if config.get('SESSION_PROTECTION', self.session_protection): 
     deleted = self._session_protection() 
     if deleted: 
      return self.reload_user() 

    # If a remember cookie is set, and the session is not, move the 
    # cookie user ID to the session. 
    # 
    # However, the session may have been set if the user has been 
    # logged out on this request, 'remember' would be set to clear, 
    # so we should check for that and not restore the session. 
    is_missing_user_id = 'user_id' not in session 
    if is_missing_user_id: 
     cookie_name = config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME) 
     header_name = config.get('AUTH_HEADER_NAME', AUTH_HEADER_NAME) 
     has_cookie = (cookie_name in request.cookies and 
         session.get('remember') != 'clear') 
     if has_cookie: 
      return self._load_from_cookie(request.cookies[cookie_name]) 
     elif self.request_callback: 
      return self._load_from_request(request) 
     elif header_name in request.headers: 
      return self._load_from_header(request.headers[header_name]) 

    return self.reload_user() 

def _load_from_request(self, request): 
    user = None 
    if self.request_callback: 
     user = self.request_callback(request) 
    if user is not None: 
     self.reload_user(user=user) 
     app = current_app._get_current_object() 
     user_loaded_from_request.send(app, user=_get_user()) 
    else: 
     self.reload_user() 

Flask передает запрос на ваш обратный вызов, если он представлен. Фланец-логин имеет хорошее example(Custom Login using Request Loader), как вы можете загрузить пользователя из запроса.

@login_manager.request_loader 
def load_user_from_request(request): 

# first, try to login using the api_key url arg 
api_key = request.args.get('api_key') 
if api_key: 
    user = User.query.filter_by(api_key=api_key).first() 
    if user: 
     return user 

# next, try to login using Basic Auth 
api_key = request.headers.get('Authorization') 
if api_key: 
    api_key = api_key.replace('Basic ', '', 1) 
    try: 
     api_key = base64.b64decode(api_key) 
    except TypeError: 
     pass 
    user = User.query.filter_by(api_key=api_key).first() 
    if user: 
     return user 

# finally, return None if both methods did not login the user 
return None 

Api_key может быть назначен, когда клиент будет авторизован через бэкэнд для разных логинов с одной физической машины.

+0

От запроса или печенья? Но это происходит от клиента, я думаю, что сервер запоминает пользователя, записывая его в память с коллекциями сеансов пользователя. Поэтому, если пользователь зарегистрировался, будет один сеанс в коллекциях, содержащий пользователя, тогда может быть сохранен статус пользователя в журнале , Вы говорите, загрузите пользователя из запроса, я не понимаю? – kitian

+0

@kitian Я расширил свой ответ – theodor