Http 基本认证模式

一.  BASIC认证概述

HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。客户端在接收到HTTP服务器的身份认证要求后,会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密,加密后的密文将附加于请求信息中,如当用户名为anjuta,密码为:123456时,客户端将用户名和密码用合并,并将合并后的字符串用BASE64加密为密文,并于每次请求数据时,将密文附加于请求头(Request Header)中。HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,如果用户名及密码正确,则根据客户端请求,返回客户端所需要的数据;否则,返回错误代码或重新要求客户端提供用户名及密码。

 

二.  BASIC认证的过程

1. 客户端向服务器请求数据,请求的内容可能是一个网页或者是一个其它的MIME类型,此时,假设客户端尚未被验证,则客户端提供如下请求至服务器:

Get /index.html HTTP/1.0
Host:www.google.com

2. 服务器向客户端发送验证请求代码401,服务器返回的数据大抵如下:

HTTP/1.0 401 Unauthorised
Server: SokEvo/1.0
WWW-Authenticate: Basic realm="google.com"
Content-Type: text/html
Content-Length: xxx

3. 当符合http1.01.1规范的客户端(如IEFIREFOX)收到401返回值时,将自动弹出一个登录窗口,要求用户输入用户名和密码。

4. 用户输入用户名和密码后,将用户名及密码以BASE64加密方式加密,并将密文放入前一条请求信息中,则客户端发送的第一条请求信息则变成如下内容:

Get /index.html HTTP/1.0
Host:www.google.com
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxx

注:xxxx....表示加密后的用户名及密码。

5. 服务器收到上述请求信息后,将Authorization字段后的用户信息取出、解密,将解密后的用户名及密码与用户数据库进行比较验证,如用户名及密码正确,服务器则根据请求,将所请求资源发送给客户端:

 

三.        BASIC认证的缺点

HTTP基本认证的目标是提供简单的用户验证功能,其认证过程简单明了,适合于对安全性要求不高的系统或设备中,如大家所用路由器的配置页面的认证,几乎都采取了这种方式。其缺点是没有灵活可靠的认证策略,如无法提供域(domainrealm)认证功能,另外,BASE64的加密强度非常低,可以说仅能防止sohu的搜索把它搜到了。当然,HTTP基本认证系统也可以与SSL或者Kerberos结合,实现安全性能较高(相对)的认证系统

 

四.        BASIC认证的JAVA实现代码。

服务器端

		HttpSession session=request.getSession();

		String user=(String)session.getAttribute("user");

		String pass;

		if(user==null){

		    try{

		       response.setCharacterEncoding("GBK");

		       PrintWriter ut=response.getWriter();

		       String authorization=request.getHeader("authorization");

		       if(authorization==null||authorization.equals("")){

		           response.setStatus(401);

		           response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");

		           out.print("对不起你没有权限!!");

		           return;

		       }

		       String userAndPass=newString(newBASE64Decoder().decodeBuffer(authorization.split(" ")[1]));

		       if(userAndPass.split(":").length<2){

		           response.setStatus(401);

		           response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");

		           out.print("对不起你没有权限!!");

		           return;

		       }

		       user=userAndPass.split(":")[0];

		       pass=userAndPass.split(":")[1];

		       if(user.equals("111")&&pass.equals("111")){

		           session.setAttribute("user",user);

		           RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");

		           dispatcher.forward(request,response);

		       }else{

		           response.setStatus(401);

		           response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");

		           out.print("对不起你没有权限!!");

		           return;

		       }

		    }catch(Exception ex){

		       ex.printStackTrace();

		    }

		}else{

		    RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");

		    dispatcher.forward(request,response);

		}

客户端实现

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
 
/**
 * A simple example that uses HttpClient to perform a GET using Basic
 * Authentication. Can be run standalone without parameters.
 *
 * You need to have JSSE on your classpath for JDK prior to 1.4
 *
 * @author Michael Becke
 */
public class BasicAuthenticationExample {
 
    /**
     * Constructor for BasicAuthenticatonExample.
     */
    public BasicAuthenticationExample() {
        super();
    }
 
