微服务 session 管理

      微服务体系结构是服务器应用程序体系结构中的新热点,它具有多种优势,包括易于扩展和在一个应用程序中使用多种编程语言的能力。但是我们知道,没有免费的午餐!这种灵活性会带来成本,并带来一些传统“整体式”应用程序所没有的挑战。在本文中,我们将研究一个挑战:跨服务共享会话。

传统单体软件的session管理

传统session 管理采用cookie和session 技术来实现网站的安全访问的。

      Cookie技术是客户端的解决方案,Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。

  这个过程如下图所示:

另一方面,会话将历史信息存储在服务器端。服务器使用会话ID来标识不同的会话,并且服务器生成的会话ID应该始终是随机且唯一的。您可以使用Cookie或URL参数来获取客户端的身份。

    session 信息是存放再服务器短的。具体地,它可以存放在内存,存放再文件系统中,或者数据库中。当你采用Go 语言编程的时候,可以使用:  github.com/gorilla/sessions 包

具体内容访问:http://www.gorillatoolkit.org/pkg/sessions

Go 代码

使用文件存储session 信息的例子。完整的代码参见:https://github.com/CurtisVermeeren/Gorilla-Sessions-Tutorial

微服务架构下session 管理面临的问题

当单一程序转换到微服务架构时,问题就来了。每个微服务都有web 服务器。显然不能各自搞一套session 管理是不现实的。

cookie 是否可共享? 

     访问多个微服务的cookie 消息是否能构共享?具体地讲,就是访问不同微服务时,是否上传同一个cookie?网上有许多说法,我看老外的描述,并且写程序验证表明:对于同一个主机上的不同端口上的web服务是可以共享cookie 的。cookie 只针对主机,而不针对端口。

session 是否可以共享?

在一个微服务器上身份验证 ,session 信息如何与其它微服务共享?可以使用共享文件或者采用数据库实现,由于我们的系统中采用了mongoDB数据库,所以我们使用mongodb 存储session信息。

在:https://github.com/kidstuff/mongostore有相应的session store 模块

Go 代码

该代码是在newfilestore 的例中修改而成的。

package main

import (
	"net/http"
	"fmt"
	"encoding/gob"
	"github.com/gorilla/sessions"
	"github.com/kidstuff/mongostore"
	"github.com/globalsign/mgo"
	"log"
)
type User struct {
	Username      string
	Authenticated bool
}
func loginHandle(rw http.ResponseWriter, req *http.Request) {
        username:=req.FormValue("username")
        password:=req.FormValue("password") 
        fmt.Println(username)
		fmt.Println(password)
        // Fetch new store.
        dbsess, err := mgo.Dial("localhost")
        if err != nil {
            panic(err)
        }
        defer dbsess.Close()

        store := mongostore.NewMongoStore(dbsess.DB("test").C("test_session"), 3600, true,
            []byte("cookie-name"))

        // Get a session.
        session, err := store.Get(req, "cookie-name")
        if err != nil {
            log.Println(err.Error())
        }

        // Add a value.
        user := &User{
            Username:      username,
            Authenticated: true,
        }
        session.Values["user"] = user

        // Save.
        if err = sessions.Save(req, rw); err != nil {
            log.Printf("Error saving session: %v", err)
        }

       // fmt.Fprintln(rw, "ok")
       http.Redirect(rw, req, "/secret", http.StatusFound)
	}
func secretHandle(rw http.ResponseWriter, req *http.Request){
    dbsess, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer dbsess.Close()

    store := mongostore.NewMongoStore(dbsess.DB("test").C("test_session"), 3600, true,
        []byte("cookie-name"))
    session, err := store.Get(req, "cookie-name")
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
    }
    user := getUser(session)
    fmt.Println(user.Username)
    if auth := user.Authenticated; !auth {
		session.AddFlash("You don't have access!")
		err = session.Save(req, rw)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Redirect(rw, req, "/forbidden.html", http.StatusFound)
		return
	}
    http.Redirect(rw, req, "/secretcontent.html", http.StatusFound)
} 
func logoutHandle(rw http.ResponseWriter, req *http.Request){
    dbsess, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer dbsess.Close()

    store := mongostore.NewMongoStore(dbsess.DB("test").C("test_session"), 3600, true,
        []byte("cookie-name"))
    session, err := store.Get(req, "cookie-name")
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
    }
    session.Values["user"] =  User{}
	session.Options.MaxAge = -1

	err = session.Save(req, rw)
	if err != nil {
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(rw, req, "/", http.StatusFound)
}
func getUser(s *sessions.Session) User {
	val := s.Values["user"]
	var user = User{}
	user, ok := val.(User)
	if !ok {
		return User{Authenticated: false}
	}
	return user
}	
func main() {
    fmt.Printf("sessions test\n")
    gob.Register(User{})
    fs := http.FileServer(http.Dir("./www"))
    http.Handle("/", fs)
    http.HandleFunc("/login",loginHandle)
    http.HandleFunc("/logout",logoutHandle)
    http.HandleFunc("/secret",secretHandle)
	http.ListenAndServe(":8900", nil)
}

实验时,运行两个相同的程序,分别是8900和8901 端口,一个程序login 另一个访问 http:localhost:8901/secret 能访问到

secret content

meeting start at AM 6:00

的页面。

有问题,请留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值