Evästeet, sessiot ja tiedostot

Luennolla käsitellään evästeitä ja sessioita mod_pythonilla. Lisäksi käydään läpi tiedostojen käsittelyä (lukeminen, tallentaminen jne.) ja tiedostojen latausta palvelimelle lomakkeen avulla.

Luentotaltiointi

Ongelmia videon katselussa?

Evästeet

Yleistä evästeistä

Evästeet ovat tekstimuotoista tietoa, jota palvelin voi lähettää selaimelle. Evästeelle voidaan määrätä haluttu elinikä, jonka ajan selain säilyttää evästeeseen tallennettua tietoa. Oletuselinikä evästeillä on meneillään oleva sessio, joka tarkoittaa, että tietoa säilytetään vain siihen asti, kun selain suljetaan. Tyypillisesti selain säilyttää tällaisia session mittaisia evästeitä muistissa. Jos evästeen elinikä on määritelty pidemmäksi, tallentaa selain sen tiedostoon.

Seuraavan kerran, kun selain pyytää sivua, se lähettää evästeen palvelimelle. Palvelimella toimiva WWW-sovellus voi hyödyntää edellisellä kerralla tallennettua tietoa. Evästettä voidaan ajatella siis eräänlaisena muuttujana, johon voidaan tallentaa tekstiä. Tieto on siten aina käytettävissä seuraavilla kerroilla, kun sivustoa käytetään.

Evästeessä voidaan säilyttää esimerkiksi ostoskorin tiedot verkkokaupassa, jolloin käyttäjä voi jättää ostoksensa kesken ja jatkaa niitä sujuvasti seuraavalla kerralla. Mitään arkaluontoista tietoa evästeissä ei kuitenkaan saa säilyttää, sillä ne ovat puhdasta kenen tahansa luettavissa olevaa tekstitietoa. Myöskään mitään tärkeää säilytettävää tietoa ei kannata tallettaa evästeissä, sillä ne ovat helposti poistettavissa.

Selaimella on aina jokin raja siitä, kuinka suuria evästeet saavat olla ja kuinka paljon niitä voidaan tallentaa. Jokaisen selaimen pitäisi kuitenkin täyttää suosituksen minimirajat:

Evästeen asettaminen yleisesti

Palvelin lähettää evästeen http-vastauksen otsikkotiedoissa. Otsikkotiedoissa lähetetään selaimelle rivi, joka on seuraavaa muotoa:

Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure

NAME tarkoittaa evästeen nimeä ja VALUE arvoa (merkkijono), joka evästeelle annetaan. Nimi on ainoa pakollinen tieto, joka evästeelle täytyy joka tapauksessa määrittää. Muut tiedot voidaan haluttaessa jättää tyhjiksi.

expires-määritys määrittää ajan, mihin asti eväste on voimassa, eli mihin asti selainta pyydetään sitä säilyttämään. Aika (DATE) ilmoitetaan muodossa: "05-May-2004 07:31:27 GMT". Jos aikaa ei määritellä, on eväste oletuksena voimassa vain session ajan.

path-määritys määrittelee palvelimen polun, jossa eväste on voimassa. Oletuksena eväste on voimassa samassa kansiossa ja sen kansion alikansioissa. Tämä tarkoittaa sitä, että jokaisella kerralla, kun selain pyytää jotain sivua evästeen voimassaoloalueelta, se lähettää evästeen palvelimelle http-pyynnön mukana. Jos poluksi määritellään vain "/", on se voimassa koko domainissa.

domain-määrityksellä voidaan määritellä evästeen vaikutusalueeksi kaikki palvelimen alidomainit. Jos arvoksi määritellään esim. ".jyu.fi", on eväste voimassa kaikissa Jyväskylän yliopiston alidomaineissa.

secure-määrityksellä voidaan määrätä, että selain lähettää evästeen palvelimelle ainoastaan silloin, kun on käytössä salattu yhteys (HTTPS/SSL). Tämä ilmaistaan siis lisäämällä sana "secure" evästeen määreisiin.

