关于HTTPS在API应用中的实践与优势深入解析
一、引言
随着互联网的迅速发展,API(应用程序接口)已成为不同软件之间数据交互的关键桥梁。
在这样的背景下,如何确保API的安全与可靠性成为了重中之重。
HTTPS作为互联网安全的重要保障手段,其在API应用中的实践与优势愈发凸显。
本文将详细探讨HTTPS在API应用中的实践过程及其显著优势。
二、HTTPS概述
HTTPS是一种通过计算机网络进行安全通信的传输协议。
它在HTTP的基础上,使用了SSL/TLS加密技术,确保数据传输过程中的保密性和完整性。
HTTPS协议的主要目标是确保互联网用户与网站之间的安全通信,防止数据在传输过程中被窃取或篡改。
三、HTTPS在API应用中的实践
1. 数据加密传输:HTTPS协议利用SSL/TLS加密技术,确保API在传输过程中的数据安全性。无论是请求参数、响应数据还是身份验证信息,都可以通过加密方式进行传输,大大降低数据被窃取或篡改的风险。
2. 身份验证与授权:HTTPS通过服务端证书,实现客户端对服务端的身份验证。这确保了API请求者能够确认所访问的服务端的身份,避免因伪造服务器导致的安全风险。同时,通过访问控制列表(ACL)等机制,实现API的授权管理,确保只有具备权限的用户才能访问特定资源。
3. 完整性校验:HTTPS协议能够对传输数据进行完整性校验,确保数据在传输过程中没有被篡改。这大大提高了API数据的安全性,防止恶意攻击者对数据进行篡改。
四、HTTPS在API应用中的优势
1. 提高安全性:HTTPS在API应用中的主要优势在于其强大的安全保障能力。通过加密传输、身份验证与授权以及完整性校验等手段,确保API数据在传输和访问过程中的安全。
2. 提升用户体验:HTTPS协议能够确保API请求的快速响应和稳定传输,避免因网络问题导致的请求失败或延迟。这有助于提高用户的使用体验,增强用户对API的信任度。
3. 增强信任度:在API应用中,使用HTTPS协议能够提升服务端的信任度。通过展示有效的SSL证书,证明服务端的身份和安全性,增强用户和服务之间的信任关系。
4. 法规遵循:在许多国家和地区,对于涉及敏感数据的API,使用HTTPS已成为法规要求。例如,处理个人信息的API必须采用HTTPS协议,以确保用户数据的安全。因此,使用HTTPS有助于API应用遵循相关法规,降低合规风险。
5. 便于监控与优化:HTTPS协议为API提供了更好的监控和优化能力。通过监控HTTPS请求的数据量和性能,开发人员可以实时了解API的使用情况,发现并解决潜在的性能问题。利用HTTP/2等高级特性,还可以进一步优化API的性能和响应时间。
6. 广泛的支持与集成:随着HTTPS技术的普及和发展,现代浏览器、操作系统和开源库都广泛支持HTTPS协议。这使得在API应用中集成HTTPS变得相对简单和容易,降低了开发和维护成本。
五、结论
HTTPS在API应用中的实践与优势不容忽视。
通过加密传输、身份验证与授权以及完整性校验等手段,HTTPS能够确保API数据的安全性和可靠性。
同时,HTTPS还有助于提升用户体验、增强信任度、遵循法规要求以及便于监控与优化。
因此,在设计和实施API时,应充分考虑使用HTTPS协议,以确保API的安全性和可靠性。
如何通过调用API实现HTTP的POST方式
API:应用程序接口(API:Application Program Interface) 应用程序接口(API:application programming interface)是一组定义、程序及协议的集合
有状态的Web应用程序都有漏洞吗?求答案
在没有足够协作的情况下,许多 Web 应用程序对可变数据(比如 JavaBeans 类)使用了 HttpSession 这个机制,从而使自身面临大量潜在的并发性危险。
虽然Java? 生态系统中存在许多 Web 框架,但它们都直接或间接地基于 Servlets 基础设施。
Servlets API 提供大量有用特性,包括通过 HttpSession 和ServletContext 机制提供的状态管理,它允许应用程序在跨多个用户请求时保持状态。
然而,在 Web 应用程序中使用共享状态受一些微妙的(并且大部分没有进行说明)的规则控制着,因此导致许多应用程序无意中触犯了规则。
结果是许多有状态 Web 应用程序存在难以发觉的严重缺陷。
范围容器在Servlet 规范中,ServletContext、HttpSession 和HttpRequest 对象被称为 范围容器(Scoped container)。
它们都有 getAttribute() 和setAttribute() 方法,为应用程序存储数据。
这些范围容器的区别在于它们的生命周期不同。
对于 HttpRequest,数据只在请求期间有效;对于 HttpSession,数据在用户和应用程序的会话期间有效;而对于 ServletContext,数据在应用程序的整个生命周期中都有效。
由于HTTP 协议是没有状态的,所以范围容器在构造有状态 Web 应用程序时十分有用;servlet 容器负责管理应用程序的状态和数据的生命周期。
尽管这个规范没有强调,但需要保证嵌套在会话或应用程序中的容器在某种程度上是线程安全的,因为 getAttribute() 和setAttribute() 方法随时都可能被不同的线程调用(这个规范没有直接要求这些实现必须是线程安全的,但它们提供的服务实际上提出了这一点)。
范围容器还为 Web 应用程序提供一个潜在的好处:容器可以透明地管理应用程序状态的复制和故障转移。
会话session 是特定用户和 Web 应用程序之间的一系列请求-响应交换。
用户希望 Web 站点记住他的身份验证凭证、购物车的物品,以及在前面的请求中输入到 Web 表单的信息。
但核心 HTTP 协议是没有状态的,这意味着必须将所有请求信息存储到请求本身。
因此,如果要创建比一个请求-响应周期更长的有用交互,就必须存储会话状态。
servlet 框架允许将每个请求与一个会话关联起来,并提供充当值存储库的 HttpSession 接口,用于存储与该会话相关的(键,值)数据项。
清单 1 是一段典型的 servlet 代码,它用于在 HttpSession 中存储购物车数据:清单1. 使用 HttpSession 存储购物车数据 HttpSession session = (true); ShoppingCart cart = (ShoppingCart)(shoppingCart); if (cart == null) { cart = new ShoppingCart(…); (shoppingCart); } doSomethingWith(cart); 清单1 提供的是 servlet 的典型用途;这个应用程序查看是否已将一个对象放到会话中,如果还没有,它将创建一个该会话的后续请求可以使用的对象。
构建在 servlet 之上的 Web 框架(比如 JSP、JSF 和 SpringMVC 等)隐藏了细节情况,但对于标记为嵌入到会话中的数据,它们替您执行了实际操作。
不幸的是,清单 1 中的用法很可能是不正确的。
与线程相关的问题当HTTP 请求到达 servlet 容器之后,会在 servlet 容器管理的线程上下文中创建 HttpRequest 和HttpResponse 对象,并传递给 servlet 的 service() 方法。
servlet 负责生成响应;在响应完成之前 servlet 一直控制这个线程,响应完成时该线程返回到可用的线程池。
Servlet 容器没有保持线程与会话之间的联系;某个会话的下一个请求很可能由另一个不同的线程来处理。
事实上,可能有多个请求同时进入同一个会话(当用户与页面交互时,使用框架或 AJAX 技术从服务器获取数据的 Web 应用程序可能发生这种现象)。
在这种情况,同一用户可能发出多个请求,这些请求在不同的线程上并行执行。
大多数情况下,这类线程问题与 Web 应用程序开发人员无关。
由于自身没有状态,HTTP 鼓励只有存储在请求中的数据(不与其他并发请求共享)和存储在存储库(比如数据库)中的、已进行并发 …展开在没有足够协作的情况下,许多 Web 应用程序对可变数据(比如 JavaBeans 类)使用了 HttpSession 这个机制,从而使自身面临大量潜在的并发性危险。
虽然Java? 生态系统中存在许多 Web 框架,但它们都直接或间接地基于 Servlets 基础设施。
Servlets API 提供大量有用特性,包括通过 HttpSession 和ServletContext 机制提供的状态管理,它允许应用程序在跨多个用户请求时保持状态。
然而,在 Web 应用程序中使用共享状态受一些微妙的(并且大部分没有进行说明)的规则控制着,因此导致许多应用程序无意中触犯了规则。
结果是许多有状态 Web 应用程序存在难以发觉的严重缺陷。
范围容器在Servlet 规范中,ServletContext、HttpSession 和HttpRequest 对象被称为 范围容器(Scoped container)。
它们都有 getAttribute() 和setAttribute() 方法,为应用程序存储数据。
这些范围容器的区别在于它们的生命周期不同。
对于 HttpRequest,数据只在请求期间有效;对于 HttpSession,数据在用户和应用程序的会话期间有效;而对于 ServletContext,数据在应用程序的整个生命周期中都有效。
由于HTTP 协议是没有状态的,所以范围容器在构造有状态 Web 应用程序时十分有用;servlet 容器负责管理应用程序的状态和数据的生命周期。
尽管这个规范没有强调,但需要保证嵌套在会话或应用程序中的容器在某种程度上是线程安全的,因为 getAttribute() 和setAttribute() 方法随时都可能被不同的线程调用(这个规范没有直接要求这些实现必须是线程安全的,但它们提供的服务实际上提出了这一点)。
范围容器还为 Web 应用程序提供一个潜在的好处:容器可以透明地管理应用程序状态的复制和故障转移。
会话session 是特定用户和 Web 应用程序之间的一系列请求-响应交换。
用户希望 Web 站点记住他的身份验证凭证、购物车的物品,以及在前面的请求中输入到 Web 表单的信息。
但核心 HTTP 协议是没有状态的,这意味着必须将所有请求信息存储到请求本身。
因此,如果要创建比一个请求-响应周期更长的有用交互,就必须存储会话状态。
servlet 框架允许将每个请求与一个会话关联起来,并提供充当值存储库的 HttpSession 接口,用于存储与该会话相关的(键,值)数据项。
清单 1 是一段典型的 servlet 代码,它用于在 HttpSession 中存储购物车数据:清单1. 使用 HttpSession 存储购物车数据 HttpSession session = (true); ShoppingCart cart = (ShoppingCart)(shoppingCart); if (cart == null) { cart = new ShoppingCart(…); (shoppingCart); } doSomethingWith(cart); 清单1 提供的是 servlet 的典型用途;这个应用程序查看是否已将一个对象放到会话中,如果还没有,它将创建一个该会话的后续请求可以使用的对象。
构建在 servlet 之上的 Web 框架(比如 JSP、JSF 和 SpringMVC 等)隐藏了细节情况,但对于标记为嵌入到会话中的数据,它们替您执行了实际操作。
不幸的是,清单 1 中的用法很可能是不正确的。
与线程相关的问题当HTTP 请求到达 servlet 容器之后,会在 servlet 容器管理的线程上下文中创建 HttpRequest 和HttpResponse 对象,并传递给 servlet 的 service() 方法。
servlet 负责生成响应;在响应完成之前 servlet 一直控制这个线程,响应完成时该线程返回到可用的线程池。
Servlet 容器没有保持线程与会话之间的联系;某个会话的下一个请求很可能由另一个不同的线程来处理。
事实上,可能有多个请求同时进入同一个会话(当用户与页面交互时,使用框架或 AJAX 技术从服务器获取数据的 Web 应用程序可能发生这种现象)。
在这种情况,同一用户可能发出多个请求,这些请求在不同的线程上并行执行。
大多数情况下,这类线程问题与 Web 应用程序开发人员无关。
由于自身没有状态,HTTP 鼓励只有存储在请求中的数据(不与其他并发请求共享)和存储在存储库(比如数据库)中的、已进行并发性控制的数据,才具有响应功能。
然而,一旦 Web 应用程序将数据存储在 HttpSession 或ServletContext 等共享容器之后,该 Web 应用程序就具有了并发性,因此必须考虑应用程序内部的线程安全问题。
尽管我们常用线程安全描述代码,但实际上它是描述数据的。
具体来说,线程安全是指适当地协调对被多个线程访问的可变数据的访问。
Servlet 应用程序通常是线程安全的,因为它们没有共享任何可变数据,因此就不需要额外的同步。
但可以通过很多种办法将共享状态引入到 Web 应用程序 — 除了 HttpSession 和ServletContext 等范围容器外,还可以使用 HttpServlet 对象的静态字段和实例字段。
如果要让 Web 应用程序跨请求共享数据,开发人员就必须注意共享数据的位置,并保证访问共享数据时线程之间有足够的协作(同步),以避免与线程相关的危险。
Web 应用程序的线程风险如果Web 应用程序将购物车等可变会话数据存储在 HttpSession 中,就有可能出现两个请求试图同时访问该购物车。
这可能导致以下几种故障: 原子性故障。
在数据不一致的状态下,一个线程正在更新多个数据项,而另一个线程正在读取这些数据 读线程和写线程之间的可见性故障。
一个线程修改购物车,但另一个线程看到的购物车内容的状态是过时的或不一致的 原子性故障清单2 展示了一个用于在游戏应用程序中设置和获取最高分的不恰当的方法实现。
它使用一个 PlayerScore 对象来表示最高分,这实际上是一个具有 name 和score 属性的普通 JavaBean 类,存储在内嵌于应用程序的 ServletContext 中(假设在应用程序启动时,初始最高分在 ServletContext 中被设置为 highScore 属性,因此 getAttribute() 调用不会失败)。
清单2. 在范围容器中存储相关项的不恰当模式 public PlayerScore getHighScore() { ServletContext ctx = getServletConfig()(); PlayerScore hs = (PlayerScore) (highScore); PlayerScore result = new PlayerScore(); (()); (()); return result; } public void updateHighScore(PlayerScore newScore) { ServletContext ctx = getServletConfig()(); PlayerScore hs = (PlayerScore) (highScore); if (() > ()) { (()); (()); } } 清单2 中的代码不够好。
这里采用的方法是在 ServletContext 中存储一个包含最高分玩家的名字和分数的可变容器。
当打破记录时,必须更新名字和分数。
假如当前的最高分玩家是 Bob,他的分数为 1000,但是 Joe 以 1100 分打破了这个记录。
在正要设置 Joe 的分数时,另一个玩家又发出获得最高分的请求。
getHighScore() 将从servlet 上下文获取 PlayerScore 对象,然后从该对象获取名字和分数。
如果不慎出现计时失误,就有可能获取 Bob 的名字和 Joe 的分数,显示 Bob 取得了 1100 分,而实际上 Bob 并没有获得这个分数(这种故障在免费游戏站点上是可以接受的,因为 “分数” 并不是 “银行存款”)。
这是一种原子性故障,因为彼此之间本应该是原子关系的两个操作 — 获取名字/分数对和更新名字/分数对 — 实际上并没有按照原子关系执行,而且其中一个线程可以在不一致状态下查看共享数据。
另外,由于分数更新逻辑遵循 check-then-act 模式,因此可能出现两个线程 “争夺” 更新最高分,从而导致难以预料的结果。
假设当前的最高分是 1000,有两个玩家同时注册更高的分数 1100 和 1200。
如果出现计时失误,这两个分数都能够通过 “高于现有分数的最高分” 检查,并且都进入到更新最高分的代码块中。
和前面一样,根据计时的实际情况,最后的结果可能不一致(采用一个玩家的名字和另一个玩家的分数),或者出现错误(分数为 1100 的玩家可能覆盖分数为 1200 的玩家)。
可见性故障 比原子性故障更复杂的是可见性 故障。
没有同步时,如果一个线程读取另外一个线程正在写的变量,读的线程将看到过时的 数据。
更糟糕的是,读线程还可能会看到 x 变量的最新数据和 y 变量的过时数据,即使先写 y 变量也是这样。
可见性故障非常复杂,因为它的发生是随机的,甚至是频繁的,这会导致罕见的难以调试的间发性故障。
可见性故障是由数据争夺引起的 — 访问共享变量时不能正确同步。
争夺数据的程序,不管它是什么样的,都属于有漏洞的程序,因为它们的行为不能可靠预测。
Java Memory Model(JMM)定义一些条件,它们保证读变量的线程能够看到另一个线程的写入结果(详细讲解 JMM 超出了本文的范围;参见 参考资料)。
JMM 在一个称为 happens-before 的程序的操作上定义一个排序。
只有在通用锁上执行同步或访问一个通用的可变变量时,才能创建跨线程的 Happens-before 排序。
在没有 happens-before 排序的情况下, Java 平台可以延迟或更改顺序,按照这个顺序,一个线程的写操作对于另一个读取同一变量的线程是可见的。
清单2 中的代码不仅有原子性故障,还有可见性故障。
updateHighScore() 方法从 ServletContext 获取HighScore 对象,然后修改它的状态。
这样做的目的是让其他调用 getHighScore() 的线程看见这些修改,但是如果 updateHighScore() 的name 和 score 属性的写操作和其他调用 getHighScore() 的线程的 name 和 score 属性的读操作之间没有 happens-before 排序,我们只能期盼运气好些,让读线程能够看到正确的值。
可能的解决方案尽管servlet 规范没有充分地描述 servlet 容器必须提供的 happens-before 保证,但可以得出结论:将一个属性放置在共享范围容器(HttpSession 或ServletContext)应该在另一个线程获取该属性之前发生。
(参见 JCiP 4.5.1 了解这个结论的推理过程。
该规范中这样描述:“执行请求线程的多个 servlets 可能同时积极地访问单个会话对象。
开发人员负责恰当地同步对会话资源的访问)。
set-after-write 技巧更新存储在其他会话容器中的可变数据时,必须在修改该数据后再次调用 setAttribute()。
这是一种常用的最佳实践。
清单 3 展示了重写 updateHighScore() 以使用这个技巧的示例(这个技巧的目的之一是提示容器值已经更改,因此可以在分布式 Web 应用程序的各个实例之间重新同步会话和应用程序状态)。
清单3. 使用 set-after-write 技巧提示 servlet 容器值已经更新 public void updateHighScore(PlayerScore newScore) { ServletContext ctx = getServletConfig()(); PlayerScore hs = (PlayerScore) (highScore); if (() > ()) { (()); (()); (highScore, hs); } } 不幸的是,尽管这个技巧能够在集群应用程序中高效地复制会话和应用程序状态,但它不能解决本例中的基本线程安全问题。
它可以减轻可见性问题(即另一个玩家可能永远看不到在 updateHighScore() 中更新的值),但还不能解决许多潜在的原子性问题。
利用同步set-after-write 技巧可以消除可见性问题,因为 happens-before 排序是可传递的,因而调用 updateHighScore() 中的setAttribute() 和调用 getHighScore() 中的getAttribute() 之间有一个边缘地带。
因为 HighScore 状态的更新在 setAttribute() 之前发生,setAttribute() 状态的更新在从 getAttribute() 返回之前发生,getAttribute() 状态的更新在 getHighScore() 的调用方使用状态之前发生,所以通过这种传递可以得出结论:调用方 getHighScore() 看到的值至少和 setAttribute() 的最近一次调用一样新。
这个技巧称为利用同步(piggybacking on synchronization),因为 getHighScore() 和updateHighScore() 方法能够在 getAttribute() 和setAttribute() 中使用同步信息来提供一些可见性保证。
然而,在上面这个例子中,这还不能完全解决问题。
set-after-write 技巧可能对状态复制非常有用,但还不能提供线程安全。
了解不可修改性要创建线程安全的应用程序,一个有用的技巧便是尽可能多地使用不可修改的数据。
清单 4 展示了重写后的最高分示例,它使用了 HighScore 的不可修改的 实现,从而避免了原子性故障(允许调用方看见不存在的玩家/分数对)和可见性故障(阻止 getHighScore() 的调用方看见在调用 updateHighScore() 时写的最新值): 清单4. 使用不可修改的 HighScore 对象修复原子性和可见性漏洞 Public class HighScore { public final String name; public final int score; public HighScore(String name, int score) { = name; = score; } } public PlayerScore getHighScore() { ServletContext ctx = getServletConfig()(); return (PlayerScore) (highScore); } public void updateHighScore(PlayerScore newScore) { ServletContext ctx = getServletConfig()(); PlayerScore hs = (PlayerScore) (highScore); if ( > ) (highScore, newScore); } 清单4 中的代码的潜在故障很少。
在 setAttribute() 和getAttribute() 中使用同步保证了可见性。
实际上,仅存储单个不可修改数据项消除了潜在的原子性故障,即 getHighScore() 的调用方可以看见名字/分数对的不一致更新。
将不可修改对象放置在范围容器避免了许多原子性和可见性故障;将有效不可修改性 对象放置在范围容器中也是安全的。
有效不可修改性对象是指那些虽然理论上是可修改的,但实际上在发布之后再没有被更改过的对象,比如 JavaBean,将一个对象放置到 HttpSession 中之后,它的 setter 方法就不再被调用。
放置在 HttpSession 中的数据不仅被该会话的请求访问;它还可能被容器本身访问(如果容器进行状态复制的话)。
所有放置在 HttpSession 或ServletContext 中的数据应该是线程安全的或有效不可修改的。
影响原子状态转换但是清单4 中的代码仍然有一个问题 — updateHighScore() 中的check-then-act 仍然使两个试图更新最高分数的线程之间存在潜在 “争夺”。
如果计时失误,有一个更新可能会丢失。
两个线程可能同时通过了 “高于现有分数的新最高分” 检查,造成它们同时调用 setAttribute()。
不能确保两个分数中最高者获得调用,这取决于计时。
要修复这个最后的漏洞,我们需要一种原子性地更新分数引用的方法,同时又要保证不受干扰。
有几种方法可以实现这个目的。
清单5 为 updateHighScore() 添加了同步,确保更新进程中固有的 check-then-act 不和另一个更新并发执行。
如果所有条件修改逻辑获得 updateHighScore() 使用的同一个锁,用这种方法就可以了。
清单5. 使用同步修复最后一个原子性漏洞 public void updateHighScore(PlayerScore newScore) { ServletContext ctx = getServletConfig()(); PlayerScore hs = (PlayerScore) (highScore); synchronized (lock) { if ( > ) (highScore, newScore); } } 虽然清单 5 中的技术是可行的,但还有一个更好的技术:使用 包中的 AtomicReference 类。
这个类的用途就是通过 compareAndSet() 调用提供原子条件更新。
清单 6 展示了如何使用 AtomicReference 来修复本示例的最后一个原子性问题。
这个方法比清单 5 中的代码好,因为很难违背更新最高分数的规则。
清单6. 使用 AtomicReference 来修复最后一个原子性漏洞 public PlayerScore getHighScore() { ServletContext ctx = getServletConfig()(); AtomicReferenceholder = (AtomicReference) (highScore); return (); } public void updateHighScore(PlayerScore newScore) { ServletContext ctx = getServletConfig()(); AtomicReference holder = (AtomicReference) (highScore); while (true) { HighScore old = (); if ( >= ) break; else if ((old, newScore)) break; } } 对于放置在范围容器中的可修改数据,应该将它们的状态转换变成原子性的,这可以通过同步或 中的原子变量类来实现。
序列化对 HttpSession 的访问在我已给出的示例中,我试图避免与访问整个应用程序中的 ServletContext 相关的各种危险。
很明显,访问 ServletContext 时需要细心的协作,因为任何请求都可以访问 ServletContext。
然而,大多数有状态 Web 应用程序严重依赖于内嵌于会话的容器 HttpSession。
同一个会话中发生多个同步请求的原因不是很直观;毕竟,每个会话都是绑定到一个特定用户或浏览器会话的,并且用户不一定一次请求多个页面。
但是在编程式地生成请求的应用程序中(比如 AJAX 应用程序),一个会话中的请求是可以重叠的。
单个会话中的请求当然可以是重叠的,但这不是一件好事。
如果可以轻松地序列化会话中的请求,当访问 HttpSession 中的共享对象时,这里提到的所有问题几乎都可以解决;序列化可以阻止原子性故障,并且利用 HttpSession 中的同步可以阻止可见性故障。
序列化特定会话的请求不会对吞吐量造成很大的影响,因为一个会话中的请求很少重叠,在一个会话中出现很多请求重叠就更罕见了。
不幸的是,servlet 规范并没有提到 “序列化同一会话中的请求”。
不过 SpringMVC 框架提供了一种方法,并且这种方法可以在其他框架中轻松实现。
SpringMVC 控制器的基类 AbstractController 提供了一个布尔变量 synchronizeOnSession;设置这里之后,它将使用一个锁,确保一个会话中只同时执行一个请求。
序列化 HttpSession 上的请求消除了很多并发性危险。
同样,将对象限制在 Event Dispatch Thread(EDT)中减少了 Swing 应用程序中的同步需求。
结束语许多有状态 Web 应用程序有很严重的并发性漏洞。
在没有足够协调的情况下,访问存储在 HttpSession 和ServletContext 等范围容器中的可变数据就会产生这些漏洞。
开发人员通常会误认为 getAttribute() 和setAttribute() 方法中的同步已经足够 但这只能应付特定情况,比如当属性是不可修改、有效不可修改或线程安全的对象时,或当可能访问容器的请求被序列化时。
通常,放置在范围容器中的所有内容都应该是高度不可修改的或线程安全的。
servlet 规范提供的范围容器机制并不管理没有提供它们自己的同步的可变对象。
将普通的 JavaBean 类存储在 HttpSession 中是很大的隐患。
收起
怎样在应用程序中使用SSL
HTTPS实际是SSL over HTTP, 该协议通过SSL在发送方把原始数据进行加密,在接收方解密,因此,所传送的数据不容易被网络黑客截获和破解。
本文介绍HTTPS的三种实现方法。
方法一 静态超链接这是目前网站中使用得较多的方法,也最简单。
在要求使用SSL进行传输的Web网页链接中直接标明使用HTTPS协议,以下是指向需要使用SSL的网页的超链接:SSL例子需要说明的是,在网页里的超链接如果使用相对路径的话,其默认启用协议与引用该超链接的网页或资源的传输协议相同,例如在某超链接“”的网页中包含如下两个超链接:SSL链接非SSL链接那么,第一个链接使用与“”相同的传输协议HTTPS,第二个链接使用本身所标识的协议HTTP。
使用静态超链接的好处是容易实现,不需要额外开发。
然而,它却不容易维护管理; 因为在一个完全使用HTTP协议访问的Web应用里,每个资源都存放在该应用特定根目录下的各个子目录里,资源的链接路径都使用相对路径,这样做是为了方便应用的迁移并且易于管理。
但假如该应用的某些资源要用到HTTPS协议,引用的链接就必须使用完整的路径,所以当应用迁移或需要更改URL中所涉及的任何部分如:域名、目录、文件名等,维护者都需要对每个超链接修改,工作量之大可想而知。
再者,如果客户在浏览器地址栏里手工输入HTTPS协议的资源,那么所有敏感机密数据在传输中就得不到保护,很容易被黑客截获和篡改!方法二 资源访问限制为了保护Web应用中的敏感数据,防止资源的非法访问和保证传输的安全性,Java Servlet 2.2规范定义了安全约束(Security-Constraint)元件,它用于指定一个或多个Web资源集的安全约束条件;用户数据约束(User-Data-Constraint)元件是安全约束元件的子类,它用于指定在客户端和容器之间传输的数据是如何被保护的。
用户数据约束元件还包括了传输保证(Transport-Guarantee)元件,它规定了客户机和服务器之间的通信必须是以下三种模式之一:None、Integral、Confidential。
None表示被指定的Web资源不需要任何传输保证;Integral表示客户机与服务器之间传送的数据在传送过程中不会被篡改; Confidential表示数据在传送过程中被加密。
大多数情况下,Integral或Confidential是使用SSL实现。
这里以BEA的WebLogic Server 6.1为例介绍其实现方法,WebLogic是一个性能卓越的J2EE服务器,它可以对所管理的Web资源,包括EJB、JSP、Servlet应用程序设置访问控制条款。
假设某个应用建立在Weblogic Server里的/mywebAPP目录下,其中一部分Servlets、JSPs要求使用SSL传输,那么可将它们都放在/mywebAPP/sslsource/目录里,然后编辑/secureAPP/Web-INF/文件,通过对的设置可达到对Web用户实现访问控制。
当Web用户试图通过HTTP访问/sslsource目录下的资源时,Weblogic Server就会查找里的访问约束定义,返回提示信息:Need SSL connection to access this resource。
资源访问限制与静态超链接结合使用,不仅继承了静态超链接方法的简单易用性,而且有效保护了敏感资源数据。
然而,这样就会存在一个问题: 假如Web客户使用HTTP协议访问需要使用SSL的网络资源时看到弹出的提示信息: Need SSL connection to access this resource,大部分人可能都不知道应该用HTTPS去访问该网页,造成的后果是用户会放弃访问该网页,这是Web应用服务提供商不愿意看到的事情。
方法三 链接重定向综观目前商业网站资源数据的交互访问,要求严格加密传输的数据只占其中一小部分,也就是说在一个具体Web应用中需要使用SSL的服务程序只占整体的一小部分。
那么,我们可以从应用开发方面考虑解决方法,对需要使用HTTPS协议的那部分JSPs、Servlets或EJBs进行处理,使程序本身在接收到访问请求时首先判断该请求使用的协议是否符合本程序的要求,即来访请求是否使用HTTPS协议,如果不是就将其访问协议重定向为HTTPS,这样就避免了客户使用HTTP协议访问要求使用HTTPS协议的Web资源时,看到错误提示信息无所适从的情况,这些处理对Web客户来说是透明的。
实现思想是:首先创建一个类,该类方法可以实现自动引导Web客户的访问请求使用HTTPS协议,每个要求使用SSL进行传输的Servlets或JSPs在程序开始时调用它进行协议重定向,最后才进行数据应用处理。
J2EE提供了两种链接重定向机制。
第一种机制是RequestDispatcher接口里的forward()方法。
使用MVC(Model-View-Controller)机制的Web应用通常都使用这个方法从Servlet转移请求到JSP。
但这种转向只能是同种协议间的转向,并不能重定向到不同的协议。
第二种机制是使用HTTPServletReponse接口里的sendRedirect()方法,它能使用任何协议重定向到任何URL,例如(“”);此外,我们还需使用到Java Servlet API中的两个方法:ServletRequest接口中的getScheme(),它用于获取访问请求使用的传输协议;HTTPUtils类中的getRequestUrl(),它用于获取访问请求的URL,要注意的是该方法在Servlet 2.3中已被移到HTTPServletRequest接口。
以下是实现协议重定向的基本步骤:1. 获取访问的请求所使用的协议;2. 如果请求协议符合被访问的Servlet所要求的协议,就说明已经使用HTTPS协议了,不需做任何处理;3. 如果不符合,使用Servlet所要求的协议(HTTPS)重定向到相同的URL。
例如,某Web用户使用HTTP协议访问要求使用HTTPS协议的资源BeSslServlet,敲入“URL:”,在执行BeSslServlet时首先使用ProcessSslServlet.processSsl()重定向到,然后 BeSslServlet与客户浏览器之间就通过HTTPS协议进行数据传输。
以上介绍的仅是最简单的例子,是为了对这种重定向的方法有个初步的认识。
假如想真正在Web应用中实现,还必须考虑如下几个问题:● 在Web应用中常常会用到GET或Post方法,访问请求的URL中就会带上一些查询字串,这些字串是使用getRequesUrl()时获取不到的,而且在重定向之后会丢失,所以必须在重定向之前将它们加入到新的URL里。
我们可以使用()来获取GET的查询字串,对于Post的Request参数,可以把它们转换成查询串再进行处理。
● 某些Web应用请求中会使用对象作为其属性,必须在重定向之前将这些属性保存在该Session中,以便重定向后使用。
● 大多数浏览器会把对同一个主机的不同端口的访问当作对不同的主机进行访问,分用不同的Session,为了使重定向后保留使用原来的Session,必须对应用服务器的Cookie 域名进行相应的设置。
以上问题均可在程序设计中解决。
通过程序自身实现协议重定向,就可以把要求严格保护的那部分资源与其他普通数据从逻辑上分开处理,使得要求使用SSL的资源和不需要使用SSL的资源各取所需,避免浪费网站的系统资源。