go実践21クッキーとセッションの使用
10319 ワード
goクッキーとセッションを使う
ディレクトリ構造は次のとおりです.
プロジェクトルートディレクトリはgowebです
gowebのディレクトリ構造├——session # セッション構成ディレクトリ ├── memorysession.go # メモリセッション実装ファイル‖ ├── session.go # session底├——testsession.go # 業務制御——————————————————————
次に、ディレクトリ構造に基づいて、フォルダとファイルを上から下に作成します.
フォルダとファイルの作成 goweb/session/memorysession.go ,memorysession.goの内容は次のとおりです.
フォルダとファイルの作成 goweb/session/session.go ,session.goの内容は以下の通りです.
フォルダとファイルの作成 goweb/testsession.go ,testsession.goの内容は次のとおりです.
サーバの起動:
トップページへのアクセス:http://daily886.com:6665/
セッションへのアクセス:http://daily886.com:6665/test
セッション統計へのアクセス:http://daily886.com:6665/count
参照先:https://www.golang123.com/book/9?chapterID=171
参照先:https://github.com/astaxie/session
ディレクトリ構造は次のとおりです.
プロジェクトルートディレクトリはgowebです
gowebのディレクトリ構造├——session # セッション構成ディレクトリ ├── memorysession.go # メモリセッション実装ファイル‖ ├── session.go # session底├——testsession.go # 業務制御——————————————————————
次に、ディレクトリ構造に基づいて、フォルダとファイルを上から下に作成します.
フォルダとファイルの作成 goweb/session/memorysession.go ,memorysession.goの内容は次のとおりです.
package session
import (
"container/list"
"sync"
"time"
)
type ProviderStruct struct {
lock sync.Mutex //
sessions map[string]*list.Element //
list *list.List // gc
}
var ps = &ProviderStruct{list:list.New()}
type SessionStore struct {
sid string //session id
timeAccessed time.Time //
value map[interface{}]interface{} //session
}
func (st *SessionStore) Set(key,value interface{}) error{
st.value[key] = value
ps.SessionUpdate(st.sid)
return nil
}
func (st *SessionStore) Get(key interface{}) interface{}{
ps.SessionUpdate(st.sid)
if v,ok := st.value[key]; ok{
return v
}else{
return nil
}
return nil
}
func (st *SessionStore) Delete(key interface{}) error{
delete(st.value, key)
ps.SessionUpdate(st.sid)
return nil
}
func (st *SessionStore) SessionID() string{
return st.sid
}
func (ps *ProviderStruct) SessionInit(sid string) (Session,error){
ps.lock.Lock()
defer ps.lock.Unlock()
v := make(map[interface{}]interface{},0)
newsess := &SessionStore{sid:sid,timeAccessed:time.Now(),value:v}
element := ps.list.PushBack(newsess)
ps.sessions[sid] = element
return newsess,nil
}
func (ps *ProviderStruct) SessionRead(sid string) (Session,error){
if element,ok := ps.sessions[sid];ok{
return element.Value.(*SessionStore),nil
}else{
sess,err := ps.SessionInit(sid)
return sess,err
}
return nil,nil
}
func (ps *ProviderStruct) SessionDestroy(sid string) error{
if element,ok := ps.sessions[sid];ok{
delete(ps.sessions,sid)
ps.list.Remove(element)
return nil
}
return nil
}
func (ps *ProviderStruct) SessionGC(maxlifetime int64){
ps.lock.Lock()
defer ps.lock.Unlock()
for{
element := ps.list.Back()
if element == nil{
break
}
if (element.Value.(*SessionStore).timeAccessed.Unix() + maxlifetime) < time.Now().Unix(){
ps.list.Remove(element)
delete(ps.sessions,element.Value.(*SessionStore).sid)
}else{
break
}
}
}
func (ps *ProviderStruct) SessionUpdate(sid string) error{
ps.lock.Lock()
defer ps.lock.Unlock()
if element,ok := ps.sessions[sid];ok{
element.Value.(*SessionStore).timeAccessed = time.Now()
ps.list.MoveToFront(element)
return nil
}
return nil
}
func init(){
ps.sessions = make(map[string]*list.Element,0)
Register("memory",ps)
}
フォルダとファイルの作成 goweb/session/session.go ,session.goの内容は以下の通りです.
package session
import (
"sync"
"time"
"encoding/base64"
"math/rand"
"net/http"
"net/url"
)
/*
session
· session
· sessionid
· session
·session ( 、 、 )
·session
*/
//session
type Manager struct {
cookieName string //cookie
lock sync.Mutex //cookie
provider Provider //cookie
maxLifeTime int64 //cookie
}
// Provider , session
type Provider interface {
//SessionInit Session , Session
SessionInit(sid string)(Session,error)
//SessionRead sid Session , , sid SessionInit Session
SessionRead(sid string)(Session,error)
//SessionDestroy sid Session
SessionDestroy(sid string) error
//SessionGC maxLifeTime
SessionGC(maxLifeTime int64)
}
//Session 、 、 sessionid
type Session interface {
Set(key,value interface{}) error
Get(key interface{}) interface{}
Delete(key interface{}) error
SessionID() string
}
var provides = make(map[string]Provider)
// manager
func NewManager(provideName,cookieName string,maxLifeTime int64) (*Manager,error){
provider,ok := provides[provideName]
if !ok {
panic("session: unknown provide "+provideName+"(forgotten import?)")
}
return &Manager{provider:provider,cookieName:cookieName,maxLifeTime:maxLifeTime},nil
}
// , session
// , nil ,
func Register(name string,provider Provider){
if provider == nil{
panic("session:Register provider is nil")
}
if _,dup := provides[name];dup{
panic("session:Register called twice for provider "+name)
}
provides[name] = provider
}
// SessionID
func (manager *Manager) sessionId() string{
b := make([]byte,32)
if _,err := rand.Read(b);err != nil{
return ""
}
return base64.URLEncoding.EncodeToString(b)
}
func (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 == ""{
sid := manager.sessionId()
session,_ = manager.provider.SessionInit(sid)
cookie := http.Cookie{
Name:manager.cookieName,
Value:url.QueryEscape(sid),
Path:"/",
HttpOnly:true,
// httpOnly ( :Cookie HttpOnly , HTTP( HTTPS) Cookie。
// HttpOnly Cookie, HTTP , JavaScript( , document.cookie),
// , ( ) Cookie。 Facebook Google HttpOnly 。)
MaxAge: int(manager.maxLifeTime),
}
http.SetCookie(w,&cookie)
}else{
sid,_ := url.QueryUnescape(cookie.Value)
session,_ = manager.provider.SessionRead(sid)
}
return
}
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()
manager.provider.SessionDestroy(cookie.Value)
expiration := time.Now()
cookie := http.Cookie{
Name:manager.cookieName,
Path:"/",
HttpOnly:true,
Expires:expiration,
MaxAge:-1,
}
http.SetCookie(w,&cookie)
}
}
func (manager *Manager) GC(){
manager.lock.Lock()
defer manager.lock.Unlock()
manager.provider.SessionGC(manager.maxLifeTime)
time.AfterFunc(time.Duration(manager.maxLifeTime),func(){
manager.GC()
})
}
フォルダとファイルの作成 goweb/testsession.go ,testsession.goの内容は次のとおりです.
package main
import (
"crypto/md5"
"io"
"fmt"
"log"
"time"
"net/http"
"html/template"
"goweb/session"
)
/*
session
· session
· sessionid
· session
·session ( 、 、 )
·session
*/
var globalSessions *session.Manager
// , main
func init(){
// session , memory session ,
globalSessions,_ = session.NewManager("memory","gosessionid",8)
fmt.Printf("globalSessions: %+v
",globalSessions)
// Gc session
go globalSessions.GC()
}
//
func sayhelloName(w http.ResponseWriter,r *http.Request) {
// w
fmt.Fprintf(w,"hello testsession.go")
}
// session
func testSession(w http.ResponseWriter, r *http.Request) {
fmt.Printf("globalSessions: %+v
",globalSessions)
sess := globalSessions.SessionStart(w, r)
r.ParseForm()
if r.Method == "GET" {
t, _ := template.New("foo").Parse(`{{define "T"}}hello,{{.}}!{{end}}
username: {{.UserName}}
token: {{.Token}}
`)
err := t.ExecuteTemplate(w,"T",template.HTML("alert('you have been testSession') "))
if err != nil{
log.Println(err)
}
w.Header().Set("Content-Type", "text/html")
// token , session
token := uniqueToken()
sess.Set("token",token)
//
type Result struct {
UserName interface{}
Token interface{}
}
res := Result{
UserName: sess.Get("username"),
Token: token,
}
//
t.Execute(w,res)
} else {
sess_token := sess.Get("token")
//token := r.Form["token"] // token
token := uniqueToken()
if sess_token!=token{
//
fmt.Fprintf(w,"token ")
return
}
sess.Set("username", "testsession")
http.Redirect(w, r, "/", 302)
}
}
func countSession(w http.ResponseWriter,r *http.Request){
sess := globalSessions.SessionStart(w, r)
createtime := sess.Get("createtime")
if createtime == nil{
sess.Set("createtime",time.Now().Unix())
}else if(createtime.(int64) + 3600) < (time.Now().Unix()){
// SID
// 3600 session ,
globalSessions.SessionDestroy(w,r)
sess = globalSessions.SessionStart(w,r)
}
ct := sess.Get("countnum")
if ct == nil{
sess.Set("countnum",1)
}else {
sess.Set("countnum",(ct.(int) + 1))
}
t, _ := template.New("foo").Parse(`{{define "T"}}hello,{{.}}!{{end}}
refresh count:{{.}}
`)
err := t.ExecuteTemplate(w,"T",template.HTML("alert('you have been countSession') "))
if err != nil{
log.Println(err)
}
w.Header().Set("Content-Type","text/html")
t.Execute(w,sess.Get("countnum"))
}
// token
func uniqueToken() string{
h := md5.New()
salt:="sessionss%^7&8888"
io.WriteString(h,salt+time.Now().String())
token:=fmt.Sprintf("%x",h.Sum(nil))
return token
}
func main() {
http.HandleFunc("/",sayhelloName) //
http.HandleFunc("/test",testSession) // session
http.HandleFunc("/count",countSession) // session
err := http.ListenAndServe(":6665",nil) //
if err != nil{
log.Fatal("ListenAndServe:",err)
}
}
サーバの起動:
[root@izj6c4jirdug8kh3uo6rdez goweb]# go mod init goweb
[root@izj6c4jirdug8kh3uo6rdez goweb]# go run testsession.go
globalSessions: &{cookieName:gosessionid lock:{state:0 sema:0} provider:0xaf94a0 maxLifeTime:8}
トップページへのアクセス:http://daily886.com:6665/
セッションへのアクセス:http://daily886.com:6665/test
セッション統計へのアクセス:http://daily886.com:6665/count
参照先:https://www.golang123.com/book/9?chapterID=171
参照先:https://github.com/astaxie/session