2014-02-18 2 views
10

Переменные сеанса не поддерживаются по запросу при использовании веб-инструментария сессий горилл. Когда я запускаю сервер и набираю localhost: 8100/page направляется на login.html, поскольку значения сеанса не существуют. После входа в систему я устанавливаю переменную сеанса в хранилище, и страница перенаправляется на home.html. Но когда я открываю новую вкладку и набираю localhost: 8100/страница должна быть направлена ​​на home.html, используя уже сохраненные переменные сеанса, но страница вместо этого перенаправляется на login.html. Ниже приведен код.Переменные сеансов в golang не сохраняются при использовании сессий горилл

package main 

import (
    "crypto/md5" 
    "encoding/hex" 
    "fmt" 
    "github.com/gocql/gocql" 
    "github.com/gorilla/mux" 
    "github.com/gorilla/sessions" 
    "net/http" 
    "time" 
) 

var store = sessions.NewCookieStore([]byte("something-very-secret")) 

var router = mux.NewRouter() 

func init() { 

    store.Options = &sessions.Options{ 
     Domain: "localhost", 
     Path:  "/", 
     MaxAge: 3600 * 1, // 1 hour 
     HttpOnly: true, 
    } 
} 
func main() { 
    //session handling 
    router.HandleFunc("/", SessionHandler) 
    router.HandleFunc("/signIn", SignInHandler) 
    router.HandleFunc("/signUp", SignUpHandler) 
    router.HandleFunc("/logOut", LogOutHandler) 
    http.Handle("/", router) 
    http.ListenAndServe(":8100", nil) 
} 

//handler for signIn 
func SignInHandler(res http.ResponseWriter, req *http.Request) { 

    email := req.FormValue("email") 
    password := req.FormValue("password") 

    //Generate hash of password 
    hasher := md5.New() 
    hasher.Write([]byte(password)) 
    encrypted_password := hex.EncodeToString(hasher.Sum(nil)) 

    //cassandra connection 
    cluster := gocql.NewCluster("localhost") 
    cluster.Keyspace = "gbuy" 
    cluster.DefaultPort = 9042 
    cluster.Consistency = gocql.Quorum 
    session, _ := cluster.CreateSession() 
    defer session.Close() 

    //select query 
    var firstname string 
    stmt := "SELECT firstname FROM USER WHERE email= '" + email + "' and password ='" + encrypted_password + "';" 
    err := session.Query(stmt).Scan(&firstname) 
    if err != nil { 
     fmt.Fprintf(res, "failed") 
    } else { 
     if firstname == "" { 
      fmt.Fprintf(res, "failed") 
     } else { 
      fmt.Fprintf(res, firstname) 
     } 
    } 

    //store in session variable 
    sessionNew, _ := store.Get(req, "loginSession") 

    // Set some session values. 
    sessionNew.Values["email"] = email 
    sessionNew.Values["name"] = firstname 

    // Save it. 
    sessionNew.Save(req, res) 
    //store.Save(req,res,sessionNew) 

    fmt.Println("Session after logging:") 
    fmt.Println(sessionNew) 

} 

//handler for signUp 
func SignUpHandler(res http.ResponseWriter, req *http.Request) { 

    fName := req.FormValue("fName") 
    lName := req.FormValue("lName") 
    email := req.FormValue("email") 
    password := req.FormValue("passwd") 
    birthdate := req.FormValue("date") 
    city := req.FormValue("city") 
    gender := req.FormValue("gender") 

    //Get current timestamp and format it. 
    sysdate := time.Now().Format("2006-01-02 15:04:05-0700") 

    //Generate hash of password 
    hasher := md5.New() 
    hasher.Write([]byte(password)) 
    encrypted_password := hex.EncodeToString(hasher.Sum(nil)) 

    //cassandra connection 
    cluster := gocql.NewCluster("localhost") 
    cluster.Keyspace = "gbuy" 
    cluster.DefaultPort = 9042 
    cluster.Consistency = gocql.Quorum 
    session, _ := cluster.CreateSession() 
    defer session.Close() 

    //Insert the data into the Table 
    stmt := "INSERT INTO USER (email,firstname,lastname,birthdate,city,gender,password,creation_date) VALUES ('" + email + "','" + fName + "','" + lName + "','" + birthdate + "','" + city + "','" + gender + "','" + encrypted_password + "','" + sysdate + "');" 
    fmt.Println(stmt) 
    err := session.Query(stmt).Exec() 
    if err != nil { 
     fmt.Fprintf(res, "failed") 
    } else { 
     fmt.Fprintf(res, fName) 
    } 
} 

