这几天在做一个功能,其实很简单。就是调用几个外部的API,返回数据后进行组装然后成为新的接口。其中一个API是一个很奇葩的API,虽然是基于HTTP的,但既没有基于SOAP规范,也不是Restful风格的接口。还好使用它也没有复杂的场景。只是构造出URL,发送一个HTTP的get请求,然后给我返回一个XML结构的数据。

我使用了Spring MVC中的RestTemplate作为客户端,然后引入了Jackson-dataformat-xml作为xml映射为对象的工具库。由于集成外部API的事情已经做了很多次了,集成这个API也是轻车熟路,三下五除二就完成了。

接下来为了验证连通性,我先在SoapUI里配置了该外部API的某个测试环境,尝试发送了一个Get请求,成功收到了Response。然后我把自己的程序运行起来,尝试通过自己的程序调用该API,结果返回了HTTP 500错误,即“internal server error”。

这可奇了怪了。我第一反应是程序中对外部API的配置和SoapUI中的配置不一样。我仔细对比了发送请求的URL,需要的HTTP header以及用作验证的username和password都是完全一致的。这个问题被排除。

接下来我想再仔细看看Response,能否找到什么蛛丝马迹。仔细查看了Response的header和body,发现header一切正常,body是个空的body,没有提供任何的可用信息。

然后我能想到的另一个解决方案就是联系该外部API的团队,让他们帮忙看看我发送了请求之后,为什么服务器会返回500。但可惜这是一个很老的服务了,找到该团队的人并且排期帮我看log至少要花好几天的时间了。而且既然SoapUI能调用成功,而应用程序却调用不成功,问题多半还是出在我们这。

接下来我想既然问题有可能出在我们这,那么肯定是request有差异。由于我发的是一个Get请求,没有body实体,URL又完全一样,那么问题很可能出在request的header上。这个API需要request中包含两个自定义的header,而我在SoapUI以及自己的程序中都已经配置了。那问题会在哪里哪?

既然在SoapUI里无法重现这个问题,我就使用了Chrome插件版的POSTMAN,通过它配置了该API的调用。然后奇迹出现了,我竟然在POSTMAN中重现了这个问题。当我看到在POSTMAN也返回了500 error后,我思考了5秒钟,猜到了原因。问题很可能是出在了Authentication这个header上面。

要说这个问题,还要从HTTP的Basic Authentication说起。Basic Authentication是HTTP实现访问控制的最简单的一种技术。HTTP Client端会将用户名和密码组合后使用Base64加密,生成key为‘Authentication’,value为‘Basic BASE64CODE’的HTTP header,发送给服务器端以便进行Basic认证方式。

但这个经典的Basic Authentication是要经历两步的。第一步,客户端发送不带Authentication header的HTTP请求,服务器检查后发现受访的资源需要认证,就会返回HTTP Status 401,表示未授权,客户端发现服务器端返回401后,会再构造一个新的请求,这次包含了Authentication header,服务器接收后验证通过,返回资源。

那么我在自己的应用程序和POSTMAN中调用返回500 internal server error的原因是当第一次给Server发送不带Authentication header的HTTP请求时,Server竟然返回了HTTP Status 500。其实它应该返回401,这样HTTP Client会再发一个包含了Authentication的新请求。由于它返回了500,HTTP Client认为服务器有问题,就停止处理了。

那为什么在SoapUI中调用可以成功那?那是因为SoapUI使用的Http client在发第一次请求时就已经设置了Authentication header,所以就没有问题。这样可以避免重复发请求的现象。这种行为叫做‘preemptive authentication’(抢先验证),在SoapUI中你可以选择是否启用该行为。具体可以参见How To Authenticate SOAP Requests in SoapUI

所以问题的根源在于该外部API在实现Basic Authentication时没有完全遵循规范,这锅我们不背

解决方案有两种。第一种是让该外部API遵循Basic Authentication的规范,如果请求未授权应该返回401而不是500。不过我说过这是一个很古老的API了,让它们改要等到猴年马月了。

第二种就是我的应用程序在给该外部API发送请求时,第一次就设置Authentication header。我们用的是RestTemplate,而RestTemplate底层使用的是Apache Http Client 4.0+版本。要注入这个header很简单,在实例化RestTemplate后,给其多加一个Intecepter。

1
2
restTemplate.getInterceptors().add(
new BasicAuthorizationInterceptor("username", "password"));

加上这一行代码后,运行程序,顺利的得到了Response,世界清静了。

最后一个问题,为什么Http Client当配置了用户名和密码后,不主动的启用‘preemptive authentication’那?毕竟可以少发很多请求啊。这是Apache官方给出的原因:

HttpClient does not support preemptive authentication out of the box, because if misused or used incorrectly the preemptive authentication can lead to significant security issues, such as sending user credentials in clear text to an unauthorized third party. Therefore, users are expected to evaluate potential benefits of preemptive authentication versus security risks in the context of their specific application environment.
Nonetheless one can configure HttpClient to authenticate preemptively by prepopulating the authentication data cache.


扩展阅读:

一个HTTP Basic Authentication引发的异常的更多相关文章

  1. 一个不当使用fclose引发的异常

    最近服务器上一个后台传输文件的服务,经常会报出异常来,只能强行终止并重启. 昨天刚好有空,现场抓了一下dump,再把程序扔到IDA里看了一下,很快就找出原因了,原来是调用fclose时出错的. 使用C ...

  2. Web services 安全 - HTTP Basic Authentication

    根据 RFC2617 的规定,HTTP 有两种标准的认证方式,即,BASIC 和 DIGEST.HTTP Basic Authentication 是指客户端必须使用用户名和密码在一个指定的域 (Re ...

  3. 网络负载均衡环境下wsHttpBinding+Message Security+Windows Authentication的常见异常

    提高Windows Communication Foundation (WCF) 应用程序负载能力的方法之一就是通过把它们部署到负载均衡的服务器场中. 其中可以使用标准的负载均衡技术, Windows ...

  4. Atitit HTTP 认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结

    Atitit HTTP认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结 1.1. 最广泛使用的是基本验证 ( ...

  5. Nancy 学习-身份认证(Basic Authentication) 继续跨平台

    开源 示例代码:https://github.com/linezero/NancyDemo 前面讲解Nancy的进阶部分,现在来学习Nancy 的身份认证. 本篇主要讲解Basic Authentic ...

  6. WPF控件ComboBox 每个Item的ToolTip引发的异常

    介绍 首先介绍下要实现的任务.做一个下拉框,当选择每个项的时候将鼠标发在上面显示该项的ToolTip的内容(Image). 实现 Model: public class SkinInfo : Noti ...

  7. HTTP Basic Authentication认证的各种语言 后台用的

    访问需要HTTP Basic Authentication认证的资源的各种语言的实现 无聊想调用下嘀咕的api的时候,发现需要HTTP Basic Authentication,就看了下. 什么是HT ...

  8. 关于Cocos的内存管理机制引发一些异常的解决方案

    错误:引发了异常: 读取访问权限冲突. this 是 0xDDDDDDDD.或者hero是 0xDDDDDDDD.hero是在GameController里创建的对象 这个的意思是this所指向的内存 ...

  9. HTTP Basic Authentication认证

    http://smalltalllong.iteye.com/blog/912046 ******************************************** 什么是HTTP Basi ...

随机推荐

  1. Error:C:\Users\issuser\AndroidStudioProjects\SQLiteDemo1\.gradle\buildOutputCleanup\cache.properties (系统找不到指定的文件。)

    android studio报下图中的这个错误的解决办法: 解决办法: 1.删除掉下图中标记的2个文件夹 2.将下图标记的文件的文件名重命名,把最后的后缀.lock去掉,因为加上了这个后缀,所以提示找 ...

  2. feed 流数据请求时机的两个思路

    最近 SF 首页 进行了大改版,效果如下: 其他地方都没什么难点,中间的 feed 流思考了不少时间,效果需要类似微博或者知乎 feed 流.之前一直没有做过类似的功能,现总结两个方案. 方案一 方案 ...

  3. --------驱动开发之 ObReferenceObjectByName() 故障排查--------

    ------------------------------------------------------ 在写 filter driver 或 rootkit 时,经常需要 attach 到设备栈 ...

  4. hihoCoder #1082 : 然而沼跃鱼早就看穿了一切(字符串处理)

    #1082 : 然而沼跃鱼早就看穿了一切 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 fjxmlhx每天都在被沼跃鱼刷屏,因此他急切的找到了你希望你写一个程序屏蔽所有句 ...

  5. 数值积分之Simpson公式与梯形公式

    Simpson(辛普森)公式和梯形公式是求数值积分中很重要的两个公式,可以帮助我们使用计算机求解数值积分,而在使用过程中也有多种方式,比如复合公式和变步长公式.这里分别给出其简单实现(C++版): 1 ...

  6. Linux学习之XShell与虚拟机的连接

    最近在慕课网上学习Linux视频,记录一下解决问题的方法和过程. 实验软件环境: 虚拟机软件Vmware Workstation10.0.虚拟机系统CentOS 6.3(32位).XShell 5.0 ...

  7. Unity LayerMask

    Unity用int32的每一位表示32个层,int32用二进制有32位,Layers通常被摄像机用来渲染部分场景,和灯光照射部分场景使用.但是它们也可以用来做射线检测时忽略一些collder或Coll ...

  8. java如何获取一个对象的大小

    When---什么时候需要知道对象的内存大小 在内存足够用的情况下我们是不需要考虑java中一个对象所占内存大小的.但当一个系统的内存有限,或者某块程序代码允许使用的内存大小有限制,又或者设计一个缓存 ...

  9. 使用notepad++作为keil的外部编辑器

    之前一直不喜欢keil的编辑界面,但是又不想太浮夸.看到很多群里有人用vscode写stm32的序,但是直接用vscode编写的花,各种设置很麻烦.而且还不能调试.于是想到有没有一个轻便简约的外部编辑 ...

  10. thinkphp3.2.2邮箱发送

    浏览:7510 最后更新:2017-03-18 14:21 分类:类库 关键字: PHPMailer 第一步:准备PHPMailer 将下载的PHPMailer放到ThinkPHP/library/V ...