基本认证过程
在很多场合需要使用基本认证,如果代码仓库的认证授权等,这里先介绍一下基本认证中客户端和服务器的交互过程
- 浏览器(或者客户端)发起指定路径的访问请求
- 服务器从客户端发来的头部中寻找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
...
