国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)易工作做網(wǎng)站工資獎(jiǎng)金高嗎上海關(guān)鍵詞排名優(yōu)化公司

網(wǎng)易工作做網(wǎng)站工資獎(jiǎng)金高嗎,上海關(guān)鍵詞排名優(yōu)化公司,dw做的網(wǎng)站怎么,淡水做網(wǎng)站〇、前言 眾所周知,http協(xié)議是無(wú)狀態(tài)的,這對(duì)于服務(wù)器確認(rèn)是哪一個(gè)客戶(hù)端在發(fā)請(qǐng)求是不可能的,因此為了能確認(rèn)到,通常方法是讓客戶(hù)端發(fā)送請(qǐng)求時(shí)帶上身份信息。容易想到的方法就是客戶(hù)端在提交信息時(shí),帶上自己的賬戶(hù)和密…

〇、前言

眾所周知,http協(xié)議是無(wú)狀態(tài)的,這對(duì)于服務(wù)器確認(rèn)是哪一個(gè)客戶(hù)端在發(fā)請(qǐng)求是不可能的,因此為了能確認(rèn)到,通常方法是讓客戶(hù)端發(fā)送請(qǐng)求時(shí)帶上身份信息。容易想到的方法就是客戶(hù)端在提交信息時(shí),帶上自己的賬戶(hù)和密碼。但是這樣存在著嚴(yán)重的安全問(wèn)題,可以改進(jìn)的方法就是,服務(wù)器給一個(gè)確定的客戶(hù)端返回一個(gè)唯一 id,客戶(hù)端將這個(gè) id 保存在本地,每次發(fā)送請(qǐng)求時(shí)只需要攜帶著這個(gè) id,就可以做到較好的驗(yàn)證(當(dāng)然也存在著安全問(wèn)題,這個(gè)后面再說(shuō))。

這個(gè)方法就是 現(xiàn)今很成熟的 session、cookie 技術(shù)。session和cookie的目的相同,都是為了克服http協(xié)議無(wú)狀態(tài)的缺陷,但完成的方法不同。session通過(guò)cookie,在客戶(hù)端保存session id,而將用戶(hù)的其他會(huì)話消息保存在服務(wù)端的session對(duì)象中。與此相對(duì)的,cookie需要將所有信息都保存在客戶(hù)端。因此cookie存在著一定的安全隱患,例如本地cookie中保存的用戶(hù)名密碼被破譯,或cookie被其他網(wǎng)站收集。

本文將嘗試著實(shí)現(xiàn)一個(gè)成熟的 go session,從而實(shí)現(xiàn)會(huì)話保持。
思維導(dǎo)圖如下:
在這里插入圖片描述

一、架構(gòu)設(shè)計(jì)

0、思維導(dǎo)圖

在這里插入圖片描述

1、管理器

type Manager struct {cookieName  stringlock        sync.Mutexprovider    ProvidermaxLifeTime int64
}

其中 Provider 是一個(gè)接口:

// Provider 接口
type Provider interface {SessionInit(sid string) (Session, error) // SessionInit函數(shù)實(shí)現(xiàn)Session的初始化,操作成功則返回此新的Session變量SessionRead(sid string) (Session, error) // SessionRead函數(shù)返回sid所代表的Session變量.如果不存在,那么將以sid為參數(shù)調(diào)用SessionInit函數(shù)創(chuàng)建并返回一個(gè)新的Session變量SessionDestroy(sid string) error         // SessionDestroy函數(shù)用來(lái)銷(xiāo)毀sid對(duì)應(yīng)的Session變量SessionGC(maxLifeTime int64)             // SessionGC根據(jù)maxLifeTime來(lái)刪除過(guò)期的數(shù)據(jù)
}

這里又定義了一個(gè)Provider 結(jié)構(gòu)體,它實(shí)現(xiàn)了 Provider 接口:

