web中cookie和session

参考:实现一个靠谱的Web认证 简书

cookie用于浏览器保存服务器设置的key/value键值对,保存浏览器中(ChromeApplication->Storage->Cookies可查看相关cookie),当下次向相同域名请求时会在request的Headers的Cookie附带设置的key/value。

session是cookie的特殊应用,session应用中key/value保存在服务器端,但服务器端会生成一个sessionID(唯一一个)并通过设置cookie的方式发送给客户端,这样客户端浏览器就通过cookie方式保存了session相关的唯一的key/value。当下次向相同域名请求时session的key/value会通过cookie方式发送给服务器,服务器通过查询保存在本地的session的cookie值获取相应的session的key/value。

session比cookie安全(session的浏览器客户端保存的仅是sessionID)。cookie有有效期,过期后自动丢弃。

设置cookie属性为:HttpOnly,Secure,Same-Site=strict,避免XSS和CSRF攻击。

  • 整个浏览器中,只有一种资源是脚本无法访问到的。这就是被设置为HttpOnly的cookie。
  • Same-Site=strict,同域网站才提交cookie。
  • 将Cookie设置为Secure,浏览器就可以只在访问https网址时才会携带Cookie。

首次访问请求/回复:

POST /login HTTP/1.1
User-Agent: PostmanRuntime/7.26.1
Accept: */*
Cache-Control: no-cache
Postman-Token: 6c210ad2-035e-4130-8b6c-202fdaa613d8
Host: 172.61.1.239:8088
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------076496752010059286203488
Content-Length: 165
----------------------------postman
Content-Disposition: form-data; name="name"
 
Sabine
----------------------------postman--
HTTP/1.1 200 OK
Cache-Control: no-cache="Set-Cookie"
Set-Cookie: session=6Ppm_NHRPdp0aV3RxzdfDUAdkGUQxrlYNKUelwzqTrU; Path=/; Expires=Sat, 11 Jul 2020 08:39:44 GMT; Max-Age=121; HttpOnly; SameSite=Lax
Vary: Cookie
Date: Sat, 11 Jul 2020 08:37:43 GMT
Content-Length: 7
Content-Type: text/plain; charset=utf-8

第二次访问,请求回复

POST /member/id HTTP/1.1
User-Agent: PostmanRuntime/7.26.1
Accept: */*
Cache-Control: no-cache
Postman-Token: 94f10475-5310-4966-b12e-cf8701f56e2c
Host: 172.61.1.239:8088
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------990265385004842034250081
Cookie: session=6Ppm_NHRPdp0aV3RxzdfDUAdkGUQxrlYNKUelwzqTrU
Content-Length: 165
----------------------------postman
Content-Disposition: form-data; name="name"

Sabine
----------------------------postman--
HTTP/1.1 200 OK
Date: Sat, 11 Jul 2020 08:38:25 GMT
Content-Length: 15
Content-Type: text/plain; charset=utf-8

golang应用

session应用中服务器需要保存数据,可选方案有memstore(一般默认,重启丢失),mysql/redis/sqlite3/postgres/mongodb等数据库,cookie-based(客户端浏览器保存)等。

采用github.com/alexedwards/scs/v2库处理session,报文如上,代码参考golang/casbin,存储在memstore(内存)。

每登录一次生成新的token,用token识别每位用户。

var sessionManager *scs.SessionManager
    sessionManager = scs.New()
    sessionManager.Lifetime = 2 * time.Minut
