panic_error!

Aplicación Web en Go (II)

Continuo con el tema abierto hace unos días sobre la programación de aplicaciones web en Go. Hoy muestro un trozo de código que ilustra como he manejado las sesiones de usuario de mi aplicación usando el paquete de sesiones de Gorilla. El uso de estas no puede ser más sencillo. En primer lugar creamos una variable de tipo sessions.NewCookieStore para el almacenamiento de las sesiones en memoria. Tal y como se indica en documentación de Gorilla de las dos claves solo la primera es la obligatoria y aunque yo las he creado a mano es recomendable el uso de la función securecookie.GenerateRandomKey() para generarlas.

// Authorization Key
var authKey = []byte{
    0x70, 0x23, 0xbd, 0xcb, 0x3a, 0x15, 0x72, 0x48,
    0x46, 0x12, 0x16, 0xfc, 0x81, 0xF1, 0x38, 0xeb,
    0xfc, 0x81, 0xfb, 0xbb, 0x9b, 0xcf, 0x8e, 0x3e,
    0xa9, 0xf5, 0x43, 0x16, 0x54, 0x3d, 0xa1, 0x22,
}

// Encryption Key
var encKey = []byte{
    0x31, 0x93, 0x3E, 0x1B, 0x00, 0x67, 0x62, 0x86,
    0xB1, 0x7B, 0x61, 0x01, 0xCA, 0xA8, 0x76, 0x44,
    0x0A, 0xEF, 0x65, 0x11, 0x21, 0x1B, 0x5A, 0x57,
    0x21, 0x72, 0xA1, 0x62, 0x5B, 0x8C, 0xE9, 0xA1,
}

var store = sessions.NewCookieStore(authKey, encKey)

var sessionName = "appname"

También he implementado una serie de funciones simples para la gestión de las sesiones: crear nueva, comprobar la validez y cerrar una sesión. Las sesiones poseen un mapa de valores llamado Values indexado por nombres y una estructura llamada Options donde podemos definir características internas de la sesión como su tiempo de expiración, el ámbito de validez, etc.

func createSession(w http.ResponseWriter, r *http.Request) 
          (*sessions.Session, bool) {

        session, err := store.Get(r, sessionName)
        if err!=nil {
                log.Println("session error: ", err.Error())
                return nil, false
        }
        session.Values["isAuthorized"] = true
        session.Options = &sessions.Options{
        Path: "/",      
        MaxAge:60*5}
        
        if err = session.Save(r, w); err != nil {
                log.Println("session error: ", err.Error())
                return nil,false
        }
        return session,true
}


func validateSession(w http.ResponseWriter, r *http.Request) 
            (*sessions.Session, bool) {

        if session, err := store.Get(r, sessionName); err == nil {
                if v, ok := session.Values["isAuthorized"]; 
                      ok && v == true {
                        return session,true
                } else {
                        return nil,false
                }
        }

        return nil,false
}


func closeSession(w http.ResponseWriter, r *http.Request) 
           (*sessions.Session, bool) {
        if session, err := store.Get(r, sessionName); err == nil {

                session.Values["isAuthorized"]=false
                session.Options = &sessions.Options{
                MaxAge: -1, 
                Path: "/"}
                
                if err := session.Save(r, w); err != nil {
                        log.Println("saving error: ", err.Error())
                        return nil,false
                }

                http.SetCookie(w, &http.Cookie{
                Name: sessionName,
                MaxAge: -1,
                Path: "/"})

                return session,true
        }else{
                log.Print(err)
        }
        return nil,false
}

La idea es usar estas funciones en nuestros controladores. Las funciones createSession y closeSession se pueden usar al implementar el controlador que gestione los comandos de login y logout de nuestra aplicación. Una vez implementado la funcionalidad de login, podemos incluir la validación de la sesión en cada controlador tal y como se muestra en este ejemplo:

func FooController(w http.ResponseWriter, r *http.Request) {

        f:=strings.Split(r.URL.Path,"/")
        cmd:=f[2]
        switch cmd{
        case "bar":
                barHandler(w,r)
        default:
                ErrorHandler(w,r,ErrResourceNotFound)
        }
}


func barHandler(w http.ResponseWriter, r *http.Request) {

        if _,ok := validateSession(w, r); ok {
                fmt.Fprint(w,"Your session is ok, yeah!")
        }else{
                http.Redirect(w, r, "/", http.StatusForbidden) 
        }
}

Solo quedaría mapear la función principal de este controlador llamada FooController tal y como se mostraba en el artículo anterior y comprobar su funcionamiento realizando una petición a http://localhost:6060/foo/bar. Como siempre con Go, fácil y claro. Los próximos artículos los dedicaré a la conexión con la base de datos y la gestión de vistas usando la librería de plantillas de Go.


Julio 2013