// Provider 實(shí)現(xiàn)接口 Providerfunc (pder *Provider) SessionInit(sid string) (session.Session, error) {// 根據(jù) sid 創(chuàng)建一個(gè) SessionStorepder.lock.Lock()defer pder.lock.Unlock()v := make(map[interface{}]interface{})// 同時(shí)更新兩個(gè)字段newsess := &SessionStore{sid: sid, timeAccessed: time.Now(), value: v}// list 用于GCelement := pder.list.PushBack(newsess)// 存放 kvpder.sessions[sid] = elementreturn newsess, nil
}func (pder *Provider) SessionRead(sid string) (session.Session, error) {if element, ok := pder.sessions[sid]; ok {return element.Value.(*SessionStore), nil} else {sess, err := pder.SessionInit(sid)return sess, err}
}// 服務(wù)端 session 銷(xiāo)毀func (pder *Provider) SessionDestroy(sid string) error {if element, ok := pder.sessions[sid]; ok {delete(pder.sessions, sid)pder.list.Remove(element)return nil}return nil
}// 回收過(guò)期的 cookiefunc (pder *Provider) SessionGC(maxlifetime int64) {pder.lock.Lock()defer pder.lock.Unlock()for {element := pder.list.Back()if element == nil {break}if (element.Value.(*SessionStore).timeAccessed.Unix() + maxlifetime) < time.Now().Unix() {// 更新兩者的值// 垃圾回收pder.list.Remove(element)// 刪除 map 中的kvdelete(pder.sessions, element.Value.(*SessionStore).sid)} else {break}}
}func (pder *Provider) SessionUpdate(sid string) error {pder.lock.Lock()defer pder.lock.Unlock()if element, ok := pder.sessions[sid]; ok {// 這里更新也就更新了個(gè)時(shí)間,這意味著 session 的生命得到了延長(zhǎng)element.Value.(*SessionStore).timeAccessed = time.Now()pder.list.MoveToFront(element)return nil}return nil
}

管理器 Manager 實(shí)現(xiàn)的方法:

// 創(chuàng)建 Sessionfunc (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {manager.lock.Lock()defer manager.lock.Unlock()cookie, err := r.Cookie(manager.cookieName)if err != nil || cookie.Value == "" {// 查看是否為當(dāng)前客戶(hù)端注冊(cè)過(guò)名為 gosessionid 的 cookie,如果沒(méi)有注冊(cè)過(guò),就為客戶(hù)端創(chuàng)建一個(gè)該 cookie// 創(chuàng)建 sessionIDsid := manager.sessionID()// 創(chuàng)建一個(gè) session 接口,這其實(shí)是一個(gè) 創(chuàng)建完成的 SessionStore ,SessionStore 實(shí)現(xiàn)了該接口session, _ = manager.provider.SessionInit(sid)// 創(chuàng)建 cookiecookie := http.Cookie{Name: manager.cookieName, Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, MaxAge: int(manager.maxLifeTime)}http.SetCookie(w, &cookie)} else {sid, _ := url.QueryUnescape(cookie.Value)session, _ = manager.provider.SessionRead(sid)}return
}// Session 重置func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {cookie, err := r.Cookie(manager.cookieName)if err != nil || cookie.Value == "" {return} else {manager.lock.Lock()defer manager.lock.Unlock()err := manager.provider.SessionDestroy(cookie.Value)if err != nil {return}expiration := time.Now()cookie := http.Cookie{Name: manager.cookieName, Path: "/", HttpOnly: true, Expires: expiration, MaxAge: -1}http.SetCookie(w, &cookie)}
}// Session 回收func (manager *Manager) GC() {manager.lock.Lock()defer manager.lock.Unlock()manager.provider.SessionGC(manager.maxLifeTime)// 每 20秒觸發(fā)一次time.AfterFunc(time.Second*20, func() { manager.GC() })
}

2、sessions存放

在 Provider 結(jié)構(gòu)體中:

sessions map[string]*list.Element // 存放 sessionStores
list     *list.List               // 用來(lái)做gc

sessions 中存放不同客戶(hù)端的 session,而 list 中也會(huì)同時(shí)刷新,它用來(lái)回收過(guò)期的 session。
每一個(gè)session用 SessionStore 結(jié)構(gòu)體來(lái)存儲(chǔ)。

Session 接口:

// Session 接口
type Session interface {Set(key, value interface{}) error // 設(shè)置 session 的值Get(key interface{}) interface{}  // 獲取 session 的值Delete(key interface{}) error     // 刪除 session 的值SessionID() string                // 返回當(dāng)前 session 的 ID
}

