webpy使用筆記(二) session/sessionid的使用


webpy使用筆記(二) session的使用

  webpy使用系列之session的使用,雖然工作中使用的是django,但是自己並不喜歡那種大而全的東西~什么都給你准備好了,自己好像一個機器人一樣趕着重復的基本工作,從在學校時候就養成了追究原理的習慣,從而有了這篇session的使用和說明。

  PS:其實有些總結的東西挺好的,想分享給大家看,而不是枯燥的代碼,這東西說實話對其他人用處不大,但都被移除首頁了~~

webpy中的session

  下面為官方的例子,用session來存儲頁面訪問的次數,從而實現對訪問次數的記錄。

  (PS,這里記錄是針對一個客戶端來說的訪問次數,而不是官方文檔說的統計有多少人正在使用session,因為每個客戶端的session並不相同,服務器會根據不同的sessionid來區分不同的客戶端的session)

  需要注意的是,官方說明在調試情況下,session並不能正常的運行,所以需要在非調試摸下測試,那么就有了下面的這個例子。

import web
#非調試模式 web.config.debug
= False urls = ( "/count", "count", "/reset", "reset" ) app = web.application(urls, locals()) session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'count': 0}) class count: def GET(self): session.count += 1 return str(session.count) class reset: def GET(self): session.kill() return "" if __name__ == "__main__": app.run()

   在官方文檔中,對上述debug模式的現象給出了這樣的解釋:

  session與調試模試下的重調用相沖突(有點類似firefox下著名的Firebug插件,使用Firebug插件分析網頁時,會在火狐瀏覽器之外單獨對該網頁發起請求,所以相當於同時訪問該網頁兩次)

  為了解決上述問題,官方給出了進一步的解決方法,如下

import web
urls = ("/", "hello")

app = web.application(urls, globals())

if web.config.get('_session') is None:
    session = web.session.Session(app, web.session.DiskStore('sessions'), {'count': 0})
    web.config._session = session
else:
    session = web.config._session

class hello:
   def GET(self):
       print 'session', session
       session.count += 1
       return 'Hello, %s!' % session.count

if __name__ == "__main__":
   app.run()

  由於web.session.Session會重載兩次,但是在上面的_session並不會重載兩次,因為上面多了一個判斷_session是否存在於web.config中。

  其實,在web.py文件中,定義了config,而Storage在下面的圖中並沒有特殊的結果,像字典一樣~

#web.py
config = storage()

#utils.py
storage = Storage

在webpy的子程序中使用session

  雖然官方文檔中提到,只能在主程序中使用session,但是通過添加__init__.py可以條用到該頁面的session,也就是說一樣使用session。

  官方給出的方法更加合理化一點,通過應用處理器,加載鈎子(loadhooks)

  在webpy中,應用處理器為app.add_processor(my_processor),下面的代碼添加到上述的完整例子中,可以再處理請求前和處理請求后分別條用my_loadhook()和my_unloadhook()。

def my_loadhook():
    print "my load hook"

def my_unloadhook():
    print "my unload hook"

app.add_processor(web.loadhook(my_loadhook))
app.add_processor(web.unloadhook(my_unloadhook))

 

結果如下,我在處理中打印了session:

  從而,可以再web.loadhook()中加載session信息,在處理之前從web.ctx.session中獲取session了,甚至可以在應用處理器中添加認證等操作。

#main.py
def session_hook():
  web.ctx.session = session
app.add_processor(web.loadhook(session_hook))

#views.py
class edit:
    def GET(self):
        try:
            session = web.ctx.session
            username = session.username
            if not username:
                return web.redirect('/login')
        except Exception as e:
            return web.redirect('/login')
        return render_template('edit.html')

sessionid

  對於服務器來說,怎樣才能區分不同客戶端呢,怎樣才能區分不同客戶端的session呢?

  是通過sessionid來實現的,最初我還傻傻的分不清session和cookie,以及不同用戶之間的信息室如何分配的!

  

  如上圖,是生成sessionid的代碼段,其中包含了隨機數、時間、ip以及秘鑰。

  在客戶端訪問服務器時,服務器會根據上述信息來計算一個針對客戶端唯一的sessionid,並通過cookie保存在客戶端中。

  客戶端用cookie保存了sessionID,當我們請求服務器的時候,會把這個sessionID一起發給服務器,服務器會到內存中搜索對應的sessionID,如果找到了對應的 sessionID,說明我們處於登錄狀態,有相應的權限;如果沒有找到對應的sessionID,這說明:要么是我們把瀏覽器關掉了(后面會說明為什 么),要么session超時了(沒有請求服務器超過20分鍾),session被服務器清除了,則服務器會給你分配一個新的sessionID。你得重新登錄並把這個新的sessionID保存在cookie中。 

