1.coolie是什么?
HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出。
cookie就是解决HTTP协议无状态的方案之一,中文是小甜饼的意思。
Cookie实际上就是服务保存在浏览器上的一段消息。浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求。
Cookie由服务器创建,并发送给浏览器,最终由浏览器保存。

2.Cookie的用途
保持用户登录状态
电商网站:京东购物车
3.Cookie的使用
测试服务端发送cookie给客户端,客户端请求时携带cookie
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
// 定义中间件
func myTime(c *gin.Context){
start := time.Now()
c.Next()
// 统计时间
since := time.Since(start)
fmt.Println("程序用时:",since)
}
func main() {
// 1. 创建路由器
r := gin.Default()
// 服务端要给客户端cookie
r.GET("cookie", func(c *gin.Context) {
// 获取客户端地址是否携带cookie
cookie,err := c.Cookie("key_cookie")
if err !=nil{
cookie = "NotSet"
//设置cookie
// name名字, value值 string,
// maxAge int,单位秒(过期时间)
// path, cookie所在的目录 domain域名 string,
// secure是否能只能通过https访问, httpOnly是否允许别人通过js获取自己的cookie bool
c.SetCookie("key_cookie","value_cookie",60,"/",
"localhost",false,true)
}
fmt.Printf("cookie的值是:%s\n",cookie)
})
// 3.监听端口,默认8080
r.Run(":8000")
}

cookie练习
模拟实现权限验证中间件
有2个路由,login和home
login用于设置cookie
home是访问查看信息的请求
在请求home之前,先跑中间件代码,检验是否存在cookie

package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 定义中间件
func AuthMiddleWare()gin.HandlerFunc{
return func(c *gin.Context) {
// 获取客户端cookie并校验费
if cookie,err:=c.Cookie("abc");err==nil{
if cookie == "123"{
c.Next()
return
}
}
// 返回错误
c.JSON(http.StatusUnauthorized,gin.H{"error":"err"})
// 若验证不通过,不再调用后续的函数处理
c.Abort()
return
}
}
func main() {
// 1. 创建路由器
r := gin.Default()
//
r.GET("/login", func(c *gin.Context) {
// 设置cookie
c.SetCookie("abc","123",60,"/",
"localhost",false,true)
// 返回信息
c.String(200,"Login success!")
})
r.GET("/home",AuthMiddleWare(), func(c *gin.Context) {
c.JSON(200,gin.H{"data":"home"})
})
// 3.监听端口,默认8080
r.Run(":8000")
}
访问/home和/login进行测试
Cookie的缺点
1、不安全,明文
2、增加带宽消耗
3、可以被禁用
4、cookie有上限
Session是什么
Session可以弥补Cookie的不足,Session必须依赖于Cookie才能使用,生成一个SessionId放在Cookie里传给客户端就可以

Session中间件开发
1.设计一个通用的Session服务,支持内存存储和redis存储
2.session模块设计
本质上k-v系统,通过key进行增删改查
session可以存储在内存或者redis(2个版本)

session接口设计:
1、SET()
2、GET()
3、DEL()
4、SAVE() :session存储,redis的实现延迟加载
SessionMgr接口设计:
1、Init():初始化,加载redis地址
2、CreatSession():创建一个线的session
3、GetSeesion():通过sessionId获取对应的session对象
MemorySession设计:
定义MemorySession对象(字段:存放所有session、存kv的map,读写锁)
构造函数,为了获取对象
1、SET()
2、GET()
3、DEL()
4、SAVE()
MemorySessionMgr设计:
定义MemorySession对象(字段:存放所有session的map,读写锁)
构造函数
1、Init()
2、CreatSession()
3、GetSeesion()
RedisSession设计:
定义RedisSession对象(字段:sessionId,存放kv的map,读写锁,redis连接池,记录内存中map是否被修改的标记)
构造函数
1、SET():将session存到内存中的map
2、GET():取数据,实现延迟加载
3、DEL()
4、SAVE():将session存到redis
RedisSessionMgr设计:
定义RedisSessionMgr对象(字段:redis地址、redis密码、连接池、读写锁,大map)
构造函数
1、Init()
2、CreatSession()
3、GetSeesion()