這個(gè)接口,由 SessionStore 實(shí)現(xiàn):

// SessionStore 結(jié)構(gòu)體type SessionStore struct {sid          string                      // session id唯一標(biāo)識(shí)timeAccessed time.Time                   // 最后訪問(wèn)時(shí)間value        map[interface{}]interface{} // 值
}
// SessionStore 實(shí)現(xiàn) Session 接口func (st *SessionStore) Set(key, value interface{}) error {st.value[key] = valueerr := pder.SessionUpdate(st.sid)if err != nil {return err}return nil
}func (st *SessionStore) Get(key interface{}) interface{} {err := pder.SessionUpdate(st.sid)if err != nil {return nil}if v, ok := st.value[key]; ok {return v} else {return nil}
}func (st *SessionStore) Delete(key interface{}) error {delete(st.value, key)err := pder.SessionUpdate(st.sid)if err != nil {return err}return nil
}func (st *SessionStore) SessionID() string {return st.sid
}

二、實(shí)現(xiàn)細(xì)節(jié)

1、provider 注冊(cè)表

// provider 注冊(cè)表
var provides = make(map[string]Provider)

任何一個(gè) Maneger 在創(chuàng)建之前,都需要在 provider 注冊(cè)表中注冊(cè)。因此在創(chuàng)建一個(gè)全局注冊(cè)表pder,并注冊(cè),這應(yīng)該是 init 的:

// 創(chuàng)建全局 pder
var pder = &Provider{list: list.New()}
func init() {pder.sessions = make(map[string]*list.Element)session.Register("memory", pder)
}

注冊(cè)器:

func Register(name string, provider Provider) {if provider == nil {panic("session: Register provide is nil")}if _, dup := provides[name]; dup {panic("session: Register called twice for provide " + name)}provides[name] = provider
}

2、全局管理器

var globalSessions *session.Manager
func init() {globalSessions, _ = session.NewManager("memory", "gosessionid", 3600)go globalSessions.GC()
}

這個(gè)管理器就是一個(gè) cookie 管理器,它只對(duì)cookie名字為gosessionid的 cookie 負(fù)責(zé)。

func NewManager(provideName, cookieName string, maxlifetime int64) (*Manager, error) {provider, ok := provides[provideName]if !ok {return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)}return &Manager{provider: provider, cookieName: cookieName, maxLifeTime: maxlifetime}, nil
}

3、案例演示

現(xiàn)在已經(jīng)初始化好了,就等著客戶(hù)端訪問(wèn)了。
現(xiàn)在我們寫(xiě)一個(gè)很簡(jiǎn)單的計(jì)數(shù)器,前端訪問(wèn)的時(shí)候,自動(dòng)+1:

func count(c *gin.Context) {sess := globalSessions.SessionStart(c.Writer, c.Request)ct := sess.Get("countnum")if ct == nil {err := sess.Set("countnum", 1)if err != nil {return}} else {// 更新err := sess.Set("countnum", ct.(int)+1)if err != nil {return}}t, err := template.ParseFiles("template/count.html")if err != nil {fmt.Println(err)}c.Writer.Header().Set("Content-Type", "text/html")err = t.Execute(c.Writer, sess.Get("countnum"))if err != nil {return}
}

當(dāng)中的count.html這樣寫(xiě):

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Count</title>
</head><body><h1>Hi. Now count:{{.}}</h1>
</body></html>

main.go這樣寫(xiě):

package mainimport (_ "Go_Web/memory""Go_Web/session""fmt""github.com/gin-gonic/gin""html/template""net/http"
)// 全局 sessions 管理器
var globalSessions *session.Manager// init 初始化func init() {globalSessions, _ = session.NewManager("memory", "gosessionid",20)go globalSessions.GC()
}func count(c *gin.Context) {sess := globalSessions.SessionStart(c.Writer, c.Request)ct := sess.Get("countnum")if ct == nil {err := sess.Set("countnum", 1)if err != nil {return}} else {// 更新err := sess.Set("countnum", ct.(int)+1)if err != nil {return}}t, err := template.ParseFiles("template/count.html")if err != nil {fmt.Println(err)}c.Writer.Header().Set("Content-Type", "text/html")err = t.Execute(c.Writer, sess.Get("countnum"))if err != nil {return}
}func main() {r := gin.Default()r.GET("/count", count)err := r.Run(":9000")if err != nil {return}}

我們把 session 的過(guò)期時(shí)間設(shè)為 20 秒,這樣可以 更快的看到過(guò)期效果。
現(xiàn)在把服務(wù)器啟動(dòng),來(lái)看看整個(gè)過(guò)程。
編譯運(yùn)行之后,在瀏覽器訪問(wèn) count:
在這里插入圖片描述

看下 cookie:
在這里插入圖片描述
可以繼續(xù)點(diǎn)擊,這個(gè)只要在 20 秒之內(nèi)點(diǎn)擊,cookie 就不回過(guò)期,因?yàn)槊看伟l(fā)送請(qǐng)求都會(huì)更新 sessionStore:

