基本认证过程
在很多场合需要使用基本认证,如果代码仓库的认证授权等,这里先介绍一下基本认证中客户端和服务器的交互过程
- 浏览器(或者客户端)发起指定路径的访问请求
- 服务器从客户端发来的头部中寻找Authorization项,如果没有提供,则返回401给浏览器
401 Unauthorized Server: Apache/2.2.22 (Ubuntu) WWW-Authenticate: Basic realm="Secure Area"
- 浏览器(或者客户端)在收到401返回后,会给用户弹出一个认证框,要求用户输入用户名和密码进行认证,在用户输入后,将用户名和密码使用":"隔开并使用base64进行编码,在请求头中加入Authorization项,再次发起请求
Host: code.ideais.net User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:27.0) Gecko/20100101 Firefox/27.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Authorization: Basic amlsaWxpOjEyMzQ1Ng== Connection: keep-alive Cache-Control: max-age=0
- 服务器从头中解析出Authorization项的值,使用base64解码得出用户名和密码,然后使用服务器端数据库或者文件中保存的用户名和密码进行比较,如果不一致,再次返回401给浏览器,继续要求输入用户名密码;如果已经通过验证,那么调用正常处理程序。
Base64常用工具
使用python进行base64编解码
>>> import base64 >>> base64.encodestring('jilili:123456') 'amlsaWxpOjEyMzQ1Ng==\n' >>> base64.decodestring("amlsaWxpOjEyMzQ1Ng==") 'jilili:123456
实现基本认证
很多Web服务器都提供了便利的认证实现,可以直接调用;用户也可以自己编写程序来实现基本认证。
注意:CGI程序在Apache环境中是无法实现基本认证的,应为Apache不提供支持,可以通过用WSGI来包装认证,然后调用CGI来处理。
Apache认证模块基本认证配置
Apache认证模块支持多种认证方式,如:基于文件的,基于MySQL的还有基于LDAP的。这里只以基于MySQL的认证加以说明。
<LocationMatch /git> AuthName "Git" AuthType Basic #AuthGroupFile /dev/null AuthBasicAuthoritative off Auth_MySQL on Auth_MySQL_Authoritative on Auth_MySQL_Host localhost Auth_MySQL_DB ideais_net Auth_MySQL_Password 123456 Auth_MySQL_User jilili Auth_MySQL_Password_Table "uni_login_account, uni_login_password" Auth_MySQL_Password_Clause " and uni_login_account.account_id=uni_login_password.account_id " Auth_MySQL_Username_Field account_name Auth_MySQL_Password_Field password Auth_MySQL_Empty_Passwords off Auth_MySQL_Encryption_Types Plaintext Crypt_DES Crypt_MD5 PHP_MD5 MySQL require valid-user </LocationMatch>
WSGI基本认证实现
WSGI中主要是获取HTTP_AUTHORIZATION,里面的值就是Base64编码过的用户名和密码信息,以下是一些代码片段。
def handle(environ): ''' 请求处理 ''' is_authorized = False http_authorization = environ.get("HTTP_AUTHORIZATION") print >> sys.stderr, "HTTP_AUTHORIZATION", http_authorization if http_authorization: auth_type, auth_body = http_authorization.split(" ", 1) if auth_type == "Basic": username, password = auth_body.decode("base64").split(":", 1) if check_user(username, password): is_authorized = True; if not is_authorized: status = '401 Unauthorized' headers = [('Content-type', 'text/html') , ('Server', 'Source Server/0.1') , ('WWW-Authenticate', 'Basic realm="Secure Area"')] # HTTP Headers document = document_401 else: cgi_scripts = "/opt/www/code.ideais.net/cgi/git-http.cgi" status, headers, document = wsgi_to_cgi(environ, cgi_scripts, username) return status, headers, document def check_user(username, password): ''' 检查用户合法性 ''' ... def application(environ, start_response): ''' 应用处理入口 ''' environ['wsgi.multithread'] = True environ['wsgi.multiprocess'] = True environ_strs = ['%s:%s' % (key, value) for key, value in sorted(environ.items())] environ_strs = '\n'.join(environ_strs) print >> sys.stderr, environ_strs status, headers, document = handle(environ) start_response(status, headers) return document ...