DecoratorをつかってApp EngineでBasic認証.
DecoratorをつかってGoogle App EngineでBasic認証をする仕掛けを作ってみた.Google App Engineにはデフォルトでgoogleのアカウントを使っての認証が入っていて,人間を相手のサービスを作るには簡単でいいのだけど,クライアント側も作ろうと思うとちょっと面倒なので.
使い方
webapp.RequestHandlerのgetメソッドなどを修飾することを前提としている.こんな感じ.第一引数がユーザ名とパスワードのdict, 第二引数がrealmである.
userDict = {"userA": "passA", "userB": "passB"} class MainPage(webapp.RequestHandler): @basicAuth(userDict, "simplestore") def get(self): logging.info('authenticated as ' + self.request.basic_user) ...
認証が成功した場合には,self.request.basic_userにユーザ名をセットする.
実装
実装にはhttp://d.hatena.ne.jp/sugyan/20090311/1236724687を参考にさせていただいた.ありがとうございます.
フローとしては,すごく単純化して書くとこんな感じ.
- 'Authorization'ヘッダが無ければ,401を返してbasic認証を要求
- 'Authorization'ヘッダがあれば,中身を解析してユーザ辞書とつき合わせる.
- 認証に成功すれば本体の関数を実行
- 失敗したら401を返して再度認証を要求
import base64 import logging def basicAuth(userDict, realm): def _deco(_func): def _f(_self, *args, **kw): def _send_401(_self, message): _self.response.set_status(401) _self.response.headers['WWW-Authenticate'] = \ 'Basic realm="%s"' % (realm) _self.response.out.write('<body><h1>%s</h1></body>\n' % message) auth_header = _self.request.headers.get('Authorization') msg = 'Basic authentication required' if auth_header: try: (scheme, base64Str) = auth_header.split(' ') if scheme == 'Basic': (username, password) = base64.b64decode(base64Str).split(':') if userDict[username] == password: _self.request.basic_user = username _func(_self, *args, **kw) return else: logging.info("failed login attempt :" + username) else: logging.error("got invalid scheme from client " + schme) msg = "Only the basic authentication is accepted." except KeyError, err: logging.info("failed login attempt :" + username) except (ValueError, TypeError), err: logging.info("failed to authenticate: "+ err.__str__()) _send_401(_self, msg) return _f return _deco