Eväste asetetaan aina HTTP-otsakkeissa mikä tarkoittaa, että sovellus, joka on jo ehtinyt tulostaa XHTML-koodia ei voi enää asettaa kyseisellä sivulla evästeitä!

Evästeen asettaminen ja poistaminen Python:ssä

Python-koodissa evästeen asettaminen toteutetaan Cookie-moduulilla.

from mod_python import apache, Cookie
import time
keksi = Cookie.Cookie('nimi','arvo', expires=time.time()+300, path="/~tjlahton/", domain=".jyu.fi")
Cookie.add_cookie(req, keksi)

keksit = Cookie.get_cookies(req)

for i in keksit.keys():
        req.write ( 'name = ' + getattr(keksit[i], "name", "" ) + "\n" )
        req.write ( 'value = ' + getattr(keksit[i], "value", "" ) + "\n" )

Kolmas parametri on evästeen voimassaoloaika, joka annetaan "Unix timestamp" -muodossa. "Unix timestamp" on sekunteina ilmoitettuna aika, joka on kulunut vuoden 1970 alusta. Pythonissa on esimerkissä käytetty time.time()-funktio, joka palauttaa tämän hetkisen ajan tuossa muodossa. Esimerkissä on lisätty siihen 300 sekunttia, jolloin eväste vanhenee 300 sekunnin kuluttua. Cookie-kirjasto muuntaa ajan evästeeseen kelpaavaan muotoon

Evästeen poistaminen tapahtuu asettamalla evästeen voimassaoloaika menneisyyteen.

Sessiot

Yleistä sessioista

Sessionhallinta on tekniikka, jonka avulla on tarkoitus säilyttää turvallisesti tietoa yhden session ajan, eli siihen asti, kunnes käyttäjä lopettaa session tai sulkee selaimen. Sessioilla toteutetaan usein esimerkiksi kirjautuminen erilaisiin palveluihin. Keskeisin ero evästeisiin on se, että sessioon liittyviä tietoja ei välitetä lainkaan selaimelle, vaan se säilytetään palvelimella. Palvelin säilyttää asetuksista riippuen sessiotiedon joko tietokannassa, tiedostossa tai muistissa.

Silloin, kun sessio käynnistetään, sille luodaan yksilöllinen sessiotunniste. Sessioon voidaan sen jälkeen asettaa haluttu määrä ns. sessiomuuttujia. Sessiotietoon päästään myöhemmin käsiksi sessiotunnisteiden avulla. Tämän vuoksi sessiotunniste on jollain tavalla aina välitettävä selaimelle.

Sessiotunnisteen välittämiseen on kaksi tapaa: välittää sitä URL:n mukana tai säilyttää evästeessä. Sessiotunnisteen välittämiseen URL:n mukana sisältyy enemmän tietoturvariskejä joten lähes aina suositaankin evästeitä.

from mod_python import apache, Session

if not hasattr(req, 'session'):
        req.session = Session.Session(req)

try:
        req.session['hits'] += 1
except:
        req.session['hits'] = 1

req.session.save()

mod_pythonissa sessiota kannattaa kuljettaa mukana req-objektissa. Erityisesti on varottava session luomista useampaan kertaan koska se voi tietyissä tilanteissa aiheuttaa lukkotilanteita Apachessa. Järkevintä on tehdä session luomista varten oma käsittelijä, joka tallentaa session req-objektiin josta muut käsittelijät pääsevät sessiota muokkaamaan.

Sessiolle voidaan asettaa timeout, joka määrää kauanko sessio on voimassa.

Sessiomuuttujia käytettäessä on aina lopuksi muistettava tallentaa session komennolla session.save()

Kaaviokuva sessioiden toiminnasta

Tiedostojen lataus palvelimelle

WWW-lomake tiedoston latauksessa

WWW-lomakkeen avulla voidaan siirtää myös paikallisia tiedostoja palvelimelle. Tätä varten on lomakkeella oltava tiedostokenttä. Se määritellään XHTML-koodissa antamalla input-elementin type-attribuutille arvo "file". Tiedostoja siirrettäessä täytyy lomakkeen käyttää POST-metodia tietojen välittämiseen. Lisäksi lomake-elementin (form) enctype-attribuutille täytyy määrittää arvo "multipart/form-data". Muuten tiedoston siirto ei onnistu. Alla on esimerkki tiedoston siirtävän lomakkeen XHTML-koodista.

<form action="jokusivu.py" method="post" enctype="multipart/form-data">
  <p>
    <input type="file" name="tiedosto" />
  </p>
  <p>
    <input type="submit" value="Siirrä" />
  </p>
</form>

Ladatun tiedoston käsittely mod_pythonilla

# bufferointi
def fbuffer(f, chunk_size=10000):
   while True:
      chunk = f.read(chunk_size)
      if not chunk: break
      yield chunk


if form.has_key('file') and form['file'].filename:
   fileitem = form['file']

   tiedosto = os.path.basename(fileitem.filename)

   polku = os.path.join(os.path.dirname(req.filename), 'upload')

   f = open(os.path.join(polku, fname), 'wb', 10000)

   for chunk in fbuffer(fileitem.file):
      f.write(chunk)
   f.close()
   req.write( '<p>Tiedoston <samp>%s</samp> siirtäminen onnistui</p>' % tiedost$

Samassa yhteydessä (ennen tiedoston varsinaista siirtämistä) on yleensä tarpeen tehdä muitakin tarkistuksia tiedostolle. Jos esimerkiksi haluttiin siirtää kuvatiedosto, on hyvä tutkia onko tiedostopääte oikeanlainen (esimerkiksi .jpg, .gif tai .png).

Tiedostojen käsittely

Tiedoston avaus onnistuu open()-funktiolla.

f = open('/tmp/tiedosto', 'r')

Funktiolle täytyy lisäksi määritellä, mitä tarkoitusta varten tiedosto avataan. Mahdollisia vaihtoehtoja ovat seuraavat:

tiedosto suljetaan komennolla f.close()

Tiedostosta lukeminen

Tiedoston voi lukea joko kerralla muuttujaan, rivi kerrallaan muuttujaan tai merkki kerrallaan muuttujaan.

Tiedosto kerralla muuttujaan

foo = f.read()

Tiedosto riveittäin

for rivi in f:
  print rivi

Tiedostoon kirjoittaminen

Tiedosto täytyy ensin muistaa avata kirjoitustilaan. Lisäksi tiedostolle täytyy muistaa antaa riittävät kirjoitusoikeudet.

Jos mahdollista, tiedosto, johon kirjoitetaan tulisi olla tietoturvasyistä sellaisessa hakemistossa, johon ei pääse käsiksi suoraan WWW:n kautta.

foo = open("tiedosto", "w")
foo.write("Kirjoitetaan tämä tiedostoon")
f.close()

Tiedoston lukitseminen

fcntl.lockf-funktiolla voidaan lukita tiedosto niin, ettei kukaan/mikään muu (esimerkiksi toinen Python-skripti) voi muuttaa sitä sen ollessa auki. Tiedosto on hyvä lukita ainakin tiedostoon kirjoittamisen ajaksi. Mahdollisia lukitusvaihtoehtoja ovat seuraavat:

import fcntl, FCNTL

file = open("foo.txt", "w")

fcntl.flock(file.fileno(), FCNTL.LOCK_EX)

file.close()

Template

Nyt kun tiedostojen lukeminen osataan niin voidaan helposti käyttää myös templateja:

from string import Template

foo = """
Tässä on tekstiä jossa on sanoja: $foo ja ${bar} jotka korvataan muulla tekstillä
"""

tmpl = Template(foo)

req.write ( tmpl.substitute(foo="testi", bar="testi2") )

f = open( os.path.join(os.path.dirname(req.filename), 'tiedosto.html'))

tmpl = Template( f.read() )

req.write( tmpl.substitute(python="Tällä tekstillä korvataan"))

Luentoesimerkit

Eivät toimi palvelimen muutosten takia

Käyttäjien kommentit

Kommentoi Lisää kommentti
Informaatioteknologia - Jyväskylän yliopiston informaatioteknologian tiedekunta