AJAX
Passord
Cookie
Eksempler >Logg inn

Innlogging

Hva
Innlogging med passord

Dette eksempelet beskriver rutiner for å registrere brukere med passord og eventuell tilleggsinformasjon. Det er mange ulike strategier for å gjøre dette og du kjenner sikkert de fleste av dem som bruker av forskjellige tjenester på nettet.

Hvilken strategi som velges er avhengig av mange forhold. Dette ekesempelet har ikke til hensikt å hindre noen i å komme inn, men det skal hindre alle i å opptre som andre enn seg selv når de har kommet inn. Det vi si at den typiske situasjonen er at (vilkårlig) mange brukere har sine egne data som de skal kunne finne igjen og bearbeide. Eller sagt på en annen måte: De har sin egen profil. Alle som ønsker kan registrere seg og komme inn.

Vi må også ta stilling til hva slags nivå vi ønsker å legge sikkerheten på. Her har vi valgt et moderat sikkerhetsnivå.

  • Vi har ingen mekanisme for å hindre noen brukere å registrere seg. Vi kan lett modifisere dette ved å legge til ekstra kontroll på registreringsdataene.
  • Vi bruker en Ajax-løsning og det vil derfor ikke være avslørende informasjon som skjulte felt e.l. som avslører brukerens identitet i kildekoden.
  • Vi bruker en cookie som settes av et tjenerskriptet og som fjernes av Javascriptkoden. Cookien er satt opp til å vare til nettleseren avslutter. Denn cookien hindrer at brukeren detter ut dersom han ser på en annen side i mellomtiden eller foretar en oppfriskning (reload) av siden.
  • Informasjon om brukerne ligger i en database.

Det er ofte et problem å ordne logikken i slike innloggingsprosedyrer. Det oppstår en rekke situasjoner som fører til dialogsekvenser som det kan være vanskelig å ha klart i hodet mens en programmerer. Figuren nedenfor illustrerer mulige sekvenser i denne innloggingsprosedyren. Figuren tjener som dokumentasjon og som støtte under programmeringen.

register

De enkelte fragmentene som skal vises er merket med F for Fragment. Alle aksjonene som respons på brukervalg er merket med J, for Job. for å holde orden på dette er alle fragmentene samlet i en fil, der hvert fragment er merket slik at det kan plukkes ut av et skript. Hver job tilsvarer en funksjon med samme navn i en Javascriptfil.

Fila som inneholder alle fragmentene er slik: registerfragments.html. Fordelen med å ha denne som en html-fil er at den kan redigeres og inspiseres og delvis testes i en nettleser. Når skriptet kannibaliserer denne og henter det fragmentet det trenger så får vi det vi forventer.

De jobbene som skal gjøres er samlet på en javascriptfil:

// ajax stuff
var registerskript='register.py'
// relies on prototype.js
function getIt(params,targetNodeId)
{
  if(params.length > 0)
    params='?'+params;
  new Ajax.Request(registerskript,
        {method:'post',
         parameters:params,
         onSuccess:function(transport){
            var T=transport.responseText;
            $(targetNodeId).update(T);
         },
         onFailure:function(){$(targetNodeId).update("Could not access content")}
         });
}
//------------------------------------------------------
// jobs according to documented statediagram
// fetching the initial login
// has password, no password, forgotten password as options
function job0() // job0
{
  var userId=readCookie('lastUser');
  if (userId==null)
    params='job=job0';
  else
    params='job=job5&userId='+userId;
  getIt(params,'entry');
}
// Sending normal log in with known password, job1
// non-blank userName and userPwd
function job1(form)
{
  userName=form.userName.value;
  userPwd=form.userPwd.value;
  ok=true;
  document.getElementById('emptynameF1').innerHTML='';
  document.getElementById('emptypwdF1').innerHTML='';
  if(userName.length ==0){
    ok=false
    document.getElementById('emptynameF1').innerHTML='fyll ut !';
  }
  if(userPwd.length ==0){
    ok=false
    document.getElementById('emptypwdF1').innerHTML='fyll ut !';
  }
  if(!ok)
    return;
  params='job=job1&userName='+escape(userName)
       +'&userPwd='+escape(userPwd);
  getIt(params,'entry');
}
// User ha forgotten his password. From F1
// userName may be non-blank
function job2(form)
{
  userName=form.userName.value;
  if(userName.length ==0)
    userName='no_value';
  params='job=job2&userName='+escape(userName);
  getIt(params,'entry');
}
// New user. userName may be non-blank
function job3(form)
{
  userName=form.userName.value;
  if(userName.length ==0)
    userName='no_value';
  getIt('','entry');
}
// user has filled inn a password form, and want to
// be reckognized
function job4(form)
{
  params='job=job4';
  userName=form.userName.value;
  document.getElementById('emptynameF3').innerHTML='';
  if(userName.length ==0)
  {
    document.getElementById('emptynameF3').innerHTML='fyll ut !';
    return
  }
  params+='&userName='+escape(userName);
  params+='&userBirth='+escape(form.userBirth.value);
  params+='&userPostnr='+escape(form.userPostnr.value);
  params+='&userLucky='+escape(form.userLucky.value);
  getIt(params,'entry');
}

