Evästeet, sessiot ja tiedostot
- Luentotaltiointi
- Evästeet
- Sessiot
- Tiedostojen lataus palvelimelle
- Tiedostojen käsittely
- Template
- Luentoesimerkit
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.
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 maksimikoko vähintään 4 kt.
- Evästeitä voidaan tallentaa yhteensä vähintään 300.
- Voidaan tallentaa vähintään 20 evästettä palvelinta tai domainia kohti.
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()
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:
- r = Tiedosto avataan vain lukemista varten.
- r+ = Avataan tiedosto lukemista ja kirjoittamista varten. Tiedostoon kirjoitus/lukeminen alkaa tiedoston alusta.
- w = Tiedosto avataan vain kirjoitusta varten. Vanha tiedosto tuhotaan tai ellei tiedostoa ole olemassa, Python luo sen.
- w+ = Avataan tiedosto lukemista ja kirjoittamista varten. Tiedosto tyhjennetään ja kirjoitus/lukeminen alkaa tiedoston alusta. Jos tiedostoa ei ole, Python luo sen.
- a = Tiedosto avataan vain kirjoitusta varten. Kirjoitus alkaa tiedoston lopusta. Jos tiedostoa ei ole, Python luo sen.
- a+ = Avataan tiedosto lukemista ja kirjoittamista varten. Kirjoitus/lukeminen alkaa tiedoston lopusta. Jos tiedostoa ei ole, Python luo sen.
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:
- LOCK_SH (tai 1) - lukitsee tiedoston lukemista varten.
- LOCK_EX (tai 2) - lukitsee tiedoston kirjoittamista varten.
- LOCK_UN (tai 3) - vapauttaa tiedoston lukituksen.
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