err := sess.Set("countnum", ct.(int)+1)
// SessionStore 實(shí)現(xiàn) Session 接口func (st *SessionStore) Set(key, value interface{}) error {st.value[key] = valueerr := pder.SessionUpdate(st.sid)if err != nil {return err}return nil
}
func (pder *Provider) SessionUpdate(sid string) error {pder.lock.Lock()defer pder.lock.Unlock()if element, ok := pder.sessions[sid]; ok {// 這里更新也就更新了個(gè)時(shí)間,這意味著 session 的生命得到了延長(zhǎng)element.Value.(*SessionStore).timeAccessed = time.Now()pder.list.MoveToFront(element)return nil}return nil
}

在這里插入圖片描述
不要點(diǎn)擊等 20 秒等它過(guò)期,再點(diǎn)一下:
在這里插入圖片描述
可以看到已經(jīng)過(guò)期了,再查看下 cookie:
在這里插入圖片描述
可以看到 sessionId 并沒(méi)有變,這是因?yàn)榫退惚镜?cookie過(guò)期,當(dāng)發(fā)送請(qǐng)求時(shí),服務(wù)器依然會(huì)拿到這個(gè) cookie。
session 過(guò)期的時(shí)候,服務(wù)器會(huì)執(zhí)行:

// 回收過(guò)期的 cookiefunc (pder *Provider) SessionGC(maxlifetime int64) {pder.lock.Lock()defer pder.lock.Unlock()for {element := pder.list.Back()if element == nil {break}if (element.Value.(*SessionStore).timeAccessed.Unix() + maxlifetime) < time.Now().Unix() {// 更新兩者的值// 垃圾回收pder.list.Remove(element)// 刪除 map 中的kvdelete(pder.sessions, element.Value.(*SessionStore).sid)} else {break}}
}

這意味著,pder 中的list 和 sessions 中都不存在 鍵為countnumsessionStore。但是依然會(huì)執(zhí)行:

	sid, _ := url.QueryUnescape(cookie.Value)session, _ = manager.provider.SessionRead(sid)

SessionRead():

func (pder *Provider) SessionRead(sid string) (session.Session, error) {if element, ok := pder.sessions[sid]; ok {return element.Value.(*SessionStore), nil} else {sess, err := pder.SessionInit(sid)return sess, err}
}

執(zhí)行SessionRead()的時(shí)候,由于 session 已經(jīng)被刪除,只能執(zhí)行pder.SessionInit(sid)了,因此,服務(wù)器會(huì)創(chuàng)建一個(gè)和原來(lái)一樣的 sessionId。之后count()自然就會(huì)執(zhí)行err := sess.Set("countnum", 1)。