// A simple confirmation that user is to be let in,from F5
function job5(form)
{
  userId=form.userId.value;
  params='job=job5&userId='+userId;
  getIt(params,'entry');
}

// user has filled inn a password form, and want to register
function job6(form)
{
  userName=form.userName.value;
  if(userName.length ==0){
    document.getElementById('emptynamex').innerHTML='fyll ut !';
    return;
  }
  userPwd1=form.userPwd41.value;
  userPwd2=form.userPwd42.value;
  if (userPwd1.length==0){
    document.getElementById('emptyPwd41').innerHTML='fyll ut !';
    return;
  }
  if (userPwd2.length==0){
    document.getElementById('emptyPwd42').innerHTML='fyll ut !';
    return;
  }
  if (userPwd1 != userPwd2){
    document.getElementById('emptyPwd42').innerHTML='de er ikke like';
    return;
  }
  params='job=job6';
  params+='&userName='+escape(userName);
  params+='&userPwd='+escape(userPwd1);
  params+='&userBirth='+escape(form.userBirth.value);
  params+='&userPostnr='+escape(form.userPostnr.value);
  params+='&userLucky='+escape(form.userLucky.value);
  getIt(params,'entry');
}
// User cancels entryattempt
function job7()
{
  params='job=job7';
  getIt(params,'entry');
}
// User is not found
function job8()
{
  params='job=job8';
  getIt(params,'entry');
}
// try registering new user again
function job9()
{
  params='job=job9';
  getIt(params,'entry');
}
// logout
function job10()
{
  // need path and domain to be able to delete ?
  // set in the pythonskript: register.py, prepareCookie
  eraseCookie('lastUser','/','it.hiof.no');
  window.location.reload();
}

Vi bruker en cookie for at brukeren skal slippe å registrere seg hver gang han går inn på siden, f.eks ved en reload. Cookien settes i serverskriptet, se nedenfor, og den fjernes i Javascriptkoden ved utlogging eller når nettleseren avslutter. Setting og fjerning av cookies kan gjøres slik:

//************* cookiestuff *******************
// There are numerous sources for cookie-kode on the net
// For instance:
// http://www.netspade.com/articles/2005/11/16/javascript-cookies/
function createCookie(name,value,minutes) {
    if (minutes) {
        var date = new Date();
        date.setTime(date.getTime()+(minutes*60*1000));
        var expires = "; expires="+date.toGMTString();
    }
    else
        var expires = "";
    document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
          if (c.indexOf(nameEQ) == 0)
            return unescape(c.substring(nameEQ.length,c.length));
    }
    return null;
}
function eraseCookie(name,path,domain) {
    if(readCookie(name)){
        document.cookie = name + "=" +
            ((path) ? "; path=" + path : "") +
            ((domain) ? "; domain=" + domain : "") +
            "; expires=Thu, 01-Jan-70 00:00:01 GMT";
    }
}