    public static void main(String[] args) throws Exception {
        HttpClient client = new HttpClient();
 
        // pass our credentials to HttpClient, they will only be used for
        // authenticating to servers with realm "realm" on the host
        // "www.verisign.com", to authenticate against
        // an arbitrary realm or host change the appropriate argument to null.
        client.getState().setCredentials(
            new AuthScope("www.verisign.com", 443, "realm"),
            new UsernamePasswordCredentials("username", "password")
        );
 
        // create a GET method that reads a file over HTTPS, we're assuming
        // that this file requires basic authentication using the realm above.
        GetMethod get = new GetMethod("https://www.verisign.com/products/index.html");
 
        // Tell the GET method to automatically handle authentication. The
        // method will use any appropriate credentials to handle basic
        // authentication requests.  Setting this value to false will cause
        // any request for authentication to return with a status of 401.
        // It will then be up to the client to handle the authentication.
        get.setDoAuthentication( true );
 
        try {         
            // execute the GET
            int status = client.executeMethod( get );
 
            // print the status and response
            System.out.println(status + "\n" + get.getResponseBodyAsString());
 
        } finally {
            // release any connection resources used by the method
            get.releaseConnection();
        }
    }
}





  Apache是目前流行的Web服务器,可运行在Linux、Unix、Windows等操作系统下,它可以很好地解决“用户名+密码”的认证问题。
  Apache用户认证所需要的用户名和密码有两种不同的存贮方式:一种是文本文件;另一种是MSQL、Oracle、MySQL等数据库。


下面以Linux的Apache为例,就这两种存贮方式,分别介绍如何实现用户认证功能。

一,采用文本文件存储

  这种认证方式的基本思想是:Apache启动认证功能后,就可以在需要限制访问的目录下(其对应的子目录也会起作用)建立一个名为.htaccess 的文件,指定认证的配置命令。当用户第一次访问该目录的文件时,浏览器会显示一个对话框,要求输入用户名和密码,进行用户身份的确认。若是合法用户,则显示所访问的页面内容,此后访问该目录的每个页面,浏览器自动送出用户名和密码,不用再输入了,直到关闭浏览器为止。
    
      以下是实现的具体步骤:

      假设Apache已经编译、安装到了/usr/local/apache目录中。缺省情况下,编译Apache时自动加入mod_auth模块,
    利用此模块可以实现“用户名+密码”以文本文件为存储方式的认证功能。
    (通过下面的命令,可以查看apache是否已经安装指定的模块,/usr/local/apache/bin/apachectl -l )

  1.修改Apache的配置文件/usr/local/apache/conf/httpd.conf,对认证资源所在的目录设定配置命令。
      下例是对/data/home/tenfyguo/proj/soso/htdocs目录的配置,对对应的虚拟机配置增加下面的配置:

  <Directory /data/home/tenfyguo/proj/soso/htdocs>
     Options Indexes FollowSymLinks
     allowoverride authconfig #关键是增加这句
     order allow,deny
     allow from all
  </Directory>

  其中,allowoverride authconfig一行表示允许对/data/home/tenfyguo/proj/soso/htdocs目录下的文件进行用户认证。

  2.在限制访问的目录/data/home/tenfyguo/proj/soso/htdocs下建立一个文件.htaccess (其实是一个隐藏文件),其内容如下:

      AuthName "这里的文字会显示在浏览器弹出的提示登录窗口中"
      AuthType basic
      AuthUserFile /data/home/tenfyguo/proj/soso/valid.txt #这里放用户名和密码文件,不能放在可以下载的地方
      require valid-user

  说明:文件.htaccess中常用的配置命令有以下几个:

  1) AuthName命令:指定认证区域名称。区域名称是在提示要求认证的对话框中显示给用户的。
  2) AuthType命令:指定认证类型。在HTTP1.0中,只有一种认证类型:basic。在HTTP1.1中有几种认证类型,如:MD5。
  3) AuthUserFile命令:指定一个包含用户名和密码的文本文件,每行一对。
  4) AuthGroupFile命令:指定包含用户组清单和这些组的成员清单的文本文件。组的成员之间用空格分开,如:
     managers:user1 user2
  5) require命令:指定哪些用户或组才能被授权访问。如:
     require user user1 user2(只有用户user1和user2可以访问)
     requires groups managers (只有组managers中成员可以访问)
     require valid-user (在AuthUserFile指定的文件中任何用户都可以访问)

  3.利用Apache附带的程序htpasswd,生成包含用户名和密码的文本文件:/data/home/tenfyguo/proj/soso/valid.txt,
      每行内容格式为“用户名:密码”。

    #cd /usr/local/apache/bin
    #htpasswd -bc /data/home/tenfyguo/proj/soso/valid.txt user1 1234
    #htpasswd -b /data/home/tenfyguo/proj/soso/valid.txt user2 5678

    文本文件/data/home/tenfyguo/proj/soso/valid.txt含有两个用户:user1,口令为1234;user2,口令为5678。

打开该文本文件可以发现对应的密码已经加密存储
      注意,不要将此文本文件存放在Web文档的目录树中,以免被用户下载。

  欲了解htpasswd程序的帮助,请执行htpasswd -h。

       完成后,重启apache使得配置生效.

      /usr/local/apache/bin/apachectl restart


 




当用户第一次打开浏览器请求:http://test.soso.com:13601/test/,此时浏览器会弹出提示框,让用户输入用户名和密码。转包查看apache的响应:
      (1)apache的响应返回:

 