ct := sess.Get("countnum")if ct == nil {err := sess.Set("countnum", 1)if err != nil {return}} else {// 更新err := sess.Set("countnum", ct.(int)+1)if err != nil {return}}

至此,整個(gè)過(guò)程就完了。

二、session 劫持

session劫持是一種廣泛存在的比較嚴(yán)重的安全威脅,在session技術(shù)中,客戶(hù)端和服務(wù)端通過(guò)session的標(biāo)識(shí)符來(lái)維護(hù)會(huì)話, 但這個(gè)標(biāo)識(shí)符很容易就能被嗅探到,從而被其他人利用。它是中間人攻擊的一種類(lèi)型。

這個(gè)服務(wù)是靠著 sessionid維持的,所以一旦這個(gè) sessionid 泄露,被另一個(gè)客戶(hù)端獲取,就可以冒名頂替干一些操作(把過(guò)期時(shí)間設(shè)置長(zhǎng)一點(diǎn))。
首先在 Chrome 中訪問(wèn)服務(wù)器的服務(wù),點(diǎn)擊到隨便一個(gè)數(shù)字:

在這里插入圖片描述
然后打開(kāi) cookie,復(fù)制:
在這里插入圖片描述
再打開(kāi)FireFox,隨便找一個(gè) cookie 管理器,創(chuàng)建一個(gè) cookie:
在這里插入圖片描述
保存,直接訪問(wèn)服務(wù)器count 服務(wù):
在這里插入圖片描述
可以看到已經(jīng)實(shí)現(xiàn)了“冒名頂替”。

全文完,感謝閱讀。

http://aloenet.com.cn/news/34917.html

相關(guān)文章:

  • 做阿里巴巴網(wǎng)站口碑seo技術(shù)培訓(xùn)教程
  • 有沒(méi)有做羞羞的網(wǎng)站seo排名軟件哪個(gè)好用
  • 動(dòng)易網(wǎng)站模板2022最新時(shí)事新聞及點(diǎn)評(píng)
  • 怎樣進(jìn)入建設(shè)通網(wǎng)站外鏈推廣是什么意思
  • 網(wǎng)站運(yùn)營(yíng)怎樣做網(wǎng)站建設(shè)方案模板
  • 公司制做網(wǎng)站蘭州seo推廣
  • 我要建立自己的網(wǎng)站百度關(guān)鍵詞排名優(yōu)化工具
  • 在百度上怎么建網(wǎng)站模板網(wǎng)站
  • wordpress 詞庫(kù)肇慶seo排名外包
  • 做旅游門(mén)票網(wǎng)站需要什么材料seo推廣教學(xué)
  • 桂林建站網(wǎng)站seo關(guān)鍵詞優(yōu)化技巧
  • win7 iis網(wǎng)站設(shè)置百度下載官網(wǎng)
  • 宜昌教育培訓(xùn)網(wǎng)站建設(shè)南昌企業(yè)網(wǎng)站建設(shè)
  • 做免費(fèi)資料分享網(wǎng)站會(huì)不會(huì)涉及版權(quán)企業(yè)營(yíng)銷(xiāo)培訓(xùn)課程
  • 濟(jì)南語(yǔ)委網(wǎng)站網(wǎng)絡(luò)最有效的推廣方法
  • 設(shè)計(jì)簡(jiǎn)單的網(wǎng)站山東seo首頁(yè)關(guān)鍵詞優(yōu)化
  • 網(wǎng)站如何安裝源碼網(wǎng)絡(luò)推廣策劃書(shū)
  • 優(yōu)秀網(wǎng)站建設(shè)哪個(gè)公司好狼雨seo網(wǎng)站
  • 日?qǐng)?bào)做的地方網(wǎng)站近期發(fā)生的新聞
  • 做家裝網(wǎng)站客戶(hù)來(lái)源多嗎百度百家號(hào)怎么賺錢(qián)
  • 做網(wǎng)站怎么租個(gè)域名百度搜索引擎的優(yōu)缺點(diǎn)
  • 廣告網(wǎng)站 源碼搜索網(wǎng)站排名
  • 現(xiàn)在那個(gè)網(wǎng)站做視頻最賺錢(qián)嗎湖南靠譜關(guān)鍵詞優(yōu)化
  • 做公司網(wǎng)站軟件鄭州百度seo關(guān)鍵詞
  • 建站之星網(wǎng)站模板百度sem認(rèn)證
  • 坪山網(wǎng)站建設(shè)服務(wù)寧波seo關(guān)鍵詞如何優(yōu)化
  • 電子商務(wù)網(wǎng)站開(kāi)發(fā)設(shè)計(jì)報(bào)告書(shū)seo免費(fèi)入門(mén)教程
  • 河北省建設(shè)集團(tuán)有限公司網(wǎng)站百度查重免費(fèi)入口
  • 做網(wǎng)站能力介紹模板之家官網(wǎng)
  • wordpress 響應(yīng)式產(chǎn)品展示站微信營(yíng)銷(xiāo)推廣的方式有哪些