HTTP Basic Authentication

来自:http://www.ibm.com/developerworks/cn/webservices/1106_webservicessecurity/
简介

正如"HTTP Basic Authentication"这个名字,它是 Authentication( 认证 ) 中最简单的方法。长期以来,这种认证方法被广泛的使用。当你通过 HTTP 协议去访问一个使用 Basic Authentication 保护的资源时,服务器通常会在 HTTP 请求的 Response 中加入一个"401 需要身份验证"的 Header,来通知客户提供用户凭证,以使用资源。如果你正在使用 Internet Explorer 或者 Mozilla Firefox 这样的可视化浏览器来访问需要认证的资源,浏览器会弹出一个窗口,让你输入用户名和密码,如果所输入的用户名在资源使用者的验证列表,并且密码完全正确,此时,用户才可以访问受限的资源。那么什么是 HTTP Basic Authentication 呢?如何在不同的服务器上配置 Basic Authentication 呢?下面本文将展开介绍。


--------------------------------------------------------------------------------

HTTP Basic Authentication 介绍

HTTP BASIC 认证的基本流程如图 1 所示,


图 1. BASIC HTTP认证基本流程


HTTP Basic Authentication 是指客户端在使用 HTTP 协议访问受限资源时,必须使用用户名和密码在一个指定的域 (Realm) 中获取认证。在正式开始之前,我们需要明白以下名词的含义:

Authentication,即认证,验证。它是一种确认的过程,通过这样的认证过程,你可以确定某物体是不是它所声称的那种物体。这通常会涉及到用户名和密码,也可能是身份证明,或生物特征,如视网膜等。
Realm,即域。一个 Realm 就是一系列用户名和密码的“数据库”,它通常用来保存、识别某个或某些 Web 应用中有效的用户和密码。它还定义了每个有效用户所对应的角色。
本文将介绍如何使用 HTTP BASIC Authentication 来保护 Web services endpoint 服务资源,当 Web services 的 Endpoints 被设置为 BASIC HTTP 认证才能访问的受限资源时,用户必须提供用户名密码才能使用它们,基本的流程如图 2 所示。


图 2. Web services 客户端访问受限 Web services 服务流程


下面,本文将以 Tomcat 何 WebSphere Application Server 为例,分别讲述如何配置 Web services 的 endpoint 为受限资源,并使用 Web services 静态和动态客户端分别加以测试。


--------------------------------------------------------------------------------
回页首
为 Tomcat 配置 Basic Authentication

本节将详细讲述如何在 Tomcat 下使用 Basic Authentication 来增强 Axis 实现的 Web services 访问的安全性。本文使用 Eclipse 来开发相关的例子,在这里假设您已经安装了相关的环境。

创建 Web services Provider 应用

在 Eclipse 中新建一个 Dynamic Web Project,并将其命名为“TomcatAxis”如图 3 所示:


图 3. 创建动态 WEB 工程


该工程的目录结构如图 4 所示:


图 4. 工程结构


为 Web services 添加实现

创建一个基于 Axis 实现的 Web Service,本文采取“Bottom up Java bean Web Service”方式创建,其具体操作过程为:

创建该 Web services 的实现类,代码如下

清单 1. Web services Provider 实现代码
 

package sample.test;
public class ServiceImpl {
  /**
   * return the summation of the two integer arguments
   * @param addend
   * @param augend
   * @return
   */
  public int sum(int addend, int augend) {
    return addend + augend;
  }
}



基于该实现类,创建 Web Service,如图 5 所示

图 5. 创建 Web Service



完成上述步骤后,会在 /TomcatAxis/WebContent/wsdl/ 目录下创建一个名称为“ServiceImpl.wsdl”的 WSDL 文件。

将 Web Application 部署到 Tomcat 上,使用 Eclipse 自带的 Web services 测试工具“Web services Explorer”来测试所创建的 Web services 是否能被使用,如图 6 所示

图 6. 测试所创建的 Web services


在确认所创建的 Web services 能够正常使用后,需要为 Web Application 配置 Basic Authentication。

为 Web Application 配置 Basic Authentication:

打开 Tomcat 安装目录下的“conf”文件夹,修改文件“tomcat-users.xml”,该文件是用来存储 Tomcat 预加载的用户和角色定义的,此文件即是上文提到的 Realm。在“<tomcat-users>”中加入如下用户和角色:

清单 2. 定义用户及其角色
 

  <!-- Web services invoker role -->
  <role rolename="WsInvokerRole"/>
 
  <!-- Web services invokers/users -->
  <user username="wsaxis" password="wsaxis" roles="WsInvokerRole"/>



打开 Web 应用“TomcatAxis”的部署描述符:web.xml 文件,并在“<web-app>”中加入如下片段:

清单 3. 配置安全资源
 

  <!-- configurations for BASIC Authentication -->
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>All Web services Endpoints</web-resource-name>
      <url-pattern>/services/*</url-pattern>
    </web-resource-collection>
   
    <auth-constraint>
      <description>Web services invokers are allowed doing invocation</description>
      <role-name>WsInvokerRole</role-name>
    </auth-constraint>
  </security-constraint>
 
  <!-- authentication method -->
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Realm of Web services Invokers</realm-name>
  </login-config>



在“<security-constraint>”片段内,定义了改 Web Application 需要保护的资源,可以通过“<url-pattern>”来定义符合一定 URL 样式的资源,上述片段的定义,保护了所有 Web services 的 endpoints。

“<login-config>”片段定义了采取 BASIC 认证方式,其中“<realm-name>”只在 BASIC 认证方式下有效,它分配安全领域名,此名称供浏览器用于对话框标题,且为 Authorization 头部的一部分。

重启 Tomcat,骤重新测试该 Web Service,将有如图 7 提示框,提示输入用户名和密码:输入之前所定义的 wsaxis 用户名及其密码后,点击 OK,Web services 将会被调用。否则将无法正常调用到 Web Service。

图 7. 信息输入对话框




--------------------------------------------------------------------------------
回页首
测试 Tomcat 下 Basic Authentication

通常 Web services 可以有两种调用方式,即静态调用方式和动态调用方式。下面将分别介绍如何使用这两种方式验证 Basic Authentication 是否正常工作。

静态调用方式测试

首先,新建一个 Java Project,命名为“TomcatAxisClient”, 如图 8


图 8. 创建 Web services 测试客户端工程


其次,将前文所声称的 Web services 的 WSDL 的 URL 作为参数,生成 Web services 客户端,如图 9


图 9. 生成 Web services Client


完成上述操作后,将有如下结构的 Web services 客户端类生成,如图 10 所示:


图 10. 生成结果


使用上述客户端类,就可以实现 Web services 的调用。现在,需要创建一个测试类,实例化客户端类,调用 Web Service。类名为“sample.test.client.runable.StaticClientTest”,详细代码见清单 4.


清单 4. 静态调用类
 

package sample.test.client.runable;

import java.net.URL;
import sample.test.ServiceImplServiceLocator;
import sample.test.ServiceImplSoapBindingStub;

public class StaticClientTest {
  public static void main(String[] args) {
    try {
    // String userName = "wsaxis", password = "wsaxis";
      int addend = 64, augend = 128;
      ServiceImplSoapBindingStub sisbs = new ServiceImplSoapBindingStub(
          new URL(  "http://localhost:8080/TomcatAxis/services/ServiceImpl"),
          new ServiceImplServiceLocator());
    // sisbs.setUsername(userName);
    // sisbs.setPassword(password);
      System.out
          .println("Static Client Invocation:\n\tThe summation is: "
              + sisbs.sum(addend, augend));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}



运行上述代码,你将发现 Web services 不能够被顺利调用,此处将会抛出 Web services 调用异常,如图 11 所示:


图 11. 未认证时执行结果


这个错误表明,你所调用的资源是受保护的,你必须提供相关的认证信息来访问它们。如果把如下代码的注释符去掉,


// String userName = "wsaxis", password = "wsaxis";
… …
// sisbs.setUsername(userName);
// sisbs.setPassword(password);



再重新编译,运行,你就能得到正确的结果,如图 12


图 12. 认证后执行结果


由于 Web services 的 endpoint 被列为受限资源,需要使用这些资源,必须提供相应的认证信息。清单 4 中被注释掉的内容,正是用来向服务器认证时使用的,由于服务器无法获得认证信息,就返回了 401 Unauthorized 错误。去掉注释后的请求得到了服务器的认证,因而能够得到正确的结果。

动态调用方式测试

动态调用方式不需要生成 Web services 客户端,通过查找 Web services 提供的服务,就能调用 Web Service,此处不对动态调用方式详加解释, 清单 5 是使用动态调用的方式测试 Basic Authentication 的。


清单 5. 动态调用测试代码
 

package sample.test.client.runable;
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class DynamicClientTest {
  public static void main(String[] args) {
    try {
      String address = "http://localhost:8080/TomcatAxis/services/ServiceImpl";
      String namespaceURI = "http://test.sample";
      String serviceName = "ServiceImplService";
      String portName = "ServiceImpl";
      String operationName = "sum";
      String userName = "wsaxis", password = "wsaxis";
      int addend = 64, augend = 128;
      ServiceFactory factory = ServiceFactory.newInstance();
      Service service = factory.createService(new QName(serviceName));
      Call call = service.createCall(new QName(portName));
      call.setTargetEndpointAddress(address);
      QName intQName = new QName("http://www.w3.org/2001/XMLSchema","int");
      call.setOperationName(new QName(namespaceURI, operationName));
      call.addParameter("addend", intQName, ParameterMode.IN);
      call.addParameter("augend", intQName, ParameterMode.IN);
      call.setReturnType(intQName);
      call.setProperty(Call.USERNAME_PROPERTY, userName);
      call.setProperty(Call.PASSWORD_PROPERTY, password);
      Object[] inParams = new Object[2];
      inParams[0] = new Integer(addend);
      inParams[1] = new Integer(augend);
      int value = ((Integer) call.invoke(inParams)).intValue();
      System.out.println("Dynamic Client Invocation:\n\tThe summation is: " + value);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}



需要指出的是,与静态调用方式不同,动态调用方式设置认证的用户名和密码是通过 Property(属性)来设置的(代码中加粗的部分)。上述代码的运行结果如图 13 所示:


图 13. 动态调用测试结果


如果去掉清单 4 中加粗的部分,同样服务器将返回 401 Unauthorized 错误。

下面,本文将结合 Rational Application Developer Standard Edition Version 8.0 和 WebSphere Application Server Version 7.0 来介绍如何在 WAS 上配置 HTTP Basic Authentication,并使用 JAX-WS 实现的 Web services 加以测试。


--------------------------------------------------------------------------------
回页首
WAS 下配置 HTTP Basic Authentication

使用 RAD 开发 Web services Provider 应用

首先,如同第 3 节中所述,在 Rational Application Developer(以下简称 RAD)中创建一个 Dynamic Project,此处需要注意,由于 RAD 中所使用的 WAS 只能部署 Enterprise Application Archive(EAR),因此,需要创建相应的 EAR 工程,并把所创建的 Dynamic Web Project 座位该 EAR 的模块。如图 14 所示:


图 14. 创建动态 WEB 工程


等待工程创建完毕之后,将有如图 15 所示的目录结构:图中高亮的两个工程是上述步骤所创建的。


图 15. 工程结构


接下来,我们仍然采用“Bottom up Java bean Web Service”方式创建用于测试的 Web Service:

创建 Web services 的实现类,并将如下代码复制并覆盖所创建的文件,如清单 6。

清单 6. Web services Provider 实现代码
 

package sample.test.jaxws;

public class JaxWsServiceImpl {
  /**
   * return the summation of the two integer arguments
   *
   * @param addend
   * @param augend
   * @return
   */
  public int sum(int addend, int augend) {
    return addend + augend;
  }
}