HTTP/1.1 401 Authorization Required
 Date: Thu, 27 Jan 2011 11:10:07 GMT
 Server: Apache/2.2.4 (Unix)
 WWW-Authenticate: Basic realm="test"
 Content-Length: 401
 Keep-Alive: timeout=5, max=100
 Connection: Keep-Alive
 Content-Type: text/html; charset=iso-8859-1

 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
 <html><head>
 <title>401 Authorization Required</title>
 </head><body>
 <h1>Authorization Required</h1>
 <p>This server could not verify that you
 are authorized to access the document
 requested.  Either you supplied the wrong
 credentials (e.g., bad password), or your
 browser doesn't understand how to supply
 the credentials required.</p>
 </body></html>

 

       可以看出,apache的返回的状态码是401, 对应的提示信息:Anthorization Required,并且返回一个很重要的响应头:WWW-Authenticate: Basic realm="test"
      

       (2)浏览器弹出提示框让用户输入用户名和密码,输入后浏览器发送如下:

 GET /test/ HTTP/1.1
 Host: test.soso.com:13601
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 Accept-Language: zh-cn,zh;q=0.5
 Accept-Encoding: gzip,deflate
 Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
 Keep-Alive: 115
 Connection: keep-alive
 If-Modified-Since: Fri, 12 Jun 2009 03:43:20 GMT
 Authorization: Basic dGVuZnlndW86dGVuZnlndW8=


 

 可以看到,此时浏览器发送请求中带上了请求头:
        Authorization: Basic dGVuZnlndW86dGVuZnlndW8=
 apache正是利用这个头信息进去权限验证的。


    当用户数量比较少时,这种方法对用户的认证是方便、省事的,维护工作也简单。但是在用户数量有数万人,甚至数十万人时,
      会在查找用户上花掉一定时间,从而降低服务器的效率。这种情形,应采用数据库方式。

二,采用数据库存储

   目前,Apache、PHP、MySQL三者是Linux下构建Web网站的最佳搭档,这三个软件都是免费软件。将三者结合起来,通过HTTP协议, 利用PHP和MySQL,实现Apache的用户认证功能。
   只有在PHP以Apache的模块方式来运行的时候才能进行用户认证。为此,在编译Apache时需要加入PHP模块一起编译。假设PHP作为Apache的模块,编译、安装Apache到/usr/local/apache目录,编译、 安装MySQL到/usr/local/mysql目录。然后进行下面的步骤:

  1.在MySQL中建立一个数据库member,在其中建立一个表users,用来存放合法用户的用户名和密码。

  1)用vi命令在/tmp目录建立一个SQL脚本文件auth.sql,内容为:
  drop datebase if exists member;
  create database member;
  use member;
  create table users (
     username char(20) not null,
     password char(20) not null,
  );

  insert into users values("user1",password("1234"));
  insert into users values("user2",password("5678"));

 

  2)启动MySQL客户程序mysql,执行上述SQL脚本文件auth.sql的命令,在表users中增加两个用户的记录。

  #mysql -u root -pmypwd < /tmp/auth.sql

 

  2.编写一个PHP脚本头文件auth.inc,程序内容为:
<?php
 /*
 “PHP_AUTH_USER”
 当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的用户名。
 “PHP_AUTH_PW”
 当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的密码。
 “AUTH_TYPE”
 当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是认证的类型。
 */

 

 function authenticate() {
  header("HTTP/1.0 401 Authorization Required");
  header("WWW-Authenticate: Basic realm='test'");
  echo "请输入认证的用户名和密码";
  exit;
 }

 

 function check_user() {
    if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
       //这里可以改成访问DB获取用户名和密码即可
       if($_SERVER['PHP_AUTH_USER']=='tenfyguo' && $_SERVER['PHP_AUTH_PW']=='tenfyguo'){
       return 0;
       }

    return -1;
    }

    return -1;

 }

 

 if(check_user() < 0 ){
    authenticate();
 }

 echo "验证正常";
 exit;
 ?>

 

  函数authenticate()的作用是利用函数header("WWW-Authenticate: Basic realm='test'"),向浏览器发送一个认证请求消息,
    使浏览器弹出一个用户名/密码的对话框。当用户输入用户名和密码后,包含此PHP脚本的URL将自动地被再次调用,
    将用户名、密码、认证类型分别存放到PHP的三个特殊变量:“PHP_AUTH_USER”、“PHP_AUTH_PW”、“AUTH_TYPE”,在PHP程序中可根据这三个变量值来判断是否合法用户。
    header()函数中,basic表示基本认证类型,realm的值表示认证区域名称。

 

  函数header("HTTP/1.0 401 Authorization Required")使浏览器用户在连续多次输入错误的用户名或密码时接收到HTTP 401错误。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值