Denne javascriptfila gjør henvendelser til et pythonskript som leverer de fragmentene vi ønsker. Navnet på jobben som skal gjøres viderføres til Pythonskriptet slik at vi kan holde styr på programmeringen i henhold til planen. Pythonskriptet er slik:

#! /usr/bin/python2.5
import cgi,smtplib,datastorage,Cookie
import cgitb; cgitb.enable()
# -*- coding: cp1252 -*-
"""
Handles all phases in a pwd registration
"""
# fragments are extracted from this file:
FRAGMENT_FILENAME='registerfragments.html'
#-----------------------------------------
# error messages
GET_FILE_ERROR='kan ikke lese fil: %s'
GET_FRAGMENT_ERROR='finner ikke fragment: %s'
#-----------------------------------------
# extract  fragments from file
# fragmentnames we want is a list of fragments
def extractFragment(filename,fragmentnames):
    T=''
    try:
        f=open(filename,'r')
        T=f.read()
        f.close()
    except:
        return GET_FILE_ERROR%filename
    Tcopy=T
    result=''
    for fragmentname in fragmentnames:
        T=Tcopy
        leftpar='<!-- '+fragmentname+ ' -->'
        rightpar='<!-- /'+fragmentname+ ' -->'
        pos1=T.find(leftpar)
        if pos1 == -1:
            return GET_FRAGMENT_ERROR%fragmentname
        T=T[pos1+len(leftpar):]
        pos2=T.find(rightpar)
        if pos2 == -1:
            return GET_FRAGMENT_ERROR%fragmentname
        result=result+T[:pos2]
    return result

#----------------------------------------
# Find password for a user with id userId
# return None if not available
def getPassword(userId):
    return datastorage.getUserPassword(userId)
#-----------------------------------------
# Find userName given a userId
# return None if not found
def getUserName(userId):
    return datastorage.getUserName(userId)

#----------------------------------------
# register user
# Complete registration from form
# expect to find:userName,userEmail,userBirth,userPostnr,userLucky
# the latter three may be empty or non-existant
def registerUser(dataList):
    # userName,userPwd,userBirth,userPostnr,userLucky
    if dataList[0]=='no_value':
            return None
    if dataList[1]=='no_value':
            return None
    datastorage.registerUser(dataList)
    userId=datastorage.getUserID(dataList[0],dataList[1])
    return userId

#----------------------------------------
# user has filled out a help-me-I-have-forgotten-passwd form
# form contains submitted data
# return True if we find a (good enough) match on submitted controldata
# False otherwise
# expect to find:userName,userEmail,userBirth,userPostnr,userLucky
# the latter three may be empty or non-existant
def controlDataSet(form):
    # unpack and see if it matches
    userName='no_value'
    userPwd='no_value'
    userBirth='no_value'
    userPostnr='no_value'
    userLucky='no_value'
    userPwd='no_value' # not supplied and stays no_value
    if form.has_key('userName'):
            userName=form['userName'].value
    if form.has_key('userBirth'):
            userBirth=form['userBirth'].value
    if form.has_key('userPostnr'):
            userPostnr=form['userPostnr'].value
    if form.has_key('userLucky'):
            userLucky=form['userLucky'].value
    # expect None if no match
    userId=datastorage.findMatch(\
           [userName,userPwd,userBirth,userPostnr,userLucky])
    return userId
#----------------------------------------
# user has submitted userName and userPwd
# is it ok to let him in ?
# return userId if so is the case
# return None otherwise
def getUserId(userName,userPwd):
    # unpack and see if user is registered with the given passwd
    result=datastorage.getUserID(userName,userPwd)
    if result == None:
        return None
    return result
#----------------------------------------
# prepare a cookie:lastUser
def prepareCookie(userName,userId):
    c=Cookie.SimpleCookie()
    c['lastUser']=userId
    # in seconds or default is end of browser session
    #c['lastUser']['max-age']=60*120
    c['lastUser']['path']='/'
    c['lastUser']['domain']='.ia.hiof.no'
    cookieText=c.output()
    return cookieText
#-------------------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=iso-8859-1\n'
result= '\n'
job='emergency_exit'
if form.has_key('job'):
    job=form['job'].value

