I cannot get value from session this way, it is nil:
session := initSession(r)
valWithOutType := session.Values[key]
Full code:
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"log"
"net/http"
)
func main() {
rtr := mux.NewRouter()
rtr.HandleFunc("/setSession", handler1).Methods("GET")
rtr.HandleFunc("/getSession", handler2).Methods("GET")
http.Handle("/", rtr)
log.Println("Listening...")
http.ListenAndServe(":3000", http.DefaultServeMux)
}
func handler1(w http.ResponseWriter, r *http.Request) {
SetSessionValue(w, r, "key", "value")
w.Write([]byte("setSession"))
}
func handler2(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("getSession"))
value := GetSessionValue(w, r, "key")
fmt.Println("value from session")
fmt.Println(value)
}
var authKey = []byte("secret") // Authorization Key
var encKey = []byte("encKey") // Encryption Key
var store = sessions.NewCookieStore(authKey, encKey)
func initSession(r *http.Request) *sessions.Session {
store.Options = &sessions.Options{
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
session, err := store.Get(r, "golang_cookie")
if err != nil {
panic(err)
}
return session
}
func SetSessionValue(w http.ResponseWriter, r *http.Request, key, value string) {
session := initSession(r)
session.Values[key] = value
fmt.Printf("set session with key %s and value %s\n", key, value)
session.Save(r, w)
}
func GetSessionValue(w http.ResponseWriter, r *http.Request, key string) string {
session := initSession(r)
valWithOutType := session.Values[key]
fmt.Printf("valWithOutType: %s\n", valWithOutType)
value, ok := valWithOutType.(string)
if !ok {
fmt.Println("cannot get session value by key: " + key)
}
return value
}
Output:
myMac ~/forStack/session $ go run ./session.go
2015/01/30 16:47:26 Listening...
First I open url http://localhost:3000/setSession and get output:
set session with key key and value value
Then I open url http://localhost:3000/getSession and get output:
valWithOutType: %!s(<nil>)
cannot get session value by key: key
value from session
Why valWithOutType is nil, although I set it requesting /setSession?
Update
I changed code according to #isza answer, but session value is still nil.
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"log"
"net/http"
)
func main() {
rtr := mux.NewRouter()
rtr.HandleFunc("/setSession", handler1).Methods("GET")
rtr.HandleFunc("/getSession", handler2).Methods("GET")
http.Handle("/", rtr)
log.Println("Listening...")
store.Options = &sessions.Options{
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
Path: "/", // to match all requests
}
http.ListenAndServe(":3000", http.DefaultServeMux)
}
func handler1(w http.ResponseWriter, r *http.Request) {
SetSessionValue(w, r, "key", "value")
w.Write([]byte("setSession"))
}
func handler2(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("getSession"))
value := GetSessionValue(w, r, "key")
fmt.Println("value from session")
fmt.Println(value)
}
var authKey = []byte("secret") // Authorization Key
var encKey = []byte("encKey") // Encryption Key
var store = sessions.NewCookieStore(authKey, encKey)
func initSession(r *http.Request) *sessions.Session {
session, err := store.Get(r, "golang_cookie")
if err != nil {
panic(err)
}
return session
}
func SetSessionValue(w http.ResponseWriter, r *http.Request, key, value string) {
session := initSession(r)
session.Values[key] = value
fmt.Printf("set session with key %s and value %s\n", key, value)
session.Save(r, w)
}
func GetSessionValue(w http.ResponseWriter, r *http.Request, key string) string {
session := initSession(r)
valWithOutType := session.Values[key]
fmt.Printf("valWithOutType: %s\n", valWithOutType)
value, ok := valWithOutType.(string)
if !ok {
fmt.Println("cannot get session value by key: " + key)
}
return value
}
What you are probably doing in your init session function with the get method you are restarting the whole session again so every time you do it the session is empty. I did a quick hack around what you wrote to show you where your error is. Please work around this example!
package appSession
import (
"net/http"
"fmt"
"log"
"github.com/gorilla/sessions"
)
var appSession *sessions.Session;
var authKey = []byte("qwer")
var encKey = []byte("asdf")
var store = sessions.NewCookieStore(authKey, encKey)
func initSession(r *http.Request) *sessions.Session {
log.Println("session before get", appSession)
if appSession != nil {
return appSession;
}
session, err := store.Get(r, "golang_cookie")
appSession = session;
log.Println("session after get", session)
if err != nil {
panic(err)
}
return session
}
func SetSessionValue(w http.ResponseWriter, r *http.Request, key, value string) {
session := initSession(r)
session.Values[key] = value
fmt.Printf("set session with key %s and value %s\n", key, value)
session.Save(r, w)
}
func GetSessionValue(w http.ResponseWriter, r *http.Request, key string) string {
session := initSession(r)
valWithOutType := session.Values[key]
fmt.Printf("valWithOutType: %s\n", valWithOutType)
value, ok := valWithOutType.(string)
log.Println("returned value: ", value);
if !ok {
fmt.Println("cannot get session value by key: " + key)
}
return value
}
In your initSession() function you change the store options:
store.Options = &sessions.Options{
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
The Options struct also contains an important Path field to which the cookie will apply. If you don't set it, its default value will be the empty string: "". This will most likely cause that the cookie will not be matched with any of your urls/paths, so your existing session will not be found.
Add a path to match all your urls like this:
store.Options = &sessions.Options{
Path: "/", // to match all requests
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
Also you shouldn't change store.Options in each call of initSession() since you call this in each incoming request. Just set this once when you create your store like this:
var store = sessions.NewCookieStore(authKey, encKey)
func init() {
store.Options = &sessions.Options{
Path: "/", // to match all requests
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
}
As I have not found answer I decided not to use cookie store but use redis store for sessions. And I found full working example here
package main
import (
"fmt"
"github.com/aaudis/GoRedisSession"
"log"
"net/http"
)
var (
redis_session *rsess.SessionConnect
)
func main() {
// Configurable parameters
rsess.Prefix = "sess:" // session prefix (in Redis)
rsess.Expire = 1800 // 30 minute session expiration
// Connecting to Redis and creating storage instance
temp_sess, err := rsess.New("sid", 0, "127.0.0.1", 6379)
if err != nil {
log.Printf("%s", err)
}
redis_session = temp_sess // assing to global variable
http.HandleFunc("/", Root)
http.HandleFunc("/get", Get)
http.HandleFunc("/set", Set)
http.HandleFunc("/des", Des)
http.ListenAndServe(":8888", nil)
}
func Root(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html")
fmt.Fprintf(w, `
Redis session storage example:<br><br>
Store key in session<br>
Get key value from session<br>
Destroy session
`)
}
// Destroy session
func Des(w http.ResponseWriter, r *http.Request) {
s := redis_session.Session(w, r)
s.Destroy(w)
fmt.Fprintf(w, "Session deleted!")
}
// Set variable to session
func Set(w http.ResponseWriter, r *http.Request) {
s := redis_session.Session(w, r)
s.Set("UserID", "1000")
fmt.Fprintf(w, "Setting session variable done!")
}
// Get variable from session
func Get(w http.ResponseWriter, r *http.Request) {
s := redis_session.Session(w, r)
fmt.Fprintf(w, "Value %s", s.Get("UserID"))
}
I played with your code for a long time and finally found out that it does not work, because you set the encryption key to an illegal value.
In the /gorilla/sessions documentation it says:
The encryption key, if set, must be either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 modes.
So since I believe that var encKey = []byte("encKey") simply does not follow this requirement. In turn, the cookie is not set in the first place.
See my code for reference. I basically added some more command line output and used a template with a redirect:
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"html/template"
"log"
"net/http"
)
var authKey = []byte("secret") // Authorization Key
//var encKey = []byte("encKey") // Encryption Key
var store sessions.Store
func main() {
rtr := mux.NewRouter()
rtr.HandleFunc("/setSession/", handler1).Methods("GET")
rtr.HandleFunc("/getSession/", handler2).Methods("GET")
http.Handle("/", rtr)
store = GetCookieStore()
log.Println("Listening...")
http.ListenAndServe(":4000", http.DefaultServeMux)
}
//setting up the cookiestore
func GetCookieStore() sessions.Store {
//maxAge := 3600 * 1 // 1 hour
maxAge := 100
//cookieStore := sessions.NewCookieStore(authKey, encKey)
cookieStore := sessions.NewCookieStore(authKey)
cookieStore.Options.HttpOnly = true
cookieStore.Options.Path = "/" // to match all requests
cookieStore.MaxAge(maxAge)
return cookieStore
}
func handler1(w http.ResponseWriter, r *http.Request) {
t, _ := template.New("foo").Parse(getSessionTemplate)
SetSessionValue(w, r, "key", "value")
session := initSession(r)
fmt.Print("handler1: ")
fmt.Println(session)
Value, ok := session.Values["key"].(string)
if !ok {
fmt.Println("Type assertion to string failed or session value could not be retrieved.")
}
t.Execute(w, Value)
}
func handler2(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("getSession"))
session := initSession(r)
fmt.Print("handler2: ")
fmt.Println(session)
value := GetSessionValue(w, r, "key")
fmt.Println("value from session")
fmt.Println(value)
}
func initSession(r *http.Request) *sessions.Session {
session, err := store.Get(r, "_golang_cookie")
if err != nil {
panic(err)
}
return session
}
func SetSessionValue(w http.ResponseWriter, r *http.Request, key, value string) {
session := initSession(r)
session.Values[key] = value
fmt.Printf("set session with key %s and value %s\n", key, value)
session.Save(r, w)
fmt.Print("setsession: ")
fmt.Println(session)
}
func GetSessionValue(w http.ResponseWriter, r *http.Request, key string) string {
session := initSession(r)
fmt.Print("getsession: ")
fmt.Println(session)
valWithOutType := session.Values[key]
fmt.Printf("valWithOutType: %s\n", valWithOutType)
value, ok := valWithOutType.(string)
if !ok {
fmt.Println("cannot get session value by key: " + key)
}
return value
}
var getSessionTemplate = `
<p><label>Session value set:</label></p>
<p><label>Value: is now: {{.}}</label></p>
<p>Getsession</p>`
Related
This code has two parts. One is to set and clear the session and the second part is login and logout.
What it does?
In the second part, If an email and password are found in the database and the match is true then it set the session and move to the about() function which has an about file. If the logout is called then it clears the session and redirects to the home page.
What it should do?
The problem is that even if I am logged out and the session is cleared, I can still visit an about page. I don't want to be allowed to visit an about page if I am not logged in.
First part
var cookieHandler = securecookie.New(
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32),
)
func setSession(email, password string, res http.ResponseWriter) {
value := map[string]string{ "email": email, "password": password}
encoded, err := cookieHandler.Encode("session", value)
if err == nil {
cookie := &http.Cookie{ Name: "session", Value: encoded, Path: "/"}
http.SetCookie(res, cookie)
}
}
func clearSession(res http.ResponseWriter) {
cookie := &http.Cookie{ Name: "session", Value: "", Path: "/", MaxAge: -1}
http.SetCookie(res, cookie)
}
Second part
func about(res http.ResponseWriter, req *http.Request) {
if err := tmpl.ExecuteTemplate(res, "about.html", nil); err != nil {
log.Fatal("template didn't execute", nil)
}
}
func loginAuth(res http.ResponseWriter, req *http.Request) {
email := req.FormValue("email")
password := req.FormValue("password")
match := database.Findaccount(email, password)
if match == true {
setSession(email, password, res)
about(res, req)
fmt.Println("You're logged in")
} else {
tmpl.ExecuteTemplate(res, "login.html", nil)
fmt.Println("Enter the correct email or password")
}
}
func logout(res http.ResponseWriter, req *http.Request) {
clearSession(res)
http.Redirect(res, req, "/", 302)
}
Few things you don't want to do, in general:
Don't use cookie encoder directly. Use a cookie session store.
Don't call an handler within an handler, prefer a redirect. This should prevent writing twice the headers/body on the response.
Don't pass the user/password in the cookie, even encoded, in 2021 we may even prevent sending that through the form at all (you might consider sending only a hash and re hash the hash on the server side to figure out if things are good to go).
Few things you always want to do:
Write tests.
Make use of middlewares.
Always provide small reproducible examples.
That being said, I have written a lookalike code with some stubs (mostly for db), I removed template support (i was not in the mood to write HTML) and more importantly I wrote tests !!
To the question How to clear the session :
Delete the values from the store, write the store
To the question and only visit an about page after login?:
Wrap that handler with a middleware that verifies login data attached to the user cookie store
-- main.go --
package main
import (
"crypto/sha256"
"encoding/gob"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
)
// Note: Don't store your key in your source code. Pass it via an
// environmental variable, or flag (or both), and don't accidentally commit it
// alongside your code. Ensure your key is sufficiently random - i.e. use Go's
// crypto/rand or securecookie.GenerateRandomKey(32) and persist the result.
var store = sessions.NewCookieStore(
securecookie.GenerateRandomKey(32),
)
//emulate db package
func dbLookupUser(user, pwd string) bool {
return user == "user" && pwd == "pwd"
}
func dbLookupHash(h string) bool {
return h == hash("user", "pwd")
}
func hash(s ...interface{}) string {
hr := sha256.New()
fmt.Fprint(hr, s...)
return fmt.Sprintf("%x", hr.Sum(nil))
}
// hashKey is a typed key for the session map store to prevent unintented overwrites.
type hashKey string
func init() {
gob.Register(hashKey(""))
}
func loginAuth(res http.ResponseWriter, req *http.Request) {
email := req.FormValue("email")
password := req.FormValue("password")
match := dbLookupUser(email, password)
if match == true {
session, _ := store.Get(req, "session-name")
session.Values["hash"] = hash(email, password)
// Save it before we write to the response/return from the handler.
err := session.Save(req, res)
if err == nil {
// about(res, req) // don't!
// the about handler might want to setup its own http response headers
// That would conflict with what we did here.
// prefer a redirect
http.Redirect(res, req, "/about", http.StatusFound)
return
}
} else {
fmt.Fprintf(res, "try again") // use a templatee instead!
// tmpl.ExecuteTemplate(res, "login.html", nil)
}
}
func logout(res http.ResponseWriter, req *http.Request) {
session, _ := store.Get(req, "session-name")
delete(session.Values, hashKey("hash"))
_ = session.Save(req, res)
http.Redirect(res, req, "/", 302)
}
func about(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "welcome to about page")
}
func requireLogin(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-name")
var h string
if x, ok := session.Values[hashKey("hash")]; ok {
h = x.(string)
}
var match bool
if h != "" {
match = dbLookupHash(h)
}
if !match {
// Write an error and stop the handler chain
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next(w, r)
}
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", loginAuth)
r.HandleFunc("/logout", logout)
r.HandleFunc("/about", requireLogin(about))
log.Fatal(http.ListenAndServe("localhost:8080", r))
}
-- main_test.go --
package main
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func TestLogin(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
form := url.Values{}
form.Set("email", "user")
form.Set("password", "pwd")
req.Form = form
w := httptest.NewRecorder()
loginAuth(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusFound; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
func TestLoginFailure(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
form := url.Values{}
form.Set("email", "!user")
form.Set("password", "!pwd")
req.Form = form
w := httptest.NewRecorder()
loginAuth(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusOK; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
func TestAboutNotLogged(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
w := httptest.NewRecorder()
requireLogin(about)(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusForbidden; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
func TestAboutLogged(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/foo", nil)
w := httptest.NewRecorder()
session, _ := store.Get(req, "session-name")
session.Values[hashKey("hash")] = hash("user", "pwd")
err := session.Save(req, w)
if err != nil {
t.Fatal(err)
}
hdr := w.Header()
req.Header.Add("Cookie", hdr["Set-Cookie"][0])
w = httptest.NewRecorder()
requireLogin(about)(w, req)
resp := w.Result()
// body, _ := io.ReadAll(resp.Body)
if wanted := http.StatusOK; resp.StatusCode != wanted {
t.Fatalf("invalid response code, got=%v wanted=%v", resp.StatusCode, wanted)
}
// implement more check
}
I'm having an issue with gorilla/sessions. My sessions aren't being saved when sessions.NewCookieStore([]byte("veryprivatekey")) is from a different package/exported variable.
Here is what works:
What works
exampleproj/main.go
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"net/http"
)
var store *sessions.CookieStore
var cookie_name = "sess"
func main() {
store = sessions.NewCookieStore([]byte("veryprivatekey"))
router := mux.NewRouter()
router.HandleFunc("/cookietest", cookietest)
router.HandleFunc("/cookietest2", cookietest2)
err := http.ListenAndServe(":8080", router)
if err != nil {
fmt.Print(err)
}
}
var cookietest = func(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, cookie_name)
if err != nil {
fmt.Println(err)
}
session.Values["Stack"] = "Overflow"
err = session.Save(r,w)
if err != nil {
fmt.Println(err)
}
}
var cookietest2 = func(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, cookie_name)
if err != nil {
fmt.Println(err)
}
fmt.Println("Stack: ", session.Values["Stack"])
}
(open localhost:8080/cookietest and check that a cookie was created)
The issue
Here is some code where sessions.CookieStore is from another package:
exampleproj/main.go
package main
import (
"expirements/example"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
var cookie_name = "sess"
func main() {
router := mux.NewRouter()
router.HandleFunc("/cookietest", cookietest)
router.HandleFunc("/cookietest2", cookietest2)
err := http.ListenAndServe(":8080", router)
if err != nil {
fmt.Print(err)
}
}
var cookietest = func(w http.ResponseWriter, r *http.Request) {
session, err := example.Store.Get(r, cookie_name)
if err != nil {
fmt.Println(err)
}
session.Values["Stack"] = "Overflow"
err = session.Save(r,w)
if err != nil {
fmt.Println(err)
}
}
var cookietest2 = func(w http.ResponseWriter, r *http.Request) {
session, err := example.Store.Get(r, cookie_name)
if err != nil {
fmt.Println(err)
}
fmt.Println("Stack: ", session.Values["Stack"])
}
exampleproj/example/ex.go
package example
import (
"github.com/gorilla/sessions"
)
var Store *sessions.CookieStore
func init() {
Store = sessions.NewCookieStore([]byte("veryprivatekey"))
Store.Options = &sessions.Options{
//Domain: config.Host,
//Path: "/",
MaxAge: 259200,
Secure: true,
HttpOnly: true,
}
}
func GetStore() *sessions.CookieStore {
return Store
}
No cookies were set. I've also tried using GetStore() instead of example.Store, but that didn't work either. Am I supposed to declare a sessions.NewCookieStore for each package?
Solution is here - surprised I couldn't find more stackoverflow questions about this.
I am using a basic wrapper in a web application like this:
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
)
const (
PORT = "8083"
)
func navbarWrapper(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "header")
h(w, r) // call original function
fmt.Fprintln(w, "footer")
}
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/cookie_write", navbarWrapper(cookie_write))
hostname, _ := os.Hostname()
log.Printf("Listening on port %s https://%s:%s/", PORT, hostname, PORT)
http.ListenAndServe(":"+PORT, r)
}
I would like to set a cookie, but it is not being sent to the browser, but is also not providing an error
var (
key = []byte("16bytestufffffff")
store = sessions.NewCookieStore(key)
)
func cookie_write(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session-name")
session.Values["authenticated"] = true
session.Values["stuff"] = "important info"
if err := session.Save(r, w); err != nil {
fmt.Println(err)
fmt.Println("error")
} else {
fmt.Println("worked?")
}
}
If I remove the wrapper, then it works fine and I can see the cookie being generated in the browser:
r.HandleFunc("/cookie_write", cookie_write)
I know I must not be saving the session correctly, but I can't figure out how to do it.
Got stuck while passing multiple values in cookie. I'm not able to find a better way for managing session and cookies. Trying to use github.com/gorilla/securecookie this package.
loginMain.go
package main
import (
"database/sql"
"log"
"net/http"
"shambhavi/packages/loginPkg"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
var router = mux.NewRouter()
var db *sql.DB
func connectDb() *sql.DB {
db, dberr := sql.Open("mysql", "root:root#tcp(127.0.0.1:8889)/shambhavi_db")
if dberr != nil {
log.Println(dberr)
}
return db
}
func login(w http.ResponseWriter, r *http.Request) {
var db *sql.DB = connectDb()
loginPkg.LoginOperation(w, r, db)
}
func main() {
http.HandleFunc("/demo", login)
http.Handle("/", router)
err := http.ListenAndServe(port, nil) // setting listening port
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
LoginPkg.go
package loginPkg
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"shambhavi/packages/sessionPkg"
_ "github.com/go-sql-driver/mysql"
)
var retMap = make(map[string]string)
func ErrorHandler(err error) {
if err != nil {
panic(err)
}
}
func LoginOperation(w http.ResponseWriter, r *http.Request, db *sql.DB) {
fmt.Println("In LoginOperation ")
r.ParseForm()
if len(r.Form["username"][0]) == 0 && len(r.Form["password"][0]) == 0 {
fmt.Fprintf(w, "Something is blank !!!")
} else {
var lvl string
var uFullName string
err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)
er := db.QueryRow("SELECT emp_name FROM emp_detail WHERE emp_uname = ?", r.FormValue("username")).Scan(&uFullName)
ErrorHandler(er)
retMap["msg"] = "Login successfully"
retMap["err"] = "Not Login"
retMap["lvl"] = lvl
retMap["fullName"] = uFullName
b, _ := json.Marshal(retMap)
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, "%s", b)
} else {
if lvl == "1" || lvl == "2" || lvl == "3" {
sessionPkg.SetSession(w, r, r.FormValue("username"), retMap) // Passing map to the fun, retMap
fmt.Fprintf(w, "%s", b)
usrnm := sessionPkg.GetUserName(r)
fmt.Println("From session variable", usrnm)
} else {
fmt.Println("Chukala ....")
fmt.Fprintf(w, "%s", b)
}
}
}
defer db.Close()
}
The problem lies in following file....
sessionHandler.go
package sessionPkg
import (
"fmt"
"net/http"
"time"
"github.com/gorilla/securecookie"
)
var cookieHandler = securecookie.New(
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32))
func SetSession(w http.ResponseWriter, r *http.Request, username string, retMap map[string]string) {
sessionData := map[string]string{
"userName": username,
"lvl": retMap["lvl"],
"fullName": retMap["fullName"],
}
expiration := time.Now().Add(365 * 24 * time.Hour)
//if encoded, err := cookieHandler.Encode("session", sessionData); err == nil {
cookie := http.Cookie{
Name: "session",
Value: sessionData["userName"], //Here i want map or something else that can accept multiple values
Expires: expiration,
//MaxAge: 3600,
}
http.SetCookie(w, &cookie)
//}
}
func GetUserName(request *http.Request) (userName string) {
//fmt.Println(request.Cookie("session"))
cookieValue := make(map[string]string)
if cookie, err := request.Cookie("session"); err == nil {
fmt.Println("cookieValue = ", cookie.Value)
//if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
//fmt.Println(cookie)
cookieValue["userName"] = cookie.Value
//fmt.Println(cookieValue["userName"])
//}
/*else {
fmt.Println("Error ", err)
}*/
}
return cookieValue["userName"]
}
/*func GetFullName(request *http.Request) (fullName string) {
fmt.Println("In GetFullName")
cookieValue := make(map[string]string)
if cookie2, err := request.Cookie("session"); err == nil {
fmt.Println("cookieValue = ", cookie2.Value)
//if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
fmt.Println(cookie2)
cookieValue["fullName"] = cookie2.Value
fmt.Println(cookieValue["fullName"])
//}
}
return cookieValue["fullName"]
}*/
func ClearSession(response http.ResponseWriter) {
cookie := &http.Cookie{
Name: "session",
Value: "",
Path: "/",
MaxAge: -1,
}
http.SetCookie(response, cookie)
}
Problem indicated in code by comment. I want to use session like in PHP. Suggest the better way to secure the cookie and maintain the session. Give some explanation too.
Edited: Explain cookieHandler.Encode() and cookieHandler.Decode(). It is not decoding the data which is passed to it.
var retMap = make(map[string]string) is a global map that you both read and write to, which is unsafe: when you have more than one user concurrently you will be overwriting the contents of this map.
You aren't using the securecookie package to encode your cookie values at all - in fact it's not clear where you're using it at all.
cookie := http.Cookie{
Name: "session",
// This should be encoded.
Value: sessionData["userName"], //Here i want map or something else that can accept multiple values
Expires: expiration,
//MaxAge: 3600,
}
Too many packages: you have a loginpackage, a sessionpackage and a package main. Packages should fulfil a 'theme' - an auth package might make more sense, or even just a single package main until you get more familiar with Go.
You're not hashing your passwords - storing the plain text password in the database and looking it up with the value of r.FormValue("password") is extremely insecure. Read this to see how to hash passwords safely in Go: Golang/App Engine - securely hashing a user's password
You should be using the gorilla/sessions package rather than the lower-level securecookie package.
Modifying the gorilla/sessions example in the documentation:
package main
import (
"net/http"
"github.com/gorilla/sessions"
)
// Use the CookieStore
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func LoginOperation(w http.ResponseWriter, r *http.Request) {
// Your existing DB code - shorter to make the example here clearer
err := db.QueryRow("SELECT lvl_flag FROM admin_instance WHERE user_name = ? AND passwd = ?", r.FormValue("username"), r.FormValue("password")).Scan(&lvl)
// Don't use a global map - create a new one
userDetails := make(map[string]string)
userDetails["msg"] = "Login successfully"
userDetails["err"] = "Not Login"
userDetails["lvl"] = lvl
userDetails["fullName"] = uFullName
// Get a session (existing/new)
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Set some session values.
session.Values["userDetails"] = userDetails
// Save it before we write to the response/return from the handler.
session.Save(r, w)
}
Later, if you want to retrieve the details:
func RequireAuth(w http.ResponseWriter, r *http.Request) {
// Get a session (existing/new)
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Type assert our userDetails map out of the session's map[string]interface{}
userDetails, ok := session.Values["userDetails"].(map[string]string)
if !ok {
// User does not have an existing session - treat them as not logged in and/or re-direct them to your login page.
http.Error(w, http.StatusCode(401), 401)
return
}
// Check the user details - e.g. if userDetails["lvl"] == "ACTIVE" { ... }
// TODO
}
I have imported packages as
import (
"github.com/gorilla/sessions"
"github.com/gorilla/mux"
//CORS
"github.com/rs/cors"
"github.com/justinas/alice"
)
and defined store and main method as follow
var store = sessions.NewCookieStore([]byte("something-very-secret"))
const My_UI="http://localhost:3000"
func init() {
store.Options = &sessions.Options{
Path: "/",
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
}
var router = mux.NewRouter() //MUX Handeler
//MAIN Function
func main() {
c := cors.New(cors.Options{
AllowedOrigins: []string{My_UI},
})
router.HandleFunc("/submitted",Login)
router.HandleFunc("/check",GetSession)
http.Handle("/", router)
chain := alice.New(c.Handler).Then(router) //CORS enable
fmt.Println("server started at port 8080")
http.ListenAndServe(":8080", chain)
}
In my method I’ve created and set session value as describe in gorilla doc
func Login(w http.ResponseWriter, r *http.Request) {
fmt.Println("In login----------->")
sess := GetCon() //get connection session
defer sess.Close() //close session
c := sess.DB("mydb").C("users") //collection-> select db table
session1, _ := store.Get(r, "loginSession") //login session
//parse json data
form := LoginUser{}
err := json.NewDecoder(r.Body).Decode(&form)
if err !=nil {
fmt.Println(err)
}
//get query data
var result []Person
errc1 := c.Find(bson.M{"email":form.Email,"password":form.Password}).All(&result)
if errc1 != nil {
js, err2 := json.Marshal("false")
if err2 != nil{return}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
} else {
if len(result)==0 {
if err2 != nil {
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
} else {
fmt.Println("Success")
session1.Values["foo"] = "bar"
session1.Save(r, w)
fmt.Println("saved",session1)
js, err2 := json.Marshal(&result[0].Id)
if err2 != nil {return}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
}
}
}
Now if i want to get this session value in another method i got nil every time. don't know what goes wrong in my code.
func GetSession(w http.ResponseWriter, r *http.Request) {
session1, _ := store.Get(r, "loginSession")
fmt.Println("Session in SessionHandler",session1)
if session.Values["foo"] == nil {
fmt.Println("not found",session.Values["foo"]))
} else {
fmt.Println("value",session.Values["foo"])
}
}
You got a mistake at your GetSession function. Please change session variable to session1
Also to check if session value is present better do it this way:
session, err := store.Get(r, ssid)
if err == nil {
if value, ok := session.Values["foo"].(string); ok {
session_data = value
}
}
I don't know what value you what to get, but I assume you want a string value. I wrote simple func GetFoo() to get string value from session1.Values["foo"].
Full example below:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/justinas/alice"
"github.com/rs/cors"
)
var store = sessions.NewCookieStore([]byte("something-very-secret"))
const My_UI = "http://localhost:3000"
var router = mux.NewRouter() //MUX Handeler
//MAIN Function
func init() {
store.Options = &sessions.Options{
Path: "/",
MaxAge: 3600 * 1, // 1 hour
HttpOnly: true,
}
}
func main() {
c := cors.New(cors.Options{
AllowedOrigins: []string{My_UI},
})
router.HandleFunc("/login", Login)
router.HandleFunc("/check", GetSession)
http.Handle("/", router)
chain := alice.New(c.Handler).Then(router) //CORS enable
fmt.Println("server started at port 8080")
http.ListenAndServe(":8080", chain)
}
func GetFoo(f interface{}) string {
if f != nil {
if foo, ok := f.(string); ok {
return foo
}
}
return ""
}
func GetSession(w http.ResponseWriter, r *http.Request) {
session1, _ := store.Get(r, "loginSession")
foo := GetFoo(session1.Values["foo"])
if foo == "" {
fmt.Println("Foo is not set! Login to set value.")
} else {
fmt.Println("Foo Value:", foo, ".")
}
}
func Login(w http.ResponseWriter, r *http.Request) {
// Save Foo
session1, _ := store.Get(r, "loginSession")
session1.Values["foo"] = "bar"
session1.Save(r, w)
}