//handler for logOut 
func LogOutHandler(res http.ResponseWriter, req *http.Request) { 
    sessionOld, err := store.Get(req, "loginSession") 

    fmt.Println("Session in logout") 
    fmt.Println(sessionOld) 
    if err = sessionOld.Save(req, res); err != nil { 
     fmt.Println("Error saving session: %v", err) 
    } 
} 

//handler for Session 
func SessionHandler(res http.ResponseWriter, req *http.Request) { 

    router.PathPrefix("/").Handler(http.FileServer(http.Dir("../static/"))) 
    session, _ := store.Get(req, "loginSession") 

    fmt.Println("Session in SessionHandler") 
    fmt.Println(session) 


    if val, ok := session.Values["email"].(string); ok { 
     // if val is a string 
     switch val { 
     case "": { 
      http.Redirect(res, req, "html/login.html", http.StatusFound) } 
     default: 
      http.Redirect(res, req, "html/home.html", http.StatusFound) 
     } 
    } else { 
     // if val is not a string type 
     http.Redirect(res, req, "html/login.html", http.StatusFound) 
    } 
} 

Может кто-нибудь сказать мне, что я делаю неправильно. Заранее спасибо.

ответ

20

Прежде всего: вы никогда не должны использовать md5 для хэш-паролей. Read this article о том, почему, а затем используйте Go's bcrypt package. Вы также должны parameterise your SQL queries иначе вы открыты для катастрофических SQL-инъекций.

В любом случае: есть несколько проблем, вам необходимо обратиться здесь:

  • Ваши сессии не «торчит» является то, что вы устанавливаете Path как /loginSession - поэтому, когда пользователь посещает какой-либо другой путь (т.е. /), сеанс недействителен для этой области.

Вы должны быть настройки магазина сеанса по программе инициализации и настройки параметров есть:

var store = sessions.NewCookieStore([]byte("something-very-secret")) 