使用该文件,创建 Web Service,在创建过程中,需要指定所采用的 Web services 运行时为“IBM WebSphere JAX-WS”,如图 16 所示

图 16. 创建 Web services Provider



Web services 创建完毕后,将会被自动发布到 WAS 上,使用 Web services Explorer 加以简单测试,以便检查 Web services 是否能够被访问调用等。
配置 Web services Provider 应用的受限访问资源

此处,我们需要仿照上文为 Web services Provider 应用配置安全性,将 Web services 的 endpoint 作为受限资源,定义在应用的部署描述符(web.xml)中。

打开 web.xml 文件,切换到 Design 页面,如图 17 所示

图 17. web.xml 编辑页面



点击“Add …”按钮,在弹出的窗口中选择“Security Role”,确认后,对“Role Name”和“Description”赋值,其中“Role Name”是必填项,如图 18 所示

图 18. 添加安全角色



仿照步骤 2,添加“Security Constraint”项,展开“Authorization Constant”,点击“Add”按钮,添加一个 Role,名称为之前所创建的 Role:“wsRole”,继续配置 Security Constraint,将 Web services 的 endpoint 加入到受限资源中。配置完成后,如图 19 和图 20 所示:

图 19. 添加受限资源




图 20. 添加受限资源 2



仿照步骤 2,添加“Login Configuration”,在“Authentication Method”中输入“BASIC”,如图 21 所示

图 21. 配置认证方式



至此,Web services Provider 的受限资源以及应用的 Basic Authentication 都配置完毕。如果需要 Basic Authentication 起作用,还必须启用 WAS 的安全设置,下面我们将简要讲述如何启动 WAS 安全。

配置 WAS 安全

打开到 WAS 的控制台,如图 22 所示

图 22. WAS 控制台



登录后,选择“Security – Global Security”菜单,并点击“Security Configuration Wizard”按钮,如图 23 所示

图 23. 配置 WAS 安全 1



在 Wizard 的步骤 2 中,选择“Federated repositories”作为资源库。点击“Next”,输入用户名密码,并确认密码,如图 24 所示。其中,用户名和密码将作为登录控制台的用户名和密码,此处,所输入的用户名和密码都是“jaxws”。完成向导后,保存所做的修改,并重新启动 WAS。

图 24. 配置 WAS 安全 2



重新登录控制台,此时,将提示你输入用户名和密码,如图 25 所示:

图 25. 配置 WAS 安全 3



部署 Web services Provider 应用

在启用了安全的 WAS 上部署步骤创建的 Web services Provider 应用。

首先,在 RAD 中,将所创建的工程导出为 EAR 包;

其次,部署所导出的 EAR 到 WAS 中,具体步骤如下:

登录 WAS 控制台,并打开“Applications - Application Types - WebSphere enterprise applications”;
点击“Install”按钮,在“Preparing for the application installation”页面,点击“Browse”按钮,浏览到已经导出的 EAR 文件,并单击“Next”,如图 26 所示

图 26. 部署 Web services Provider 到 WAS 1



在新页面中选择“Detailed”选项,如图 27,单击“Next”

图 27. 部署 Web services Provider 到 WAS 2



单击链接“Step 8”,在 RAD 中所创建的 Role 和 WAS 中用户或组之间建立关联,如图 28 所示:

图 28. 建立角色 ( 用户 ) 关联



完成向导,并启动该 Web services Provider,如图 29 所示:

图 29. 启动 Web services Provider 应用



至此,JAX-WS Web services Provider 的 Basic Authentication 已经配置完毕,下一节,将将输入和采用静态和动态调用方式测试它。


--------------------------------------------------------------------------------
回页首
测试 WAS 下 Basic Authentication

静态调用方式测试

如同测试 Tomcat 上的 Basic Authentication 类似,需要在 RAD 中创建一个 Java Project,利用之前所创建的 Web services Provider 提供的 WSDL 文件,创建 Web services Client,需要指出的是,Web services 的运行时需要指定为“IBM WebSphere JAX-WS”。生成客户端之后的 Java Project 的目录结构如图 30 所示


图 30. 创建测试工程


创建一个测试 Class,命名为“sample.test.jaxws.client.runable.StaticClientTest”,其内容如清单 7 所示:


清单 7. 静态测试代码
 

package sample.test.jaxws.client.runable;
import javax.xml.ws.BindingProvider;
import sample.test.jaxws.JaxWsServiceImplPortProxy;

public class StaticClientTest {
  public static void main(String[] args) {
    String userName = "jaxws", password = "jaxws";
    JaxWsServiceImplPortProxy proxy = new JaxWsServiceImplPortProxy();
    BindingProvider bp = (BindingProvider) proxy._getDescriptor()
        .getProxy();
    bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, userName);
    bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,
        password);
    System.out.println(proxy.sum(123, 312));
  }
}



运行该测试 Class,将得到如图 31 所示的结果:


图 31. 测试结果


如果将“userName”或“password”赋予了其他的值,运行测试 Class,将得到 401 Unauthorized 错误,如图 32 所示:


图 32. 未加认证的测试结果


动态调用方式测试

使用动态调用方式测试该 Web Service,需要基于 JAX-WS 的 API 开发客户端代码,本文给出测试需要 BASIC Authentication 认证的 Web services 的样例代码,如清单 8 所示:


清单 8. 动态测试代码
 

package sample.test.jaxws.client.runable;

import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

public class DynamicClientTest {
  public static void main(String[] args) throws Exception {
    String userName = "jaxws", password = "jaxws";
    String endpointUrl = "http://localhost:9080/WasJaxWs/JaxWsServiceImplService";
    QName serviceName = new QName("http://jaxws.test.sample/",
        "JaxWsServiceImplService");
    QName portName = new QName("http://jaxws.test.sample/",
        "JaxWsServiceImplPort");

    Service service = Service.create(serviceName);
    service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);
    Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
        SOAPMessage.class, Service.Mode.MESSAGE);

    MessageFactory mf = MessageFactory
        .newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
    SOAPMessage request = mf.createMessage();
    SOAPPart part = request.getSOAPPart();
    SOAPEnvelope env = part.getEnvelope();
    SOAPBody body = env.getBody();
    SOAPElement operation = body.addChildElement("sum", "tns",
        "http://jaxws.test.sample/");

    // parameters and values
    SOAPElement addend = operation.addChildElement("arg0");
    addend.addTextNode("512");
    SOAPElement augend = operation.addChildElement("arg1");
    augend.addTextNode("512");
    request.saveChanges();

    // TODO add BASIC Authentication data
  BindingProvider bp = (BindingProvider) dispatch;
  bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, userName);
  bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);

    // invoke the Web services
    SOAPMessage response = dispatch.invoke(request);

    // find the result element
    QName qname = new QName("http://jaxws.test.sample/", "sumResponse");
    Iterator<?> iter = response.getSOAPBody().getChildElements(qname);
    SOAPElement sbe = (SOAPElement) iter.next();
    SOAPElement retEle = (SOAPElement) sbe.getChildElements(
        new QName("return")).next();

    System.out.println("Dynamic Client Invocation:\n\tThe summation is: "
        + retEle.getValue());
  }
}



以下是该样例代码的执行结果,如图 33 所示


图 33. 动态测试结果


通常,如果我们可以使用 Eclipse 或者 RAD 自带的 Web services Explorer 等测试工具来对 Web services 做简单的测试,或者,我们通过客户端来测试所开发的 Web services Provider。这样的测试通常只能发现简单而明显的问题,如何对 Web services Provider 做系统的测试呢? soapUI 这个工具能帮我们更加方便,更加系统地测试 Web services Provider。下面,我们将概述如何使用 soapUI 来测试 Web services 的 BASIC Authentication。


--------------------------------------------------------------------------------
回页首
使用 SOAPUI 测试配置了 Basic Authentication 的 Web Service

soapUI 的是世界领先的 SOA 和 Web services 的测试工具。凭借其易于使用的图形界面和企业级功能,soapUI 能让你轻松,快速的创建和执行自动化测试,回归和负载测试。

首先,需要从 soapUI 的 官方网站 下载最新的 soapUI 安装文件,安装 soapUI。

其次,创建一个 soapUI 测试工程,提供被测试 Web services 的 WSDL 以便创建测试用例。如图 34 所示


图 34. soapUI 新建工程


如果选择了“Create Test Suite”,soapUI 将根据 Web services 的 operation 创建测试用例,同时,你还可以创建相关的性能测试用例,如图 35 所示


图 35. 生成测试集合


图 36 给出了所创建的测试工程,打开测试用例,可以看出,屏幕被分割成 3 个区域,在区域 A 中,显示了所创建的功能性测试用例和用于测试 Web services 性能的 Load Test 用例。区域 B 是 Web services 请求的配置区域,此处可以设置 Web services 使用的参数,认证等相关的信息。B 区中,红方格中的 Username 和 Password 正式设置 BASIC Authentication 使用的用户名和密码的。区域 C 是 Web services 请求返回(Response)显示区域。


图 36. soapUI 布局


然后,在区域 B 中,给 Web services 所使用参数赋值,并点击 B 区上方的“ ”按钮,如图 37 所示。


图 37. 运行测试


此时,由于没有设置相关的认证信息,将会得到图 38 所示的相关错误信息。


图 38. 未认证时 soapUI 测试结果


在 B 区域红方格所示区域中输入认证信息,再重新执行测试用例,将得到如图 39 所示的结果。


图 39. soapUI 添加认证信息


以上就是如何使用 soapUI 来测试 Web services BASIC Authentication 的步骤,从中我们可以发现,只需要简单的操作便可以实现 Web services 的测试,因此,soapUI 对于提供 Web services 的测试效率是有帮助的。


--------------------------------------------------------------------------------
回页首
总结

通过以上实例,本文验证了 BASIC Authentication 能够在传输层保护 Web services 资源安全,实现安全调用,但是如何在 Web services 消息层安全传输以及数据加密传输,请参考本文相关的后续文章。

Web services 安全实践,第 2 部分:基于 WS-Security 和 Policy Set 为 Web services 配置消息层安全机制

Web services 安全实践,第 3 部分:创建自定义的 Policy Set 和 Bindings 为 Web services 配置消息层安全机制

Web services 安全实践,第 4 部分:基于 Web services security API 为 Web services 提供消息层安全机制

猜你喜欢

转载自liao492006.iteye.com/blog/1849110
今日推荐