session的結構

  上面提到了session在webpy中式一種dict的方式存儲,

class Session(object):
    """Session management for web.py
    """
    __slots__ = [
        "store", "_initializer", "_last_cleanup_time", "_config", "_data", 
        "__getitem__", "__setitem__", "__delitem__"
    ]

    def __init__(self, app, store, initializer=None):
        self.store = store
        self._initializer = initializer
        self._last_cleanup_time = 0
        self._config = utils.storage(web.config.session_parameters)
        self._data = utils.threadeddict()
        
        self.__getitem__ = self._data.__getitem__
        self.__setitem__ = self._data.__setitem__
        self.__delitem__ = self._data.__delitem__

        if app:
            app.add_processor(self._processor)

    def __contains__(self, name):
        return name in self._data

    def __getattr__(self, name):
        return getattr(self._data, name)
    
    def __setattr__(self, name, value):
        if name in self.__slots__:
            object.__setattr__(self, name, value)
        else:
            setattr(self._data, name, value)
        
    def __delattr__(self, name):
        delattr(self._data, name)

    def _processor(self, handler):
        """Application processor to setup session for every request"""
        self._cleanup()
        self._load()

        try:
            return handler()
        finally:
            self._save()

    def _load(self):
        """Load the session from the store, by the id from cookie"""
        cookie_name = self._config.cookie_name
        cookie_domain = self._config.cookie_domain
        cookie_path = self._config.cookie_path
        httponly = self._config.httponly
        self.session_id = web.cookies().get(cookie_name)

        # protection against session_id tampering
        if self.session_id and not self._valid_session_id(self.session_id):
            self.session_id = None

        self._check_expiry()
        if self.session_id:
            d = self.store[self.session_id]
            self.update(d)
            self._validate_ip()
        
        if not self.session_id:
            self.session_id = self._generate_session_id()

            if self._initializer:
                if isinstance(self._initializer, dict):
                    self.update(deepcopy(self._initializer))
                elif hasattr(self._initializer, '__call__'):
                    self._initializer()
 
        self.ip = web.ctx.ip

    def _check_expiry(self):
        # check for expiry
        if self.session_id and self.session_id not in self.store:
            if self._config.ignore_expiry:
                self.session_id = None
            else:
                return self.expired()

    def _validate_ip(self):
        # check for change of IP
        if self.session_id and self.get('ip', None) != web.ctx.ip:
            if not self._config.ignore_change_ip:
               return self.expired() 
    
    def _save(self):
        if not self.get('_killed'):
            self._setcookie(self.session_id)
            self.store[self.session_id] = dict(self._data)
        else:
            self._setcookie(self.session_id, expires=-1)
            
    def _setcookie(self, session_id, expires='', **kw):
        cookie_name = self._config.cookie_name
        cookie_domain = self._config.cookie_domain
        cookie_path = self._config.cookie_path
        httponly = self._config.httponly
        secure = self._config.secure
        web.setcookie(cookie_name, session_id, expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, path=cookie_path)
    
    def _generate_session_id(self):
        """Generate a random id for session"""

        while True:
            rand = os.urandom(16)
            now = time.time()
            secret_key = self._config.secret_key
            session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key))
            session_id = session_id.hexdigest()
            if session_id not in self.store:
                break
        return session_id

    def _valid_session_id(self, session_id):
        rx = utils.re_compile('^[0-9a-fA-F]+$')
        return rx.match(session_id)
        
    def _cleanup(self):
        """Cleanup the stored sessions"""
        current_time = time.time()
        timeout = self._config.timeout
        if current_time - self._last_cleanup_time > timeout:
            self.store.cleanup(timeout)
            self._last_cleanup_time = current_time

    def expired(self):
        """Called when an expired session is atime"""
        self._killed = True
        self._save()
        raise SessionExpired(self._config.expired_message)
 
    def kill(self):
        """Kill the session, make it no longer available"""
        del self.store[self.session_id]
        self._killed = True
Session類

 

  在webpy的session中,存儲方式包括兩種DiskStore和DBStore,分別為硬盤存儲和數據庫存儲。

  

  

  而session的存儲也可以看出來,把sessionid作為key來存儲session信息

  

參考

http://doc.outofmemory.cn/python/webpy-cookbook/

http://webpy.org/docs/0.3/tutorial

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM