文章是对 JSR-000340 JavaTM Servlet 3.1 Final Release的Java™ Servlet规范的翻译,尚未校准
Web应用程序是由应用程序开发员创建的,他们将应用程序给予、出售或以其他方式转让给部署者以安装到运行环境中。应用程序开发人员将安全要求传达给部署者和部署系统。这些信息可以通过应用程序的部署描述符以声明的方式传达,也可以通过在应用程序代码中使用注解的方式传达,还可以通过 ServletRegistration接口的 setServletSecurity方法以编程方式传达。
本章描述了Servlet容器的安全机制和接口,以及用于传达应用程序安全要求的部署描述符、注解和编程机制。
一个Web应用程序包含可以被许多用户访问的资源。这些资源经常公布在不受保护的开放网络,如互联网。在这样的环境中,大量的网络Web应用程序会有安全要求。
尽管质量保证和实施细节可能有所不同,但servlet容器有满足这些要求的机制和基础设施,它们有以下一些共同的特点。
声明式安全指的是以应用程序的外部形式表达应用程序的安全模型或要求的方法,包括角色、访问控制和认证要求。部署描述符是Web应用程序中声明式安全的主要工具。
部署者将应用程序的逻辑安全要求映射到运行时环境特定的安全策略的表示上。在运行时,Servlet容器使用安全策略表示来执行认证和授权。
安全模型适用于Web应用程序的静态内容部分,以及应用程序中由客户端请求的Servlet和过滤器。不适用于当servlet使用RequestDispatcher调用静态资源或使用forward或include的servlet的场景。
当仅仅声明式安全是不足以表达应用的安全模型时,编程式安全被用于意识到安全的应用。编程式安全包括以下HttpServletRequest接口的方法:
login方法允许应用程序执行用户名和密码收集(作为基于表单的登录的替代)。
authenticate方法允许应用程序从无限制的请求上下文中启动容器对请求调用者的认证。
logout方法允许应用程序重置请求的调用者身份。
getRemoteUser方法返回容器与请求相关的远程用户(也就是调用者)的名字。
isUserInRole方法确定与请求相关的远程用户(也就是调用者)是否处于指定的安全角色。
getUserPrincipal方法确定远程用户(即调用者)的本名,并返回一个与远程用户对应的java.security.Principal对象。在getUserPrincipal返回的Principal上调用getName方法,会返回远程用户的名字。这些API允许servlets根据获得的信息做出业务逻辑决策。
如果没有用户被认证,getRemoteUser方法返回null,isUserInRole方法总是返回false,而getUserPrincipal方法返回null。
isUserInRole方法需要一个 "String "参数,引用一个应用程序的角色。对于调用isUserInRole时使用的每个不同的角色引用,应在部署描述符中声明一个安全-角色-ref元素,其角色名称与该角色引用相对应。每个 security-role-ref 都应该包含一个 role-link 子元素,其值是应用程序嵌入式角色引用所链接的应用程序安全角色的名称。容器使用角色名称等于角色引用的 security-roleref 来确定要测试用户的成员身份的安全角色。
例如,将安全角色引用 "FOO "映射到安全角色的名字为 “manager”,其语法为:
FOO manager
在这种情况下,如果一个由属于 "manager "安全角色的用户调用的servlet调用isUserInRole("FOO"),结果将是true。
如果在调用isUserInRole时使用的角色引用没有matching security-role-ref,容器必须默认测试用户是否属于role-name等于调用中使用的角色引用的安全角色。
角色名称 "*"决不能作为调用 isUserInRole 的参数。任何带有 "*"的对 isUserInRole的调用必须返回错误。如果要测试的安全角色的角色名是 “**”,并且应用程序没有声明具有角色名 "**"的应用程序安全角色,isUserInRole必须只在用户已被验证时返回 true;也就是说,只有当 getRemoteUser和 getUserPrincipal都将返回一个非空的值时。否则,容器必须检查用户是否是应用角色的成员。
security-role-ref元素的声明告知部署者应用程序所使用的角色参考,以及必须为其定义映射。
本节定义了为配置Servlet容器强制执行的安全约束而提供的注释和apis。
@ServletSecurity注解为定义访问控制约束提供了一种替代机制,相当于那些可以通过便携式部署描述符中的security-constraint元素或通过ServletRegistration接口的setServletSecurity方法以编程方式表示。Servlet容器必须支持对实现javax.servlet.Servlet接口的类(及其子类)使用@ServletSecurity注解。
package javax.servlet.annotation;@Inherited
@Documented
@Target(value=TYPE)
@Retention(value=RUNTIME)
public @interface ServletSecurity {HttpConstraint value();HttpMethodConstraint[] httpMethodConstraints();
}
TABLE 13-1 The ServletSecurity Interface
| Element | Description | Default |
|---|---|---|
| value | HttpConstraint定义了应用于所有HTTP方法的保护,这些方法不在httpMethodConstraints所返回的数组中。 | @HttpConstraint |
| httpMethodConstrain | HTTP方法特定的约束数组 | {} |
@HttpConstraint
@HttpConstraint注解是在@ServletSecurity注解中使用的,用来表示适用于所有HTTP协议方法的安全约束,这些方法的相应@HttpMethodConstraint并没有出现在@ServletSecurity注解中。
对于特殊情况,即返回所有默认值的@HttpConstraint与至少一个返回所有默认值以外的@HttpMethodConstraint相结合,@HttpConstraint表示没有安全约束将被应用到任何HTTP协议方法上,否则安全约束将被应用。这个例外是为了确保对@HttpConstraint的这种潜在的非特定使用不会产生约束,这些约束将为这些方法明确地建立不受保护的访问;因为它们不会被约束所覆盖。
package javax.servlet.annotation;@Documented
@Retention(value=RUNTIME)
public @interface HttpConstraint {ServletSecurity.EmptyRoleSemantic value();java.lang.String[] rolesAllowed();ServletSecurity.TransportGuarantee transportGuarantee();
}
TABLE 13-2 The HttpConstraint Interface
| Element | Description | Default |
|---|---|---|
| value | 默认的授权语义,适用于(仅)当roleAllowed返回一个空的数组。 | PERMIT |
| rolesAllowed | An array containing the names of the authorized roles一个包含授权角色名称的数组 | {} |
| transportGuarantee | 在连接的请求到达时必须满足的数据保护需求。 | NONE |
@HttpMethodConstraint
@HttpMethodConstraint注解被用于@ServletSecurity注解中,以表示对特定HTTP协议消息的安全约束。
package javax.servlet.annotation;
@Documented
@Retention(value=RUNTIME)
public @interface HttpMethodConstraint {ServletSecurity.EmptyRoleSemantic value();java.lang.String[] rolesAllowed();ServletSecurity.TransportGuarantee transportGuarantee();
}
TABLE 13-3 The HttpMethodConstraint Interface
| Element | Description | Default |
|---|---|---|
| value | HTTP协议的方法名称 | |
| emptyRoleSemantic | The default authorization semantic that applies (only) when rolesAllowed returns an empty array.默认的授权语义,适用于(仅)当roleAllowed返回一个空数组时。 | PERMIT |
| rolesAllowed | An array containing the names of the authorized roles 一个包含授权角色名称的数组 | {} |
| transportGuarantee | 在连接的请求到达时必须满足的数据保护需求。 | NONE |
@ServletSecurity注解可以在Servlet实现类上指定(即针对),其值由子类根据为@Inherited元注解定义的规则继承。在一个Servlet实现类中最多可以出现一个@ServletSecurity注解的实例,而且@ServletSecurity注解一定不能被指定在(即针对)一个Java方法上。
当一个或多个@HttpMethodConstraint注解被定义在一个@ServletSecurity注解中时,每个@HttpMethodConstraint定义了适用于@HttpMethodConstraint中确定的HTTP协议方法的security-constraint。除了它的@HttpConstraint返回所有默认值的情况,以及它至少包含一个@HttpMethodConstraint返回所有默认值以外的情况,@ServletSecurity注解定义了另一个 security-constraint,适用于所有HTTP协议方法,这些方法的相应@HttpMethodConstraint还没有被定义。
在便携式部署描述符中定义的 security-constraint元素对于约束中出现的所有 url-patterns都是权威的。
当便携式部署描述符中的security-constraint包括一个url-pattern,它与映射到@ServletSecurity注释的类的模式完全匹配,该注释必须对Servlet容器对该模式执行的约束没有影响。
当 metadata-complete=true 被定义为便携式部署描述符时,@ServletSecurity注解不适用于部署描述符中映射到(任何Servlet映射到)被注解类的任何 url-patterns。
@ServletSecurity注解不适用于使用ServletContext接口的addServlet(String, Servlet)方法创建的ServletRegistration的url-patterns,除非该Servlet是由ServletContext接口的createServlet方法构建。
除了上面列出的例外情况,当一个Servlet类被注解为@ServletSecurity时,该注解定义了适用于所有映射到该类的Servlet的url-patterns的安全约束。
当一个类没有被注解为@ServletSecurity时,应用于从该类映射出来的Servlet的访问策略是由相应的可移植部署描述符中适用的 "security-constraint "元素(如果有的话)建立的,如果没有这些元素,则由通过 ServletRegistration接口的 setServletSecurity方法为目标Servlet以编程方式建立的约束(如果有的话)建立。
下面的例子演示了ServletSecurity注释的用法。
CODE EXAMPLE 13-1 对于所有的HTTP方法,没有约束
@ServletSecurity
public class Example1 extends HttpServlet {
}
CODE EXAMPLE 13-2 适用于所有的HTTP方法,没有授权限制,需要保密传输
@ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
public class Example2 extends HttpServlet {
}
CODE EXAMPLE 13-3 对于所有的HTTP方法,所有的访问被拒绝
@ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
public class Example3 extends HttpServlet {
}
CODE EXAMPLE 13-4 对于所有的HTTP方法,auth-constraint要求是R1角色的成员。
@ServletSecurity(@HttpConstraint(rolesAllowed = "R1"))
public class Example4 extends HttpServlet {
}
CODE EXAMPLE 13-5 对于除GET和POST以外的所有HTTP方法,没有任何限制;对于GET和POST方法,需要加入R1角色的授权限制;对于POST,需要保密传输。
@ServletSecurity((httpMethodConstraints = {
@HttpMethodConstraint(value = "GET", rolesAllowed = "R1"),
@HttpMethodConstraint(value = "POST", rolesAllowed = "R1", transportGuarantee = TransportGuarantee.CONFIDENTIAL)
})
public class Example5 extends HttpServlet {
}
CODE EXAMPLE 13-6 对于所有的HTTP方法,除了GET auth-constraint要求加入R1角色;对于GET,没有任何约束。
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),httpMethodConstraints = @HttpMethodConstraint("GET"))
public class Example6 extends HttpServlet {
}
CODE EXAMPLE 13-7 对于除TRACE以外的所有HTTP方法,要求加入R1角色的auth-constraint;对于TRACE,拒绝所有访问。
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),httpMethodConstraints = @HttpMethodConstraint(value="TRACE",emptyRoleSemantic = EmptyRoleSemantic.DENY))
public class Example7 extends HttpServlet {
}
这一节描述了"@ServletSecurity"注解与它作为 "security-constraint "元素的等同表示的映射。它是为了方便使用容器现有的 “security-constraint"执行机制来执行。Servlet 容器对”@ServletSecurity"注解的执行必须与容器对本节中定义的映射所产生的 "security-constraint"元素的执行效果相当。
@ServletSecurity注解被用来定义一个独立于方法的@HttpConstraint,后面是一个零个或多个@HttpMethodConstraint规范的列表。这个独立于方法的约束被应用于所有没有定义HTTP方法特定约束的HTTP方法。
当没有包含@HttpMethodConstraint元素时,@ServletSecurity注解对应于一个包含web-resource-collection的单一security-constraint元素,该元素不包含http-method元素,因此与所有HTTP方法有关。
下面的例子描述了一个没有包含@HttpMethodConstraint注解的@ServletSecurity注解作为一个单一的security-constraint元素的表示。由相应的Servlet(注册)定义的url-pattern元素将包括在web-resource-collection中,任何包含的auth-constraint和user-data-constraint元素的存在和价值将由13.4.1.3节 "Mapping @HttpConstraint and @HttpMethodConstraint to XML."中定义的@HttpConstraint值来决定。
CODE EXAMPLE 13-8 mapping @ServletSecurity with no contained @HttpMethodConstraint
@ServletSecurity(@HttpConstraint(rolesAllowed = "Role1"))... Role1
当指定了一个或多个@HttpMethodConstraint元素时,独立于方法的约束对应于一个单一的security-constraint,包含一个web-resource-collection,其中包含http-method-omission,用于每个在@HttpMethodConstraint元素中命名的HTTP方法。如果独立于方法的约束返回所有的缺省值,并且至少有一个@HttpMethodConstraint不返回缺省值,那么必须不创建包含http-method-omission元素的security-constraint。每个@HttpMethodConstraint都对应于另一个security-constraint,它包含一个web-resource-collection,其中有一个http-method元素命名了相应的HTTP方法。
下面的例子描述了一个"@ServletSecurity"注解与一个包含的"@HttpMethodConstraint"到两个安全约束元素的映射。由相应的Servlet(注册)定义的url-pattern元素将包括在两个约束的web-resource-collection中,任何包含的auth-constraint和user-data-constraint元素的存在和价值将由相关的@HttpConstraint和@HttpMethodConstraint值的映射决定,如第13.4.1.3节 "Mapping @HttpConstraint and @HttpMethodConstraint to XML."中所定义的。
CODE EXAMPLE 13-9 将@ServletSecurity与包含的@HttpMethodConstraint进行映射
@ServletSecurity(value=@HttpConstraint(rolesAllowed = "Role1"),httpMethodConstraints = @HttpMethodConstraint(value = "TRACE",emptyRoleSemantic = EmptyRoleSemantic.DENY))
... TRACE Role1
... TRACE
本节描述了"@HttpConstraint"和"@HttpMethodConstraint"注解值(定义在"@ServletSecurity"中使用)与相应的 "auth-constraint"和 "user-data-constraint"表示的映射,这些注解共享一个相同的模型,用于表达在可移植部署描述符中使用的 "auth-constraint"和 "user-data-constraint "元素的等值。该模型由以下3个元素组成:
emptyRoleSemantic
授权语义,要么是 “PERMIT”,要么是 “DENY”,在 "rolesAllowed "中没有指定角色时适用。这个元素的默认值是PERMIT,DENY不支持与非空的rolesAllowed列表相结合。
rolesAllowed
一个包含授权角色名称的列表。当这个列表为空时,其含义取决于emptyRoleSemantic的值。当角色名称 "*"包含在允许的角色列表中时,没有特殊含义。当特殊的角色名称 "**"出现在rolesAllowed中时,它表示用户认证,与角色无关,是需要的,也是足够的。这个元素的默认值是一个空列表。
transportGuarantee
数据保护要求,要么是 “NONE”,要么是 “CONFIDENTIAL”,必须由请求到达的连接来满足。这个元素在意义上等同于一个用户-数据-约束',包含一个传输保证’的相应值。这个元素的默认值是NONE。
下面的例子描述了上面描述的@HttpConstraint模型与web.xml中auth-constraint和user-data-constraint元素之间的对应关系。
CODE EXAMPLE 13-10 emptyRoleSemantic=PERMIT, rolesAllowed={}, transportGuarantee=NONE
no constraints
CODE EXAMPLE 13-11 emptyRoleSemantic=PERMIT, rolesAllowed={}, transportGuarantee=CONFIDENTIAL
CONFIDENTIAL
CODE EXAMPLE 13-12 emptyRoleSemantic=PERMIT, rolesAllowed={Role1}, transportGuarantee=NONE
Role1
CODE EXAMPLE 13-13 emptyRoleSemantic=PERMIT, rolesAllowed={Role1}, transportGuarantee=CONFIDENTIAL
Role1
CONFIDENTIAL
CODE EXAMPLE 13-14 emptyRoleSemantic=DENY, rolesAllowed={}, transportGuarantee=NONE
CODE EXAMPLE 13-15 emptyRoleSemantic=DENY, rolesAllowed={}, transportGuarantee=CONFIDENTIAL
CONFIDENTIAL
setServletSecurity方法可以在ServletContextListener中使用,以定义应用于ServletRegistration的映射的安全约束。
Collection setServletSecurity(ServletSecurityElement arg);
setServletSecurity的javax.servlet.ServletSecurityElement参数在结构和模型上类似于@ServletSecurity注释的ServletSecurity接口。因此,第13.4.1.2节 “Mapping @ServletSecurity to security-constraint”(第13-133页)中定义的映射,类似于将含有 "HttpConstraintElement "和 "HttpMethodConstraintElement"值的 "ServletSecurityElement security-constraint 安全约束 "表示。
setServletSecurity方法返回(可能是空的)URL模式的集合,这些URL模式已经是可移植部署描述符中security-constraint元素的确切目标(因此未受调用影响)。
如果获取 ServletRegistration的 ServletContext已经被初始化,该方法会抛出一个 IllegalStateException。
当便携式部署描述符中的 "security-constraint "包括 “url-pattern”,与 "ServletRegistration"映射的模式完全匹配时,对 "ServletRegistration"的 "setServletSecurity"的调用必须对Servlet容器,对该模式实施的约束没有影响。
除了上面列出的例外情况,包括当Servlet类被注解为"@ServletSecurity"时,当 "setServletSecurity"在 "ServletRegistration"上被调用时,它建立了适用于注册的 "url-patterns"的安全限制。
安全角色是由应用程序开发人员或汇编人员定义的用户的逻辑分组。当应用程序被部署时,角色由部署者映射到运行时环境中的委托人或组。servlet 容器根据委托人的安全属性,为与传入请求相关的委托人执行声明性或程序性安全。
这可以通过以下两种方式发生。
web客户端可以使用以下机制之一来验证用户对网络服务器的身份。
基于用户名和密码的HTTP基本认证,是HTTP/1.0规范中定义的认证机制。web服务器请求web客户端对用户进行认证。作为请求的一部分,web服务器传递用户要被认证的领域(一个字符串)。web客户端从用户那里获得用户名和密码,并将它们传送给web服务器。然后web服务器在指定的领域内对用户进行认证。
基本认证不是一个安全的认证协议。用户密码是以简单的base64编码发送的,而且目标服务器没有被认证。额外的保护措施可以缓解其中的一些问题:在一些部署场景中应用了安全的传输机制(HTTPS),或网络层面的安全(如IPSEC协议或VPN策略)。
与HTTP基本认证一样,HTTP摘要认证也是根据用户名和密码来认证用户。然而,与HTTP基本认证不同,HTTP摘要认证并不通过网络发送用户密码。在HTTP摘要认证中,客户端发送密码(和额外数据)的单向加密散列。尽管密码不在线路上发送,但HTTP摘要认证要求认证容器可以获得明文密码等价物,以便它可以通过计算预期的摘要来验证收到的认证器。Servlet容器应该支持HTTP_DIGEST认证。
“login screen"的外观和感觉不能使用网络浏览器的内置认证机制来改变。本规范引入了一个必要的基于表单的认证机制,允许开发者控制登录屏幕的外观和感觉。
web应用程序部署描述符包含登录表格和错误页面的条目。登录表格必须包含用于输入用户名和密码的字段。这些字段必须分别命名为j_username和j_password。
当用户试图访问一个受保护的网络资源时,容器会检查用户的身份验证。如果用户通过了认证并拥有访问该资源的权限,所请求的网络资源就会被激活并返回对它的引用。如果用户没有被认证,就会发生以下所有的步骤。
在步骤7中到达的重定向请求的HTTP协议方法可能与触发认证的请求的HTTP方法不同。因此,在步骤6的重定向之后,即使请求到达的HTTP方法不需要认证,表单认证器也必须处理重定向的请求。为了提高被重定向请求的HTTP方法的可预测性,容器应该使用303(SC_SEE_OTHER)状态代码进行重定向(在步骤6中),除非需要与HTTP 1.0用户代理的互操作性;在这种情况下,应该使用302状态代码。
当通过不受保护的传输进行时,基于表单的认证会受到一些与基本认证相同的漏洞的影响。
当触发认证的请求通过安全传输到达时,或登录页面受CONFIDENTIAL的用户数据约束时,登录页面必须返回给用户,并通过安全传输提交给容器。
登录页面应该受到 CONFIDENTIAL 的用户数据约束,而且 CONFIDENTIAL 的用户数据约束应该包含在包含认证要求的每个安全约束中。
HttpServletRequest接口的登录方法为应用程序控制其登录屏幕的外观和感觉提供了一种替代方法。
基于表单的登录和基于URL的会话跟踪在实施上可能会有问题。基于表单的登录应该只在会话由cookies或SSL会话信息维护时使用。
为了使认证适当地进行,登录表单的动作必须始终是j_security_check。这个限制是为了使登录表单无论对哪种资源都能工作,并避免要求服务器指定出站表单的动作字段。登录表单应该在密码表单字段上指定autocomplete=”off”。
下面是一个例子,显示了该表单应该如何被编码到HTML页面中:
如果基于表单的登录是由于HTTP请求而被调用的,那么原始的请求参数必须被容器保留下来,以便在认证成功后将调用重定向到被请求的资源。
如果用户使用表单登录进行了认证并创建了一个 HTTP 会话,那么该会话的超时或无效会导致用户被注销,即后续请求必须导致用户被重新认证。注销的范围与认证的范围相同:例如,如果容器支持单点登录,如符合Java EE技术的Web容器,用户将需要在Web容器上托管的任何Web应用程序中重新进行认证。
使用HTTPS(HTTP over SSL)的终端用户认证是一种强认证机制。这种机制要求客户端拥有一个公钥证书(PKC)。目前,PKC在电子商务应用中很有用,也可用于浏览器内的单密码。
Servlet 容器应该提供公共接口,这些接口可用于集成和配置额外的 HTTP 消息层认证机制,以便由容器代表部署的应用程序使用。这些接口应该提供给容器供应商以外的其他各方使用(包括应用程序开发人员、系统管理员和系统集成商)。
为了促进额外的容器认证机制的可移植实施和集成,建议所有的Servlet容器都实施The Java Authentication SPI for Containers(即JSR 196)的Servlet容器配置文件。该SPI可在以下网站下载。http://www.jcp.org/en/jsr/detail?id=196。
由于在运行时环境中角色被映射到的基础安全身份(如用户和组)是特定于环境的,而不是特定于应用程序的,所以最好是这样。
因此,要求servlet容器在容器级别(而不是在Web应用级别)跟踪认证信息。这允许为一个网络应用程序认证的用户访问由容器管理的、允许相同安全身份的其他资源。
安全约束是一种定义网络内容保护的声明性方式。安全约束将授权和或用户数据约束与网络资源的HTTP操作联系起来。安全约束在部署描述符中表示为 “security-constraint”,由以下元素组成。
web-resource-collection in deployment descriptor)auth-constraint in deployment descriptor)user-data-constraint in deployment descriptor)安全约束所适用的HTTP操作和Web资源(即受约束的请求)由一个或多个Web资源集合确定。一个网络资源集合由以下元素组成。
url-pattern in deployment descriptor)http-method or http-method-omission elements in the deployment descriptor)一个授权约束建立了一个认证要求,并命名了允许执行受限请求的授权角色。一个用户必须是至少一个指定角色的成员,才能被允许执行受限制的请求。特殊角色名称*是部署描述符中定义的所有角色名称的简写。特殊角色名称**是对任何独立于角色的认证用户的缩写。当特殊角色名称 "**"出现在授权约束中时,它表示任何独立于角色的认证用户都被授权执行受约束的请求。一个没有角色名称的授权约束表示在任何情况下都不允许对受限请求的访问。一个授权约束由以下元素组成。
role name (role-name in deployment descriptor).
用户数据约束确立了一项要求,即受约束的请求必须通过受保护的传输层连接接收。所需保护的强度由传输保证的值来定义。INTEGRAL的传输保证被用来建立内容完整性的要求,CONFIDENTIAL的传输保证被用来建立保密性的要求。
传输保证 "NONE "表明,当在任何连接(包括不受保护的连接)上收到受限制的请求时,容器必须接受这些请求。容器可以针对INTEGRAL值施加保密的传输保证。一个用户数据约束由以下元素组成
transport guarantee (transport-guarantee in deployment descriptor)
如果没有授权约束适用于一个请求,那么容器必须接受该请求而不要求用户认证。如果没有用户数据约束适用于一个请求,那么当通过任何连接(包括不受保护的连接)收到请求时,容器必须接受该请求。
为了组合约束,当一个HTTP方法在一个 "web-resource-collection"中没有被命名,或者该集合在一个包含的 "http-method"元素中特别命名了该HTTP方法,或者该集合包含一个或多个 "http-method-omission"元素,而这些元素都没有命名该HTTP方法时,就可以说该HTTP方法出现在该集合内。
当一个url-pattern和HTTP方法对组合出现在多个安全约束中(即在一个web-resource-collection中),约束(对模式和方法)是通过组合单个约束来定义的。出现相同模式和方法的约束的组合规则如下。
将命名为角色或通过名称 "*"暗示角色的授权约束组合在一起,应产生各个约束中的角色名称的联合,作为允许的角色。命名为 "**"的授权约束应与命名或暗示角色的授权约束相结合,允许任何独立于角色的认证用户。一个不包含授权约束的安全约束应与命名或暗示角色的授权约束相结合,允许未经认证的访问。在特殊情况下,没有命名任何角色的授权约束应与任何其他约束相结合,以覆盖其影响并导致访问被排除。
适用于共同的 "url-pattern"和 "http-method"的 "user-data-constraints"的组合,应产生由各个约束接受的可接受的连接类型的联合。不包含 user-data-constraint的安全约束应与其他user-data-constraint相结合,使未受保护的连接类型成为可接受的连接类型。
下面的例子说明了约束条件的组合及其转化为适用约束条件的表格。假设一个部署描述符包含以下安全约束。
precluded methods /* /acme/wholesale/* /acme/retail/* GET POST
wholesale /acme/wholesale/* GET PUT SALESCLERK
wholesale 2 /acme/wholesale/* GET POST CONTRACTOR CONFIDENTIAL
retail /acme/retail/* GET POST CONTRACTOR HOMEOWNER
这个假想的部署描述符的翻译将产生表13-4中定义的约束。
TABLE 13-4 Security Constraint Table
| url-pattern | http-method | permitted roles | supported connection types |
|---|---|---|---|
| /* | all methods except GET, POST | 阻止访问 | 不限制 |
| /acme/wholesale/* | all methods except GET, POST | 阻止访问 | 不限制 |
| /acme/wholesale/* | GET | CONTRACTOR SALESCLERK | 不限制 |
| /acme/wholesale/* | POST | CONTRACTOR | CONFIDENTIAL 保密的 |
| /acme/retail/* | all methods except GET, POST | 阻止访问 | 不限制 |
| /acme/retail/* | GET | CONTRACTOR HOMEOWNER | 不限制 |
| /acme/retail/* | POST | CONTRACTOR HOMEOWNER | 不限制 |
当 Servlet 容器收到一个请求时,它应该使用第 121 页 "Use of URL Paths"中描述的算法来选择定义在与请求的 URI 最匹配的 url-pattern上的约束(如果有)。如果没有选择约束条件,那么容器应接受该请求。否则,容器应确定该请求的 HTTP 方法是否受到所选模式的约束。如果没有,该请求应被接受。否则,该请求必须满足适用于 "url-pattern "的 HTTP 方法的约束。以下两条规则必须得到满足,请求才会被接受并被分派到相关的Servlet。
403(SC_FORBIDDEN)状态代码。如果访问被限制在允许的角色,并且请求没有被认证,那么请求将被拒绝为未授权,并且返回401(SC_UNAUTHORIZED)状态码以引起认证。如果访问被限制在允许的角色,并且请求的认证身份不是这些角色的成员,那么请求将被拒绝为禁止,并向用户返回403(SC_FORBIDDEN)状态码。security-constraint 模式提供了列举(包括省略)HTTP协议方法的能力,security-constraint 中定义的保护要求适用于这些方法。当HTTP方法在 security-constraint 中被列举出来时,约束所定义的保护只适用于列举出的方法。我们把没有被列举出来的HTTP方法称为 "未覆盖的 "HTTP方法。未被覆盖的HTTP方法在所有请求的URL上都不被保护,因为这些URL的 url-pattern与 security-constraint是最佳匹配。
当HTTP方法没有被列举在 security-constraint 中时,约束所定义的保护措施适用于HTTP(扩展)方法的完整集合。在这种情况下,在所有请求的URL中没有未被覆盖的HTTP方法,对于这些URL,security-constraint的url-pattern是最佳匹配。
下面的例子描述了三种HTTP协议方法可能不被覆盖的方式。在所有适用于 url-pattern的约束条件被组合后,确定方法是否被覆盖,如13.8.1节 “Combining Constraints”(第13-143页)中所述。
一个 security-constraint 在 "http-method "元素中命名一个或多个HTTP方法。除了约束中指定的方法外,所有其他的HTTP方法都会被忽略。
wholesale /acme/wholesale/* GET SALESCLERK
All HTTP Methods except GET are uncovered.除了GET,所有的HTTP方法都没有被发现。
一个 security-constraint 在 "http-method-omission"元素中命名一个或多个HTTP方法。所有在约束中命名的HTTP方法都会被释放。
wholesale /acme/wholesale/* GET
GET是不包括的。所有其他方法都被排除在 "auth-contraint "的范围之外。
一个"@ServletSecurity"注解包括一个返回所有默认值的"@HttpConstraint",它还包括至少一个返回所有默认值以外的"@HttpMethodConstraint"。除了那些在@HTTPMethodConstraint中命名的HTTP方法外,所有的HTTP方法都会被注解所覆盖。这种情况类似于情况1,使用ServletRegistration接口的setServletSecurity方法也会产生类似的结果。
@ServletSecurity((httpMethodConstraints = {@HttpMethodConstraint(value = "GET", rolesAllowed = "R1"),@HttpMethodConstraint(value = "POST", rolesAllowed = "R1",transportGuarantee = TransportGuarantee.CONFIDENTIAL)
})public class Example5 extends HttpServlet {
}
除了GET和POST,所有的HTTP方法都没有被发现。
目标: 确保所有受限制的URL模式的所有HTTP方法都有预期的安全保护(也就是说,被覆盖)。
并声明(使用http-method>元素,或同等的注释)所有HTTP方法(有安全保护)将被允许在约束的URL模式。“元素或 HttpMethodConstraint注解来表示除”"或 "HttpMethodConstraint"命名的方法之外的所有HTTP方法集合。当使用注解时,使用HttpConstraint注解来定义应用于所有其他HTTP方法的安全语义,并配置EmptyRoleSemantic=DENY来导致所有其他HTTP方法被拒绝。在应用部署期间,容器必须告知部署者,在应用安全约束配置中存在的任何未覆盖的 HTTP 方法是由为该应用定义的约束的组合造成的。所提供的信息必须确定未覆盖的 HTTP 协议方法,以及 HTTP 方法被覆盖的相应 URL 模式。通知部署者的要求可以通过记录所需信息来满足。
当应用程序的 web.xml 中设置了 "deny-uncovered-http-methods "标志时,当任何 HTTP 协议方法与请求的 URL 一起使用时,容器必须拒绝该方法,因为该 HTTP 方法在适用于与请求 URL 最匹配的 url 模式的组合安全约束中被揭露。被拒绝的请求应被拒绝为禁止,并应返回403(SC_FORBIDDEN)状态代码。
为了使未覆盖的HTTP方法被拒绝,部署系统应该建立额外的排除授权约束,以覆盖这些HTTP方法在限制的url-pattern中,HTTP方法是未覆盖的。
当应用程序的安全配置不包含未覆盖的方法时,deny-uncovered-http-methods标志必须对应用程序的有效安全配置没有影响。
在安全配置包含未覆盖方法的应用程序中应用 deny-uncovered-http-methods标志,在某些情况下可能会拒绝对资源的访问,而这些资源必须是可访问的,以便应用程序能够运行。在这种情况下,应该完成应用程序的安全配置,使所有未覆盖的方法被适当的约束配置所覆盖。
应用程序开发人员应该定义安全约束配置,使HTTP方法不被覆盖,他们应该设置deny-uncovered-http-methods标志,以确保他们的应用程序不依赖于通过未覆盖的方法进行访问。
Servlet容器可以提供一个可配置的选项,选择未覆盖方法的默认行为是 ALLOW 还是 DENY。这个选项可以在每个应用的粒度或更大的粒度上进行配置。请注意,将这个默认值设置为DENY可能会导致一些应用程序失败。
默认情况下,访问资源是不需要认证的。当包含与请求URI最匹配的 url-pattern的安全约束(如果有的话)结合起来,对请求的HTTP方法施加 auth-constraint(命名角色)时,就需要认证。同样地,除非适用于请求的安全约束结合起来,对请求的HTTP方法施加一个user-data-constraint (有一个受保护的 transport-guarantee),否则不需要一个受保护的传输。
容器在将请求分派给servlet引擎之前建立了请求的调用者身份。在整个请求的处理过程中,或者在应用程序成功地调用请求的 authenticate、login或 logout之前,调用者的身份都不会改变。对于异步请求,在初始调度时建立的调用者身份保持不变,直到整个请求的处理完成,或者应用程序成功调用请求上的authenticate, login或logout。
在处理一个请求的过程中被登录到一个应用程序中,正好对应于有一个与请求相关的有效的非空的呼叫者身份,这可以通过调用请求中的getRemoteUser或getUserPrincipal确定。如果这些方法的返回值为空,则表明调用者在处理该请求时没有登录到应用程序中。
容器可以创建HTTP Session对象来跟踪登录状态。如果开发者在用户未被认证时创建了一个会话,而容器随后对用户进行了认证,那么在登录后对开发者代码可见的会话必须是在登录发生前创建的同一个会话对象,这样就不会有会话信息的丢失。