Virittelin Djangon käyttämään tavallista HTTP-autentikointia kustomoidusta käyttäjätaulusta, mikä osoittautui hieman haastavaksi. Django käyttää normaalisti cookie-pohjaista autentikointia ja omaa määrämuotoista auth_user-taulua. Sen takia päätin ohittaa sisäänrakennetun autentikoinnin kokonaan ja tehdä oman.

Ensimmäinen vaatimus on saada Apache välittämään Authorization-headeri Djangolle sellaisenaan. Itse käytän Apachen mod_fastcgi:tä kehityksessä, ja tämä edellytti seuraavanlaista direktiiviä Apachen konfiguraatiossa (oleellista on -pass-header Authorization):

FastCGIExternalServer /var/www/html/django.fcgi -socket /tmp/django.sock -pass-header Authorization

Tämän jälkeen Django-sovelluksen views.py-moduuliin voi lisätä muutaman tarpeellisen funktion (User-luokka on itse määrittelemäni malli käyttäjätaululle):

def parse_http_auth(request):
        "Parse HTTP Basic Authorization and return (username, password) or ('', '')."
        for header in ['HTTP_AUTHORIZATION', 'Authorization']:
                if header in request.META:
                        auth = request.META[header].split()
                        if (len(auth) == 2) and (auth[0].lower() == "basic"):
                                uname, passwd = base64.b64decode(auth[1]).split(':')
                                return uname, passwd
        return ('', '')

def authenticate_user(request): "Authenticate user with HTTP Basic Authentication. Return User object or None." (uname, passwd) = parse_http_auth(request) if (len(uname) > 0) and (len(passwd) > 0): user = User.objects.get(login=uname) if passwd == user.password: return user return None

Viimeinen tarvittava apuväline on @require_auth-dekoraattori, jonka avulla on helppo määritellä ne näkymät, joilta vaaditaan autentikointia. Dekoraattori lisää autentikoidun käyttäjän keyword-argumenttina näkymäfunktiolle, jolloin def index(request) tuleekin muotoon def index(request, user):

class require_auth:
        "Custom authentication decorator, will inject User object as kw arg 'user' to caller or return error."
        def __init__(self, func):
                self.func = func
        def __call__(__self, *__args, **__kw):
                request = __args[0]
                user = authenticate_user(request)
                if user == None:
                        response =  HttpResponse('<html><body><h1>401 Unauthorized</h1><p>Authorization is required.</p></body></html>')
                        response.status_code = 401
                        response['WWW-Authenticate'] = 'Basic realm="Django"'
                        return response
                __kw['user'] = user
                return __self.func(*__args, **__kw)

Näiden jälkeen voi sitten alkaa lisäillä dekoraattoria tavallisiin näkymäfunktioihin:

@require_auth
def index(request, user):
        return render_to_response('index.html')

Näyttäisi toimivan. En tiedä onko tämä ratkaisu yksinkertaisin mahdollinen...

Published 5.5.2008