// login, first request
if err := sessionManager.RenewToken(r.Context()); err != nil {
    writeError(http.StatusInternalServerError, "ERROR_LOGIN", w, err)
    return
}
sessionManager.Put(r.Context(), "userID", user.ID)
sessionManager.Put(r.Context(), "role", user.Role)
w.WriteHeader(http.StatusOK)
w.Write([]byte(“SUCCESS”)

// second request
var role string
var uid int
role = session.GetString(r.Context(), "role")
if role == "" {
    role = "anonymous"
}

uid = session.GetInt(r.Context(), "userID")
if uid == 0 {
        writeError(http.StatusInternalServerError, "ERROR_NOT_GET_UID", w, errors.New("userID is not in session"))
    return
}
                
log.Printf("role: %v, uid: %v, path: %v, method: %v\n", role, uid, r.URL.Path, r.Method)

gin中session可采用github.com/gin-contrib/sessions处理,采用cookie-based方式存储session:

subject cookie用于存储所有sessionid,通过sessionId识别每位用户。

package main

import (
        "github.com/gin-contrib/sessions"
        "github.com/gin-contrib/sessions/cookie"
     _ "github.com/gin-contrib/sessions/memstore"
"github.com/gin-gonic/gin" "github.com/google/uuid" "log" ) func main() { r := gin.Default() store := cookie.NewStore([]byte("secret")) // cookie-based存储
     // store := memstore.NewStore([]byte("secret"))  // memstore r.Use(sessions.Sessions(
"subject", store)) r.POST("/login", func(c *gin.Context) { // Verify username and password. // ... // Store current subject in session // and send back session ID. session := sessions.Default(c) sessionId := uuid.New().String() session.Set(sessionId, "alice") session.Save() log.Println("sessionId: ", sessionId) c.SetCookie("SESSIONID", sessionId, 3600, "/", "", false, true) }) r.GET("/book", func(c *gin.Context) { log.Println("session: ", getSessionValue(c)) c.String(200, "you read the book successfully") }) r.POST("/book", func(c *gin.Context) { log.Println("session: ", getSessionValue(c)) c.String(200, "you posted a book successfully") }) r.Run(":8066") } // subjectFromSession get subject from session. func getSessionValue(c *gin.Context) string { // Get session ID from cookie. sessionId, err := c.Cookie("SESSIONID") if err != nil { // Cookie not found. return "" } log.Println("sessinId: ", sessionId) // Get subject from session. session := sessions.Default(c) if subject, ok := session.Get(sessionId).(string); !ok { return "" } else { return subject } }

报文:

POST /login HTTP/1.1
User-Agent: PostmanRuntime/7.26.1
Accept: */*
Cache-Control: no-cache
Postman-Token: a121c376-6b59-494b-ab50-91572e60cf38
Host: 172.61.1.240:8066
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 0
HTTP/1.1 200 OK Set-Cookie: subject=MTU5NDQ2MDAwMXxEdi1CQkFFQ180SUFBUkFCRUFBQVFfLUNBQUVHYzNSeWFXNW5EQ1lBSkdRNFptWXhOMkpqTFdGbVptWXRORFF3TUMwNU5tSmlMVGsxWXpaaE9Ea3pORGxtT0FaemRISnBibWNNQndBRllXeHBZMlU9fDS4MPiKDPT8bSgAsQy5imB1wxcj8t5WnKCe4T23lUqT; Path=/; Expires=Mon, 10 Aug 2020 09:33:21 GMT; Max-Age=2592000 Set-Cookie: SESSIONID=d8ff17bc-afff-4400-96bb-95c6a89349f8; Path=/; Max-Age=3600; HttpOnly Date: Sat, 11 Jul 2020 09:33:21 GMT Content-Length: 0
POST /book HTTP/1.1
User-Agent: PostmanRuntime/7.26.1
Accept: */*
Cache-Control: no-cache
Postman-Token: 728e3984-b0d9-488a-9b9e-3c9c895b713a
Host: 172.61.1.240:8066
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: subject=MTU5NDQ2MDAwMXxEdi1CQkFFQ180SUFBUkFCRUFBQVFfLUNBQUVHYzNSeWFXNW5EQ1lBSkdRNFptWXhOMkpqTFdGbVptWXRORFF3TUMwNU5tSmlMVGsxWXpaaE9Ea3pORGxtT0FaemRISnBibWNNQndBRllXeHBZMlU9fDS4MPiKDPT8bSgAsQy5imB1wxcj8t5WnKCe4T23lUqT; SESSIONID=d8ff17bc-afff-4400-96bb-95c6a89349f8
Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Sat, 11 Jul 2020 09:34:09 GMT
Content-Length: 30
you posted a book successfully

login多次:

POST /login HTTP/1.1
User-Agent: PostmanRuntime/7.26.1
Accept: */*
Cache-Control: no-cache
Postman-Token: 0c1a8a62-8148-412a-9e2f-55fa8cc53075
Host: 172.61.1.240:8066
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: subject=MTU5NDQ2MDY3NXxEdi1CQkFFQ180SUFBUkFCRUFBQV9nRUFfNElBQkFaemRISnBibWNNSmdBa01Ua3lZV1ZoTUdNdE5tUTVaQzAwTnpWaExXRXhOREl0TVRRMk9UZzNNVFF5TlRVNEJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa1pEaG1aakUzWW1NdFlXWm1aaTAwTkRBd0xUazJZbUl0T1RWak5tRTRPVE0wT1dZNEJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa05EWmhPVFF4WkRVdFpXSTNNQzAwWWpjNExUbGlZakl0WWpVd01qVXpNRGxpTm1NeEJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa05UTTFPVEJrTURZdFlqTTJaUzAwWmpjMExXSmlZbUV0WkRGa1l6a3dPVEprWkRsbUJuTjBjbWx1Wnd3SEFBVmhiR2xqWlE9PXylvjniFh2WXagX0ljGW31oyswx4YIClIvYMzlW709evw==; SESSIONID=53590d06-b36e-4f74-bbba-d1dc9092dd9f
Content-Length: 0

HTTP/1.1 200 OK Set-Cookie: subject=MTU5NDQ2MDY5NnxEdi1CQkFFQ180SUFBUkFCRUFBQV9nRV9fNElBQlFaemRISnBibWNNSmdBa01Ua3lZV1ZoTUdNdE5tUTVaQzAwTnpWaExXRXhOREl0TVRRMk9UZzNNVFF5TlRVNEJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa1pEaG1aakUzWW1NdFlXWm1aaTAwTkRBd0xUazJZbUl0T1RWak5tRTRPVE0wT1dZNEJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa05EWmhPVFF4WkRVdFpXSTNNQzAwWWpjNExUbGlZakl0WWpVd01qVXpNRGxpTm1NeEJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa05UTTFPVEJrTURZdFlqTTJaUzAwWmpjMExXSmlZbUV0WkRGa1l6a3dPVEprWkRsbUJuTjBjbWx1Wnd3SEFBVmhiR2xqWlFaemRISnBibWNNSmdBa05qQTRaVEUzWWprdE4yUXlZeTAwTWpCaUxXSXpNMlF0TVRRMll6Y3pOVEl6TVRkbUJuTjBjbWx1Wnd3SEFBVmhiR2xqWlE9PXzbTV6Jq4nqQSgkxSIWQ5-13jFezSogWo-IvFOw5dnWdA==; Path=/; Expires=Mon, 10 Aug 2020 09:44:56 GMT; Max-Age=2592000 Set-Cookie: SESSIONID=608e17b9-7d2c-420b-b33d-146c7352317f; Path=/; Max-Age=3600; HttpOnly Date: Sat, 11 Jul 2020 09:44:56 GMT Content-Length: 0

以下转自:Python爬虫番外篇之Cookie和Session  python修行路

关于cookie和session估计很多程序员面试的时候都会被问到,这两个概念在写web以及爬虫中都会涉及,并且两者可能很多人直接回答也不好说的特别清楚,所以整理这样一篇文章,也帮助自己加深理解

什么是Cookie

其实简单的说就是当用户通过http协议访问一个服务器的时候,这个服务器会将一些Name/Value键值对返回给客户端浏览器,并将这些数据加上一些限制条件。在条件符合时,这个用户下次再访问服务器的时候,数据又被完整的带给服务器。

因为http是一种无状态协议,用户首次访问web站点的时候,服务器对用户一无所知。而Cookie就像是服务器给每个来访问的用户贴的标签,而这些标签就是对来访问的客户端的独有的身份的一个标识,这里就如同每个人的身份证一样,带着你的个人信息。而当一个客户端第一次连接过来的时候,服务端就会给他打一个标签,这里就如同给你发了一个身份证,当你下载带着这个身份证来的时候,服务器就知道你是谁了。所以Cookie是存在客户端的,这里其实就是在你的浏览器中。

Cookie中包含了一个由名字=值(name = value)这样的信息构成的任意列表,通过Set-Cookie或Set-Cookie2 HTTP响应(扩展)首部将其贴到客户端身上。如下图例子所示:

其实这里有一个非常典型的应用,就是关于你登录很多网站的账号信息,你让记住密码之后,一段时间内,不需要输入密码,每次都是登录状态

Cookie的分类

这里Cookie主要分为两种:
会话Cookie:不设置过期时间,保存在浏览器的内存中,关闭浏览器,Cookie便被销毁
普通Cookie:设置了过期时间,保存在硬盘上

Cookie属性

因为最开始的cookie是网景公司定义的,后来又有了RFC版本所以当前的Cookie有两个版本:Version 0 Version 1 他们有两种设置响应头的标识,分别是:Set-Cookie和Set-Cookie2,这也造成了一些属性的不同,这里需要注意:常用的为Version 0

Version 0的属性

NAME = Value :键值对设置要保存的Name/Value,这里的name不能喝其他属性的名字一样
Expires:过期时间
Domain:生成该Cookie的域名
Path: 该Cookie是在当前的哪个路径下生成
Secure:如果设置了这个属性,那么只会在SSH连接时才会回传该Cookie

Version 1的属性

Name=VALUE:键值对设置要保存的Name/Value,这里的name不能喝其他属性的名字一样
Comment:主是想,用于说明该Cookie有什么用途
CommentURL:该服务器为此COokie提供URI注释
Discard:是否在回话结束丢弃该Cookie,默认为false
Domain:生成该Cookie的域名
Max-Age:最大失效时间,与Version 0不同的是这里设置的是在多少秒后失效
Path:该Cookie是在当前的哪个路径下生成
Port:该 Cookie 在什么端口下可以回传服务端,如果有多个端口,以逗号隔开
Secure:如果设置了这个属性,那么只会在SSH连接时才会回传该Cookie

关于Session

上面我们知道了Cookie可以让服务器端跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些 Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,而 Session 的出现正是为了解决这个问题。

同一个客户端每次和服务端交互时,不需要每次都传回所有的Cookie值,而是只要传回一个ID这个ID是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的ID,客户端只要传回这个ID就行了,这个ID通常是NANE为JSESIONID的一个Cookie。所以Session其实是利用Cookie进行信息处理的。

  1. cookie和session的共同之处在于:cookie和session都是用来跟踪浏览器用户身份的会话方式。
  2. cookie 和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。
  3. cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session,当然也没有绝对的安全,只是相对cookie,session更加安全
  4. session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
  5. cookie和session各有优缺点,所以将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中 
posted @ 2019-05-19 22:51  yuxi_o  阅读(846)  评论(0编辑  收藏  举报