session_mgr.go
package session
// 定义管理者,管理所有session
type SessionMgr interface {
// 初始化
Init(addr string,options ...string)(err error)
CreateSession() (session Session,err error)
Get(sessionId string)(Session Session,err error)
}
session.go
package session
type Session interface {
Set(key string,value interface{})error
Get(key string)(interface{},error)
Del(key string)error
Save()error
}
session_mgr.go
package session
// 定义管理者,管理所有session
type SessionMgr interface {
// 初始化
Init(addr string,options ...string)(err error)
CreateSession() (session Session,err error)
Get(sessionId string)(Session Session,err error)
}
memory.go
package session
import (
"errors"
"github.com/gogo/protobuf/plugin/defaultcheck"
"google.golang.org/genproto/googleapis/datastore/admin/v1"
admin "google.golang.org/genproto/googleapis/datastore/admin/v1beta1"
"google.golang.org/grpc/credentials/oauth"
"sync"
)
// 对象
//MemorySession设计:
//定义MemorySession对象(字段:存放所有session、存kv的map,读写锁)
//构造函数,为了获取对象
//1、SET()
//2、GET()
//3、DEL()
//4、SAVE()
type MemorySession struct{
sessionId string
// 存kv
data map[string]interface{}
rwlock sync.RWMutex
}
// 构造函数
func NewMemorySession(id string)*MemorySession{
s := &MemorySession{
sessionId: id,
data: make(map[string]interface{},16),
//rwlock: sync.RWMutex{},
}
return s
}
func (m *MemorySession) Set(key string,value interface{})(err error){
// 加锁
m.rwlock.Lock()
defer m.rwlock.Unlock()
// 设置值
m.data[key] = value
return
}
func (m *MemorySession) Get(key string)(value interface{},err error){
m.rwlock.Lock()
defer m.rwlock.Unlock()
value,ok := m.data[key]
if !ok {
err = errors.New("key not exists in session")
return
}
return
}
func (m *MemorySession) Del(key string)(err error){
m.rwlock.Lock()
defer m.rwlock.Unlock()
delete(m.data,key)
return
}
func(m *MemorySession) Save()(err error){
return
}
memory_session_mgr.go
package session
import (
uuid "github.com/satori/go.uuid"
"sync"
)
//MemorySessionMgr设计:
//定义MemorySession对象(字段:存放所有session的map,读写锁)
//构造函数
//1、Init()
//2、CreatSession()
//3、GetSeesion()
// 定义对象
type MemorySessionMgr struct {
sessionMap map[string]Session
rwlock sync.RWMutex
}
//构造函数
func NewMemnorySession() *MemorySessionMgr {
sr:=&MemorySessionMgr{
sessionMap: make(map[string]Session,1024),
}
return sr
}
func (s *MemorySessionMgr) Init(addr string,option ...string)(err error){
return
}
func (s *MemorySessionMgr)CreateSession()(session Session,err error){
s.rwlock.Lock()
defer s.rwlock.Unlock()
// go get github.com/google/uuid
// 用uuid作为sessionId
id, err := uuid.NewV4()
if err !=nil{
return
}
// 转string
sessionId := id.String()
// 创建session
session = NewMemnorySession(sessionId)
return
}
memory_session_mgr.go
package session
import (
"errors"
"github.com/satori/go.uuid"
"sync"
)
//MemorySessionMgr设计:
//定义MemorySession对象(字段:存放所有session的map,读写锁)
//构造函数
//1、Init()
//2、CreatSession()
//3、GetSeesion()
// 定义对象
type MemorySessionMgr struct {
sessionMap map[string]Session
rwlock sync.RWMutex
}
//构造函数
func NewMemnorySessionMgr(sessionId string) *MemorySessionMgr {
sr:=&MemorySessionMgr{
sessionMap: make(map[string]Session,1024),
}
return sr
}
func (s *MemorySessionMgr) Init(addr string,option ...string)(err error){
return
}
// 创建session
func (s *MemorySessionMgr)CreateSession()(session Session,err error){
s.rwlock.Lock()
defer s.rwlock.Unlock()
// go get github.com/google/uuid
// 用uuid作为sessionId
id, err := uuid.NewV4()
if err !=nil{
return
}
// 转string
sessionId := id.String()
// 创建session
session = NewMemnorySessionMgr(sessionId)
// 加入到大map
s.sessionMap[sessionId]=session
return
}
func (s *MemorySessionMgr)Get(sessionId string)(session Session,err error){
s.rwlock.Lock()
defer s.rwlock.Unlock()
session,ok:=s.sessionMap[sessionId]
if !ok{
err=errors.New("session not exit")
return
}
return
}
redis_session.go
package session
import (
"encoding/json"
"errors"
"github.com/gomodule/redigo/redis"
"sync"
)
//RedisSession设计:
//定义RedisSession对象(字段:sessionId,存放kv的map,读写锁,redis连接池,记录内存中map是否被修改的标记)
//构造函数
//1、SET():将session存到内存中的map
//2、GET():取数据,实现延迟加载
//3、DEL()
//4、SAVE():将session存到redis
type RedisSession struct {
sessionId string
pool *redis.Pool
// 设置session,可以先放在内存map中
// 批量导入redis,提升性能
sessionMap map[string]interface{}
// 读写锁
rwlock sync.RWMutex
// 记录内存中map是否被操作
flag int
}
//用常量定义状态
const(
// 内存数据没变化
SessionFlagNone = iota
// 有变化
SessionFlagModify
)
// 构造函数
func NewRedisSession(id string,pool *redis.Pool)*RedisSession{
s:= &RedisSession{
sessionId: id,
pool:pool,
sessionMap: make(map[string]interface{},16),
flag: SessionFlagNone,
}
return s
}
func (r *RedisSession)Set(key string,value interface{})(err error){
// 加锁
r.rwlock.Lock()
defer r.rwlock.Unlock()
// 设置值
r.sessionMap[key] = value
// 标志记录
r.flag = SessionFlagModify
return
}
// session存储 到redis
func (r *RedisSession)Save()(err error){
// 加锁
r.rwlock.Lock()
defer r.rwlock.Unlock()
// 若数据不变,不需存
if r.flag != SessionFlagModify{
return
}
// 从内存中的sessionMap进行序列化
data,err := json.Marshal(r.sessionMap)
if err!=nil{
return
}
// 获取redis连接
conn := r.pool.Get()
// 保存kv
_, err = conn.Do("SET",r.sessionId,string(data))
// 改变状态
r.flag = SessionFlagNone
if err != nil{
return
}
return
}
func (r *RedisSession)Get(key string)(result interface{},err error){
// 加锁
r.rwlock.Lock()
defer r.rwlock.Unlock()
// 先判断内存
result,ok := r.sessionMap[key]
if !ok{
err = errors.New("key not exists")
}
return
}
// 从redis里再次加载
func (r *RedisSession)loadFromRedis()(err error){
conn := r.pool.Get()
reply, err:= conn.Do("GET",r.sessionId)
if err!=nil{
return
}
// 转字符串
data, err := redis.String(reply,err)
if err!= nil{
return
}
// 取到的东西,反序列化到内存的map
err = json.Unmarshal([]byte(data),&r.sessionMap)
if err !=nil{
return
}
return
}
func (r * RedisSession) Del(key string)(err error){
r.rwlock.Lock()
defer r.rwlock.Unlock()
r.flag = SessionFlagModify
delete(r.sessionMap,key)
return
}
redis_session_mgr.go
package session
import (
"errors"
"github.com/gomodule/redigo/redis"
uuid "github.com/satori/go.uuid"
"sync"
"time"
)
//RedisSessionMgr设计:
//定义RedisSessionMgr对象(字段:redis地址、redis密码、连接池、读写锁,大map)
//构造函数
//1、Init()
//2、CreatSession()
//3、GetSeesion()
type RedisSessionMgr struct {
// redis地址
addr string
// 密码
password string
// 连接池
pool *redis.Pool
// 锁
rwlock sync.RWMutex
// 大map
SessionMap map[string]Session
}
func (r *RedisSessionMgr)Init(addr string,options ...string)(err error){
// 若狗其他参数
if len(options)>0{
r.password = options[0]
}
// 创建连接池
r.pool=myPool(addr,r.password)
r.addr = addr
return
}
func myPool(addr, password string) *redis.Pool {
return &redis.Pool{
Dial: func() (conn redis.Conn , e error) {
conn, err := redis.Dial("tcp",addr)
if err!=nil{
return nil,err
}
// 若有密码,判断
if _,err := conn.Do("AUTO",password);err !=nil{
conn.Close()
return nil,err
}
return conn,err
},
DialContext: nil,
TestOnBorrow: func(conn redis.Conn, t time.Time) error {
_,err := conn.Do("PING")
return err
},
MaxIdle: 64,
MaxActive: 1000,
IdleTimeout: time.Duration(time.Second),
Wait: false,
MaxConnLifetime: 0,
}
}
func (r *RedisSession)CreateSession() (session Session,err error){
// 加锁
r.rwlock.Lock()
defer r.rwlock.Unlock()
// 用uuid作为sessionId
id,err := uuid.NewV4()
if err!=nil{
return
}
// 转string
sessionId := id.String()
// 创建session
session = NewRedisSession(sessionId,r.pool)
// 加入到大map
r.sessionMap[sessionId] = session
return
}
// 构造
//func NewRedisSessionMgr() SessionMgr{
// sr := &RedisSessionMgr{
// SessionMap: make(map[string]Session,32),
// }
// return sr
//}
func (r *RedisSessionMgr) Get(sessionId string)(session Session,err error){
r.rwlock.Lock()
defer r.rwlock.Unlock()
session,ok := r.SessionMap[sessionId]
if !ok{
err = errors.New("session not exists")
return
}
return
}
init.go 版本控制
package session
import "fmt"
// 中间件让用户选择使用哪个版本
var (
sessionMgr SessionMgr
)
func Init(provider string,addr string,options ...string)(err error){
switch provider {
case "memory":
sessionMgr = NewMemnorySessionMgr()
case "redis":
sessionMgr = NewRedisSession()
default:
fmt.Println("不支持")
return
}
err = sessionMgr.Init(addr,options...)
return
}