func init() { 

    store.Options = &sessions.Options{ 
    Domain: "localhost", 
    Path:  "/", 
    MaxAge: 3600 * 8, // 8 hours 
    HttpOnly: true, 
} 

Причина вы могли бы установить более конкретный путь, если авторизованные пользователи всегда в суб- маршрут, как /accounts. В вашем случае это не то, что происходит.

Я должен добавить, что вкладка «Ресурс» Chrome в веб-инспекторе (Ресурсы> Куки-файлы) невероятно полезна для отладки таких проблем, как вы можете видеть истечение срока действия cookie, путь и другие настройки.

  • Вы также проверяете session.Values["email"] == nil, что не работает. Пустая строка в Go просто "", и потому, что session.Values является map[string]interface{}, вам нужно type assert значение в строку:

т.е.

if val, ok := session.Values["email"].(string); ok { 
     // if val is a string 
     switch val { 
      case "": 
       http.Redirect(res, req, "html/login.html", http.StatusFound) 
      default: 
       http.Redirect(res, req, "html/home.html", http.StatusFound) 
     } 
    } else { 
     // if val is not a string type 
     http.Redirect(res, req, "html/login.html", http.StatusFound) 
    } 

мы имеем дело с «не строка» дела поэтому мы четко говорим о том, что программа должна делать, если сеанс не так, как мы ожидали (клиент изменил его, или более старая версия нашей программы использовала другой тип).

  • Вы не проверяете ошибки при сохранении сеансов.

    sessionNew.Save(req, res) 
    

...должно быть:

err := sessionNew.Save(req, res) 
    if err != nil { 
      // handle the error case 
    } 
  • Вы должны получить/проверить сессию в SessionHandlerдо, обслуживающих статические файлы (вы делаете это в очень окольным путем, однако):

    func SessionHandler(res http.ResponseWriter, req *http.Request) { 
        session, err := store.Get(req, "loginSession") 
        if err != nil { 
         // Handle the error 
        } 
    
        if session.Values["email"] == nil { 
         http.Redirect(res, req, "html/login.html", http.StatusFound) 
        } else { 
         http.Redirect(res, req, "html/home.html", http.StatusFound) 
        } 
        // This shouldn't be here - router isn't scoped in this function! You should set this in your main() and wrap it with a function that checks for a valid session. 
        router.PathPrefix("/").Handler(http.FileServer(http.Dir("../static/"))) 
    } 
    
+0

Я добавил init() и удалил session.Options, который устанавливал путь к/loginSession, но он все еще не работает. – tushR

+0

@tushR Я обновил свой ответ. – elithrar

+0

Он по-прежнему не работает. Я добавил операторы печати, печатающие переменную сеанса в обработчике сеанса и после входа в систему, это то, что я получил в качестве вывода. сессия в SessionHandler & {Карты [] 0xc2000f3330 правдой 0xc2000c0ea0 loginSession} сессия после лесозаготовок: & {карты [адрес электронной почты: [email protected] имени: Tushar] 0xc2001a7930 правды сессии в SessionHandler & {карта [ ] 0xc2001a7c00 true 0xc2000c0ea0 loginSession} Последний обработчик сеанса печатается, когда вы открываете новую вкладку и вводите localhost: 8100/ – tushR

1

Следуя цепочке комментариев, попробуйте удалить ограничение Domain из параметров сеанса или заменить его полным доменным именем, которое разрешает (используя /etc/hosts например).

Это, кажется, ошибка в Chromium, где не отправляются файлы cookie с явным доменом «localhost». Проблема, похоже, не представлена ​​в Firefox.

Я был в состоянии получить демо работает, используя

store.Options = &sessions.Options{ 
    // Domain: "localhost", 
    MaxAge: 3600 * 1, // 1 hour 
    HttpOnly: true, 
} 
+0

Я использую Firefox и после удаления домена, он все еще не работает. Проблема store.get() в функции SessionHandler не возвращает сохраненный сеанс, который мы сохраняем в LoginHandler. – tushR

+0

Удалите существующий файл cookie из своего браузера, а затем установите «Domain:» «' (что плохо, и это нужно делать только при тестировании!). Мне удалось получить эту работу после этого и жестко кодировать значения сеанса: '/' маршрут перенаправлен на '/ html/home.html'. При удалении сеанса он перенаправлялся на '/ html/login.html'. В вашем приложении все еще много проблем: вы не должны повторно подключаться к БД при каждом запросе; проверьте свои ошибки сеанса; и ваш статический файловый сервер ничего не делает. Прочитайте http://golang.org/doc/articles/wiki/ и посмотрите, как это будет раньше. – elithrar

+0

У меня есть html и angularjs в интерфейсе и golang на бэкэнд, так как можно настроить переменные сеанса в golang как куки в браузере. Нужно ли устанавливать переменные сеанса из golang в файлы cookie в код angularjs? У меня не так много опыта в интерфейсе, поэтому вы можете объяснить поток создания сеансов на бэкэнд и получить их отражение на лицевой стороне. – tushR

4

Проблема заключается в том что вы пишете в ответ перед вызовом session.Save. Это предотвращает запись заголовков и, таким образом, ваш файл cookie отправляется клиенту.

В коде после session.Query вы вызываете Fprintf на ответ, как только этот код исполняется, вызов sessionNew.Save практически ничего не делает. Удалите любой код, который записывается в ответ, и повторите попытку.

Я думаю, что сессия gorilla toolkit должна возвращать ошибку при вызове Save, если ответ уже написан.

+1

У меня была проблема, когда мой cookie не устанавливался с использованием горилл/сессий. Ваш ответ был на месте. Благодаря! – Gavin

0

Используйте серверную часть «FilesystemStore» вместо «CookieStore», чтобы сохранить переменные сеанса. Другой альтернативой было бы обновление сеанса как контекстной переменной для запроса, то есть сохранение сеанса в контексте и разрешение браузера передавать его в каждом запросе, используя context.Set() из пакета gorilla/context.

Использование «CookieStore» является тяжелым для клиента, потому что по мере того, как объем информации, хранящейся в cookie, растет, больше информации передается по проводу для каждого запроса и ответа. Преимущество, которое он выполняет, заключается в том, что нет необходимости хранить информацию о сеансе на стороне сервера. Если это не является ограничением для хранения информации о сеансе на сервере, идеальным способом должно быть сохранение информации, связанной с регистрацией и аутентификацией, в хранилище сеансов «не-cookie» на стороне сервера и просто передача маркера клиенту. Сервер будет поддерживать карту данных токена и сеанса. «FilesystemStore» позволяет это сделать.

Хотя «FilesystemStore» и «CookieStore» реализуют интерфейс «Store», каждая из их реализаций функции «Сохранить()» несколько отличается. Исходный код для обеих функций CookieStore.Save() и FilesystemStore.Save() поможет нам понять, почему «CookieStore» не может сохранять информацию о сеансе. Метод Save() FileystemStore, помимо записи информации сеанса в заголовок ответа, также сохраняет информацию в файле сеанса на стороне сервера. В реализации «CookieStore», если браузер не может отправить новый модифицированный файл cookie из ответа на следующий запрос, запрос может выйти из строя. В реализации «FileystemStore» токен, предоставляемый браузеру, остается неизменным. Информация о сеансе обновляется в файле и извлекается на основе запрашивающего токена, когда это требуется.

Смежные вопросы