if job=='job0':
    # set up the intial form for name and password entry
    # this form also includes single lines for
    # forgotten password and want passwrd (new user)
    result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
    print result

elif job=='job1':
    # normal login attempt with known passwd
    # we expect to find: userName and userPwd
    # userPwd is unpacked in getUserId
    # Let him in or return to initial situation (job0)
    userName='no_value'
    if form.has_key('userName'):
        userName=form['userName'].value
    userPwd='no_value'
    if form.has_key('userPwd'):
        userPwd=form['userPwd'].value
    userId=getUserId(userName,userPwd)
    if userId != None:
        result+=extractFragment(FRAGMENT_FILENAME,['F6'])%\
          (userId,userName)
        print prepareCookie(userName,userId)
        print result
    else:
        result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F2'])%\
          userName+'</div>'
        print result
elif job=='job2':
    # give user a chance to fill in controldata
    # expect userName, may be no_value
    userName='no_value'
    if form.has_key('userName'):
        userName=form['userName'].value
    if userName=='no_value':
        userName=''
    result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F3'])%\
      userName+'</div>'
    print result

elif job=='job3':
    # start a New user form
    # expect userName, may be no_value
    userName='no_value'
    if form.has_key('userName'):
        userName=form['userName'].value
    if userName=='no_value':
        userName=''
    result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F4'])%\
      (userName)+'</div>'
    print result

elif job=='job4':
    # control data filled in by a forgetfull user
    # expect to find:userName,userEmail,userBirth,userPostnr,userLucky
    # the latter three may be empty or non-existant
    userName='no_value'
    if form.has_key('userName'):
        userName=form['userName'].value
    userId=controlDataSet(form)
    if userId != None:
        userPwd=getPassword(userId)
        result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F5'])%\
          (userId,userName,userName,userPwd)+'</div>'
    else:
        result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F2'])%\
          userName+'</div>'
    print result

elif job=='job5':
    #transport us in
    # expect to find userId
    userId='no_value'
    if form.has_key('userId'):
        userId=form['userId'].value
    userName=getUserName(userId)
    if userName==None:
        result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
    else:
        result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F6'])%\
          (userId,userName)+'</div>'
        print prepareCookie(userName,userId)
        print result

elif job=='job6':
    # receive data from password request form
    # expect to find:userName,userPwd,userBirth,userPostnr,userLucky
    # the latter three may be empty or non-existant
    # register everything
    userName='no_value'
    userPwd='no_value'
    userBirth='no_value'
    userPostnr='no_value'
    userLucky='no_value'
    if form.has_key('userName'):
        userName=form['userName'].value
    if form.has_key('userPwd'):
        userPwd=form['userPwd'].value
    if form.has_key('userBirth'):
        userBirth=form['userBirth'].value
    if form.has_key('userPostnr'):
        userPostnr=form['userPostnr'].value
    if form.has_key('userLucky'):
        userLucky=form['userLucky'].value
    userId= registerUser([userName,userPwd,userBirth,userPostnr,userLucky])
    if userId != None:
        result+= '<div>'+extractFragment(FRAGMENT_FILENAME,['F6'])%\
          (userId,userName)+'</div>'
        print prepareCookie(userName,userId)
    else:
        result+='<div>'+ extractFragment(FRAGMENT_FILENAME,['F7'])+'</div>'
    print result
elif job=='job7':
    # transport to initial form
    result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
    print result
elif job=='job8':
    # transport to initial form
    result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
    print result

elif job=='job9':
    # transport to registration form
    result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F4'])%''+'</div>'
    print result

else: # unknown job
    print '\n<div> not implemented: %s</div>'%job

Selve datalagringen er skilt ut i en egen, utbyttbar, modul som ser slik ut:

#! /usr/bin/python2.5
import cgi,MySQLdb,random
import cgitb; cgitb.enable()
# -*- coding: cp1252 -*-
"""
Handles data access for a std registration.
Must handle these tasks:
- Initialize the database table with a dummy user
       initDatabase()
- Find password for a user, given the ID:
       getUserPassword(userId)
- Find name  for a user, given id:
       getUserName(userId)
- Find id  for a user, given name and password:
       getUserID(userName,userPwd)
- Find id of a user, given all other data:
       findMatch(test)
- Register a user, give all data(except id):
       registerUser(dl)
Using database with table with structure as shown below
"""
#----------------------------------------------
# SQL - expressions
SQL_USE_DB="""use diverse;"""
SQL_DROP_TABLE="""drop table if exists regusers;"""
SQL_INIT_TABLE="""
create table regusers(
        userId INT PRIMARY KEY AUTO_INCREMENT,
        userName VARCHAR(35),
        userPwd VARCHAR(10),
        userBirth VARCHAR(10),
        userPostnr VARCHAR(10),
        userLucky VARCHAR(10)
        );
"""
SQL_USER_WITH_NAME="""select * from regusers where userName='%s';"""
SQL_USER_WITH_ID="""select userName from regusers where userId='%s';"""
SQL_USER_PWD="""select userPwd from regusers where userId='%s';"""
SQL_USER_ID="""select userId from regusers 
                where userName='%s' and userPwd='%s';"""
SQL_INSERT_USER_DATA="""
  insert into regusers(userName,userPwd,userBirth,userPostnr,userLucky)
    values('%s','%s','%s','%s','%s'); """
#----------------------------------------------
# connect and execute a sql-request
def connectAndExecute(sql):
    try:
        myBase=MySQLdb.connect(host='frigg.hiof.no',
                               user='borres',
                               passwd='hemmelig',
                               db='diverse')
        myTab=myBase.cursor()
        sql=sql.decode('latin1')
        myTab.execute(sql)
        myBase.commit()
        myBase.close()
        return myTab.fetchall()
    except MySQLdb.Error, e:
        print "Error %d: %s" % (e.args[0], e.args[1])
        return None
#----------------------------------------
# init database with a dummy user
def initDatabase():
    connectAndExecute(SQL_USE_DB)
    connectAndExecute(SQL_DROP_TABLE)
    connectAndExecute(SQL_INIT_TABLE)
    connectAndExecute(SQL_INSERT_USER_DATA%('ole','gallas','46','1784','7'))
#-----------------------------------------
# get registered password for a user
def getUserPassword(userId):
    result=connectAndExecute(SQL_USER_PWD%userId)
    if len(result)==0:
        return None
    return str(result[0][0])
#----------------------------------------
# Get unique ID for a user, based on name and pwd
def getUserID(userName,userPwd):
    result=connectAndExecute(SQL_USER_ID%(userName,userPwd))
    if len(result) != 1:
        return None
    else:
        return str(result[0][0])
#----------------------------------------
# Get userName , based on userId
def getUserName(userId):
    result=connectAndExecute(SQL_USER_WITH_ID%(userId))
    if len(result) != 1:
        return None
    else:
        return str(result[0][0])
#--------------------------------------------
# get a users dataset
def getUserData(userName):
    result=connectAndExecute(SQL_USER_WITH_NAME%userName)
    if len(result) == 0:
        return []
    return result
#------------------------------------------
# check if this set matches someone in th database
def findMatch(test):
    # test: userName,userPwd,userBirth,userPostnr,userLucky
    usrList=getUserData(test[0])
    found=False
    for usr in usrList:
        #usr: userId,userName,userPwd,userBirth,userPostnr,userLucky
        # userPwd is index 2, pass it
        found=True
        for ix in range(2,5):
            if test[ix]!=usr[ix+1]:
                found=False
        if found:
            return usr[0] # userid
    return None
#----------------------------------------
# set users datasett
def registerUser(dl):
    if len(dl) != 5:
        return None;
    # save no_value as missing
    # makes comparing easier (mismatch and no value)
    for ix in range(0,len(dl)):
        if dl[ix] == 'no_value':
            dl[ix] = 'missing'
    connectAndExecute(SQL_INSERT_USER_DATA%(dl[0],dl[1],dl[2],dl[3],dl[4]))
Referanser

All relevant kode er sitert i teksten.

Se også modulene: Sesjoner og Autentisering

Vedlikehold

B. Stenseth, revidert okt. 2009

( Velkommen ) Eksempler >Logg inn ( Bildegalleri )