第13章 安全
应用开发人员创建Web应用,他把应用给、销售,或转让给部署人员安装到运行时环境中。应用开发人员与部署人员沟通部署系统以及安全需求。该信息可以通过应用部署描述符声明传达,或者在应用代码中使用注解来传达。
本质描述了Servlet容器安全机制、接口、部署描述符和基于注解机制传达应用安全需求。
13.1 介绍
web应用包含的资源可以被多个用户访问。这些资源常常在开放网络如Internet上不受保护的遍历。在这样的环境中,大量的web应用将有安全需求。
尽管质量保障和实现细节可能会有所不同,但servlet容器有满足这些需求的机制和基础设施,共用如下一些特性:
■ 身份认证:表示通信实体之间以代表特定身份彼此证明进行授权访问。
■ 资源访问控制:表示用户或程序与资源交互是受限制的,以达到强制完整性、保密性、或可用性约束的目的。
■ 数据完整性:表示用来证明信息在传输过程中没有被第三方修改。
■ 保密性或数据隐私:表示用来保证信息只对已授权访问的用户访问。
13.2 声明式安全
声明式安全是指以在应用外部的形式表达应用的安全模型需求,包括角色、访问控制和认证需求。部署描述符是web应用中声明式安全的主要手段。
部署人员映射应用的逻辑安全需求到特定于运行时环境代表性的安全策略。在运行时,servlet容器使用安全策略表示来实施认证和授权。
安全模型适用于web应用的静态内容部分和客户端请求到的应用内的servlet和过滤器。安全模型不适用于当servlet使用RequestDispatcher调用静态内容或使用forward或include到的servlet。
13.3 编程式安全
当单独使用声明式安全是不足以表达应用的安全模型时,编程式安全被用于安全意识的应用。编程式安全包括以下HttpServletRequest接口的方法:
■ authenticate
■ login
■ logout
■ getRemoteUser
■ isUserInRole
■ getUserPrincipal
login方法允许应用执行用户名和密码收集(作为一种Form-Based Login的替代)。authenticate方法允许一个应用由容器从不受约束的请求上下文内部让请求调用者认证。
logout方法提供用于应用重置来访者的请求身份。
getRemoteUser方法由容器返回与该请求相关的远程用户(即来访者)的名字。
isUserInRole方法确定与该请求相关的远程用户(即来访者)是否在一个特定的安全角色中。
getUserPrincipal方法确定远程用户(即来访者)的Principal名称并返回一个与远程用户对应的java.security.Principal对象。调用getUserPrincipal返回的Principal的getName方法返回远程用户的名字。这些API允许Servlet基于获得的信息做一些业务逻辑决策。
如果没有用户通过身份认证,getRemoteUser方法返回null,isUserInRole方法总返回false,getUserPrincipal方法返回null。
isUserInRole方法需要一个字符串用户的角色名称参数。应该在部署描述符中声明一个security-role-ref元素,其有一个role-name子元素,包含了传递到方法的角色名称。security-role-ref元素可能包含一个role-link子元素,其值是用户可能会被映射到的安全角色名称。当决定返回调用值时,容器使用security-role-ref映射到的security-role。
例如,映射安全角色引用“FOO”到role-name为"manager"的安全角色的语法是:
<security-role-ref>
<role-name>FOO</role-name>
<role-link>manager</role-link>
</security-role-ref>
在这种情况下,如果属于“manager”安全角色的用户调用了servlet,则调用isUserInRole("FOO")API的结果是true。
如果没有security-role-ref元素与一个已经声明的security-role元素匹配,容器默认必须参照web应用的security-role元素列表去检查role-name元素参数。isUserInRole方法参考该列表以确定调用者是否映射到一个安全角色。开发人员必须注意,使用默认机制可能限制了在应用中改变角色名称不必重新编译发起调用的servlet的灵活性。
13.4 编程式访问控制注解
本章定义的注解和API提供用于配置Servlet容器强制的安全约束。
13.4.1 @ServletSecurity注解
@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();
}
表 13-1 ServletSecurity接口
元素 | 描述 | 默认值 |
value | HttpConstraint 定义了应用到没有在httpMethodConstraints 返回的数组中表示的所有HTTP方法的保护。 | @HttpConstraint |
httpMethodConstraints | HTTP方法的特定限制数组 | {} |
@HttpConstraint
@HttpConstraint注解用在@ServletSecurity注解中表示应用到所有HTTP协议方法的安全约束,且HTTP协议方法对应的@HttpMethodConstraint没有出现在@ServletSecurity注解中。
package javax.servlet.annotation;
@Documented
@Retention(value=RUNTIME)
public @interface HttpConstraint {
ServletSecurity.EmptyRoleSemantic value();
java.lang.String[] rolesAllowed();
ServletSecurity.TransportGuarantee transportGuarantee();
}
表 13-2 HttpConstraint接口
元素 | 描述 | 默认值 |
value | (仅)当rolesAllowed返回一个空数组时应用的默认授权语义。 | PERMIT |
rolesAllowed | 包含授权角色的数组 | {} |
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();
}
表 13-3 HttpMethodConstraint接口
元素 | 描述 | 默认值 |
value | HTTP协议方法名 |
|
emptyRoleSemantic | (仅)当rolesAllowed返回一个空数组应用的默认授权语义。 | PERMIT |
rolesAllowed | 包含授权角色的数组 | {} |
transportGuarantee | 在连接的请求到达时必须满足的数据保护需求。 | NONE |
@ServletSecurity注解可以指定在(更准确地说,目标是) Servlet实现类上,且根据@Inherited元注解定义的规则,它的值是被子类继承的。至多只有一个@ServletSecurity注解实例可以出现在Servlet实现类上,且@ServletSecurity注解必须不指定在(更准确地说,目标是)Java方法上。
当一个或多个@HttpMethodConstraint注解定义在@ServletSecurity注解中时,每一个@HttpMethodConstraint定义的security-constraint,其应用到@HttpMethodConstraint中标识的HTTP协议方法。围绕的@ServletSecurity注解定义了应用到所有HTTP协议方法的security-constraint,除了那些定义在@ServletSecurity中的@HttpMethodConstraint相关的HTTP协议方法。
定义在便携式部署描述符中的security-constraint元素用于对所有出现在该约束中的url-pattern授权。
当在便携式部署描述符中的一个security-constraint包含一个url-pattern,其精确匹配一个使用@ServletSecurity注解的模式映射到的类,该注解必须不影响Servlet容器在该模式上实施的强制约束。
当为便携式部署描述符定义了metadata-complete=true时,@ServletSecurity注解不会应用到部署描述符中的任何url-pattern映射到(任何servlet映射到)的注解类。
@ServletSecurity注解不应用到ServletRegistration使用ServletContext接口的addServlet(String, Servlet)方法创建的url-pattern,除非该Servlet是由ServletContext接口的createServlet方法构建的。
除了上面列出的,当一个Servlet类使用了@ServletSecurity注解,该注解定义的安全约束应用到所有url-pattern映射到的所有Servlet映射到的类。
当一个类没有加@ServletSecurity注解时,应用到从那个类映射到的Servlet的访问策略是由合适的security-constraint元素确定的,如果有,在相关的便携式部署描述符中,或者由约束禁止任何这样的标签,则如果有,通过ServletRegistration接口的setServletSecurity方法编程式为目标servlet确定。
13.4.1.1示例
以下示例演示了ServletSecurity注解的用法。
代码示例13-1 用于所有HTTP方法,且无约束
@ServletSecurity
public class Example1 extends HttpServlet {
}
代码示例13-2 用于所有HTTP方法,无认证约束,需要加密传输
@ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
public class Example2 extends HttpServlet {
}
代码示例13-3 用于所有HTTP方法,拒绝所有访问
@ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
public class Example3 extends HttpServlet {
}
代码示例13-4 用于所有HTTP方法,认证约束需要成员身份在角色“R1”中
@ServletSecurity(@HttpConstraint(rolesAllowed = "R1"))
public class Example4 extends HttpServlet {
}
代码示例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 {
}
代码示例 13-6 用于除了GET之外的所有HTTP方法,认证约束需要成员身份在“R1”角色中;对于GET,无约束
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),
httpMethodConstraints = @HttpMethodConstraint("GET"))
public class Example6 extends HttpServlet {
}
代码示例 13-7 用于除了TRACE之外的所有HTTP方法,认证约束需要成员身份在“R1”角色中;对于TRACE,拒绝所有访问
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"),
httpMethodConstraints = @HttpMethodConstraint(value="TRACE",
emptyRoleSemantic = EmptyRoleSemantic.DENY))
public class Example7 extends HttpServlet {
}
13.4.1.2映射@ServletSecurity为security-constraint
本节将介绍@ServletSecurity注解映射为它等价表示,security-constraint元素。这提供了使用已存在容器的security-constraint实施机制来简化实施。由Servlet容器实施的@ServletSecurity注解必须在实施的效果上是等价的,由容器从在本节中定义的映射产生security-constraint元素。
@ServletSecurity注解用于定义一个方法无关的@HttpConstraint,且紧跟着一个包含零个或多个@HttpMethodConstraint规格的列表。方法无关的约束应用到那些没有定义HTTP特定方法约束的所有HTTP方法。
当没有包含@HttpMethodConstraint元素,@ServletSecurity注解相当于包含一个web-resource-collection的单个security-constraint元素,且web-resource-collection不包含http-method元素,因此涉及到所有HTTP方法。
下面的例子展示了把一个不包含@HttpMethodConstraint注解的@ServletSecurity注解表示为单个security-constraint元素。相关的servlet(registration)定义的url-pattern元素将被包含在web-resource-collection中, 任何包含的auth-constraint 和 user-data-constraint元素的存在和值,将由定义在13-132页中13.4.1.3节的“映射@HttpConstraint 和 @HttpMethodConstraint为XML”的映射的@HttpConstraint的值确定。
代码示例 13-8 不包含@HttpMethodConstraint的映射@ServletSecurity
@ServletSecurity(@HttpConstraint(rolesAllowed = "Role1"))
<security-constraint>
<web-resource-collection>
<url-pattern>...</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>Role1</role-name>
</auth-constraint>
</security-constraint>
当指定了一个或多个@HttpMethodConstraint元素,方法无关的约束相当于包含一个web-resource-collection的单个security-constraint元素,且web-resource-collection包含的http-method-omission元素为每一个HTTP方法指定了方法特定的约束。每一个@HttpMethodConstraint相当于另一种包含一个web-resource-collection 的security-constraint,且web-resource-collection包含一个使用相应HTTP方法命名的http-method元素。下面的例子展示了映射带有单个@HttpMethodConstraint的@ServletSecurity注解为两种security-constraint元素。相应的Servlet(registration)定义的url-pattern元素将被包含在两种约束的web-resource-collection中,且任何包含的auth-constraint 和user-data-constraint元素的存在和值,将由定义在13-132页中13.4.1.3节的“映射@HttpConstraint和@HttpMethodConstraint为XML”的映射关联的@HttpConstraint和@HttpMethodConstraint的值确定。
代码示例 13-9 映射包含@HttpMethodConstraint的@ServletSecurity
@ServletSecurity(value=@HttpConstraint(rolesAllowed = "Role1"),
httpMethodConstraints = @HttpMethodConstraint(value = "TRACE",
emptyRoleSemantic = EmptyRoleSemantic.DENY))
<security-constraint>
<web-resource-collection>
<url-pattern>...</url-pattern>
<http-method-omission>TRACE</http-method-omission>
</web-resource-collection>
<auth-constraint>
<role-name>Role1</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>...</url-pattern>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
13.4.1.3映射@HttpConstraint和@HttpMethodConstraint为XML
本节将介绍映射@HttpConstraint和@HttpMethodConstraint注解值(在@ServletSecurity中定义使用的)为它们等价的auth-constraint和user-data-constraint表示,这些注解共用一个通用模型用于表示用在便携式部署描述符中的auth-constraint 和user-data-constraint元素的等价形式。该模型包括以下3种元素:
■ emptyRoleSemantic
授权语义,PERMIT或DENY,适用于在rolesAllowed中没有指定的角色时。此元素的默认值为PERMIT,且DENY不支持与非空的rolesAllowed列表结合使用。
■ rolesAllowed
一个包含授权角色的名字列表。当该列表为空时,其含义取决于emptyRoleSemantic的值。当角色名字“*”包含在允许的角色列表中时是没有特别的含义。该元素的默认值是一个空列表。
■ transportGuarantee
数据保护需求,NONE或CONFIDENTIAL,在连接的请求到达时必须满足。该元素与一个包含一个使用相应值的transport-guarantee的user-data-constraint是等价的。该元素的默认值是NONE。
下面的例子展示了上述的@HttpConstraint模型和web.xml中的auth-constraint和user-data-constraint元素之间的对应关系。
代码示例 13-10 emptyRoleSemantic=PERMIT, rolesAllowed={}, transportGuarantee=NONE
无约束
代码示例 13-11 emptyRoleSemantic=PERMIT,rolesAllowed={}, transportGuarantee=CONFIDENTIAL
<<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
代码示例 13-12 emptyRoleSemantic=PERMIT, rolesAllowed={Role1}, transportGuarantee=NONE
<auth-constraint>
<security-role-name>Role1</security-role-name>
</auth-constraint>
代码示例 13-13 emptyRoleSemantic=PERMIT,rolesAllowed={Role1}, transportGuarantee=CONFIDENTIAL
<auth-constraint>
<security-role-name>Role1</security-role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
代码示例 13-14 emptyRoleSemantic=DENY,rolesAllowed={}, transportGuarantee=NONE
<auth-constraint/>
代码示例 13-15 emptyRoleSemantic=DENY,rolesAllowed={}, transportGuarantee=CONFIDENTIAL
<auth-constraint/>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
13.4.2 ServletRegistration.Dynamic的setServletSecurity
可在ServletContextListener 中使用setServletSecurity方法来定义应用到ServletRegistration定义的映射的安全约束。
Collection<String>setServletSecurity(ServletSecurityElement arg);
setServletSecurity的javax.servlet.ServletSecurityElement参数与ServletSecurity接口的@ServletSecurity注解在结构和模型上是类似的。因此,定义在13-130页13.4.1.2节的“映射@ServletSecurity为security-constraint”的映射,应用类似的包含HttpConstraintElement和HttpMethodConstraintElement值的ServletSecurityElement映射为其等价的security-constraint表示。
setServletSecurity方法返回一组URL pattern(可能空),其已是便携式部署描述符中的security-constraint元素的精确目标(因此,调用是不影响的)。
如果ServletContext中得到的ServletRegistration已经被初始化了,该方法抛出IllegalStateException。
当便携式部署描述符中的security-constraint包含一个url-pattern其精确匹配ServletRegistration映射的pattern,调用ServletRegistration的setServletSecurity必须对Servlet容器对pattern实施的约束没有任何影响。
除了上面列出的,包括当Servlet类注解了@ServletSecurity,当调用了ServletRegistration的setServletSecurity,它制定应用到registration的url-pattern的安全约束。
13.5 角色
安全角色是由应用开发人员或装配人员定义的逻辑用户分组。当部署了应用,由部署人员映射角色到运行时环境的principal或组。
Servlet容器根据principal的安全属性为与进入请求相关的principal实施声明式或编程式安全。这可能以如下任一方式发生:
1. 部署人员已经映射一个安全角色到运行环境中的一个用户组。调用的principal 所属的用户组取自其安全属性。仅当principal 所属的用户组已经由部署人员映射了安全角色,principal是在安全角色中。
2. 部署人员已经映射安全角色到安全策略域中的principal 名字。在这种情况下,调用的principal的名字取自其安全属性。仅当principal 名字与安全角色已映射到的principal 名字一样时,principal 是在安全角色中。
13.6 认证
web客户端可以使用以下机制之一向web服务器认证用户身份:
■ HTTP基本认证(HTTP Basic Authentication)
■ HTTP摘要认证(HTTP Digest Authentication)
■ HTTPS客户端认证(HTTPS Client Authentication)
■ 基于表单的认证(Form Based Authentication)
13.6.1 HTTP基本认证
HTTP基本认证基于用户名和密码,是HTTP/1.0规范中定义的认证机制。Web服务器请求web客户端认证用户。作为请求的一部分,web服务器传递realm(一个字符串)给要被认证的用户。Web客户端获取用户的用户名和密码并传给web服务器。Web服务器然后在指定的realm认证用户。
基本认证是不安全的认证协议。用户密码以简单的base64编码发送,且未认证目标服务器。额外的保护可以减少一些担忧:安全传输机制(HTTPS),或者网络层安全(如IPSEC协议或VPN策略)被应用到一些部署场景。
13.6.2 HTTP摘要认证
与HTTP基本认证类似,HTTP摘要认证也是基于用户名和密码认证用户,但不像HTTP基本认证,HTTP摘要认证不在网络上发送用户密码。在HTTP摘要认证中,客户端发送单向散列的密码(和额外的数据)。尽管密码不在线路上发送,HTTP摘要认证需要对认证容器可用的明文密码等价物(密码等价物可以是这样的,它们仅能在一个特定的realm用来认证用户),以致容器可以通过计算预期的摘要验证接收到的认证者。Servlet容器应支持HTTP_DIGEST身份认证。
13.6.3基于表单的认证
“登录界面”的外观在使用web浏览器的内置的认证机制时不能被改变。本规范引入了所需的基于表单的认证机制,允许开发人员控制登录界面的外观。
Web应用部署描述符包含登录表单和错误页面条目。登录界面必须包含用于输入用户名和密码的字段。这些字段必须分别命名为j_username和j_password。
当用户试图访问一个受保护的web资源,容器坚持用户的认证。如果用户已经通过认证则具有访问资源的权限,请求的web资源被激活并返回一个引用。如果用户未被认证,发生所有如下步骤:
1. 与安全约束关联的登录界面被发送到客户端,且由容器存储触发认证的URL路径。
2. 用户被要求填写表单,包括用户名和密码字段。
3. 客户端post表单到服务器。
4. 容器尝试使用来自表单的信息认证用户。
5. 如果认证失败,使用forward或redirect返回错误页面,且响应状态码设置为200。
6. 如果认证成功,检查被验证用户的principal,看其是否是以一个授权的角色访问资源。
7. 如果用户被授权,客户端使用存储的URL路径重定向回资源。
如果用户没有被授权,包含失败信息的错误页面发送给用户。
基于表单的认证与基本认证具有同样的缺点,因为用户密码以纯文本传输且未认证目标服务器。同样,额外的保护可以减少一些担忧:安全传输机制(HTTPS),或者网络层安全(如IPSEC协议或VPN策略)被应用到一些部署场景。
HttpServletRequest接口的login方法提供另一种用于应用控制它的登录界面外观的手段。
13.6.3.1登录表单
基于表单的登录和基于URL的session跟踪可以通过编程实现。基于表单的登录应该仅被用在当session由cookie或SSL session信息维护时。
为了进行适当的认证,登录表单的action 总是j_security_check。该限制使得不管请求什么资源,登录表单都能工作,且避免了要求服务器指定输出表单的action字段。
下面的示例展示了如何把表单编码到HTML页中:
<form method=”POST” action=”j_security_check”>
<input type=”text” name=”j_username”>
<input type=”password” name=”j_password”>
</form>
如果因为HTTP请求造成基于表单的登录被调用,容器必须保存原始请求参数,在成功认证时使用,它重定向调用所请求的资源。
如果用户已使用表单登录通过认证,且已经创建一个HTTP session,该session的超时或失效将导致用户被注销,在这种情况下,随后的请求必须导致用户重新认证。注销与认证具有相同的作用域:例如,如果容器支持单点登录,如兼容JavaEE技术的web容器,用户只需要与托管在web容器中的任何一个web应用重新认证即可。
13.6.4 HTTPS客户端认证
使用HTTPS(HTTP over SSL)认证最终用户是一种健壮的认证机制。该机制需要客户端拥有PublicKey Certificate(PKC)。目前,PKCs在电子商务应用中是很有用的,也对浏览器中的单点登录很有用。
13.6.5 其他容器认证机制
Servlet容器应该提供可用于集成和配置其他的HTTP消息层的认证机制的公共接口给代表已部署应用的容器使用。这些接口应该提供给参与者使用而不是容器供应商(包括应用开发人员、系统管理人员和系统集成人员)。
为了便于实现和集成其他容器认证机制,建议为所有Servlet容器实现Servlet容器Profile的Javatm认证SPI(即,JSR 196)。SPI可下载地址:http://www.jcp.org/en/jsr/detail?id=196
13.7 服务器跟踪认证信息
下面的安全标识(如用户和组)在运行时环境中映射的角色是环境指定的而非应用指定的,理想的是:
1. 使登录机制和策略是web应用部署到的环境属性。
2. 在同一个容器部署的所有应用能使用相同的认证信息来表示principal,且
3. 需要重新认证用户仅当已经越过了安全策略域边界。
因此,servlet容器需要在容器级别(而不是在web应用级别)跟踪认证信息。这允许在一个web应用已经通过认证的用户可以访问容器管理的以同样的安全标识许可的其他资源。
13.8 指定安全约束
安全约束是一种定义web内容保护的声明式方式。安全约束关联授权和或在web资源上对HTTP操作的用户数据约束。安全约束,在部署描述符中由security-constraint表示,其包含以下元素:
■ web资源集合(部署描述符中的web-resource-collection)
■ 授权约束 (部署描述符中的auth-constraint)
■ 用户数据约束 (部署描述符中的user-data-constraint)
HTTP操作和应用安全约束(取出受限的请求)的web资源由一个或多个web资源集合标识。Web资源集合包含以下元素:
■ URL 模式 (部署描述符中的url-pattern)
■ HTTP methods (部署描述符中的http-method或http-method-omission元素)
授权约束规定认证和命名执行受约束请求的被许可的授权角色的要求。用户必须至少是许可执行受约束请求的命名角色中的一个成员。特殊角色名“*”是定义在部署描述符中的所有角色名的一种简写。没有指定角色的授权约束表示在任何情况下都不允许访问受约束请求。授权约束包含以下元素:
■ role name (部署描述符中的role-name)
用户数据约束规定了在受保护的传输层连接之上接收受约束的请求的要求。需要保护的强度由传输保障的值定义。INTEGRAL类型的传输保障用于规定内容完整性要求,而传输保障CONFIDENTIAL用于规定保密性要求。传输保障“NONE”表示当容器通过任何包括不受保护的连接接受到请求时,必须接受此受约束的请求。用户数据约束包括如下元素:
■ transport guarantee (部署描述符中的transport-guarantee)
如果没有授权约束应用到请求,容器必须接受请求,而不要求用户身份认证。如果没有用户数据约束应用到请求,当容器通过任何包括不受保护的连接接收到请求时,必须接受此请求。
13.8.1 组合约束
为了组合约束,HTTP方法可以说是存在于web-resource-collection中,仅当没有在集合中指定HTTP方法,或者集合在包含的http-method元素中具体指定了HTTP方法,或者集合包含一个或多个http-method-omission元素,但那些没有指定的HTTP方法。
当url-pattern和HTTP方法以组合方式(即,在web-resource-collection中)出现在多个安全约束中,该约束(在模式和方法上的)是通过合并单个约束定义的。以相同的模式和方法出现的组合约束规则如下所示:
授权约束组合,其明确指定角色或通过“*” 隐式指定角色,可产生单个约束的合并的角色名称作为许可的角色。不包含授权约束的安全约束将与明确指定角色的或隐式指定角色的允许未授权访问的安全约束合并。授权约束的一个特殊情况是其没有指定角色,将与任何其他约束合并并覆盖它们的作用,这导致访问被阻止。
应用到常见的url-pattern和http-method的user-data-constraint组合,可产生合并的单个约束接受的连接类型作为接受的连接类型。不包含user-data-constraint的安全约束,将与其他user-data-constraint合并,使不安全的连接类型是可接受的连接类型。
13.8.2 示例
下面的示例演示了组合约束及它们翻译到的可应用的约束表格。假设部署描述符包含如下安全约束。
<security-constraint>
<web-resource-collection>
<web-resource-name>precluded methods</web-resource-name>
<url-pattern>/*</url-pattern>
<url-pattern>/acme/wholesale/*</url-pattern>
<url-pattern>/acme/retail/*</url-pattern>
<http-method-omission>GET</http-method-omission>
<http-method-omission>POST</http-method-omission>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>wholesale</web-resource-name>
<url-pattern>/acme/wholesale/*</url-pattern>
<http-method>GET</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>SALESCLERK</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>wholesale 2</web-resource-name>
<url-pattern>/acme/wholesale/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>CONTRACTOR</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>retail</web-resource-name>
<url-pattern>/acme/retail/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>CONTRACTOR</role-name>
<role-name>HOMEOWNER</role-name>
</auth-constraint>
</security-constraint>
翻译这个假定的部署描述符将产生定义在表13-4中的约束。
表 13-4 安全约束表
url-pattern | http-method | 许可的角色 | 支持的连接类型 |
/* | 所有方法 除GET,POST | 阻止访问 | 不限制 |
/acme/wholesale/* | 所有方法 除GET,POST | 阻止访问 | 不限制 |
/acme/wholesale/* | GET | CONTRACTOR SALESCLERK | 不限制 |
/acme/wholesale/* | POST | CONTRACTOR | CONFIDENTIAL |
/acme/retail/* | 所有方法 除GET,POST | 阻止访问 | 不限制 |
/acme/retail/* | GET | CONTRACTOR HOMEOWNER | 不限制 |
/acme/retail/* | POST | CONTRACTOR HOMEOWNER | 不限制 |
13.8.3 处理请求
当servlet容器接收到一个请求,它将使用119页“使用URL路径”描述的规则来选择在请求URI最佳匹配的url-pattern上定义的约束(如果有)。如果没有约束被选择,容器将接受该请求。否则,容器将确定在选择的模式上是否此请求的HTTP方法是受约束的。如果不是,请求将被接受。否则,请求必须满足在url-pattern应用到HTTP方法的约束。请求被接受和分派到相关的servlet,必须满足以下两个规则。
1. 接收到的请求的连接特性必须满足至少一种由约束定义的支持的连接类型。如果该规则不满足,容器将拒绝该请求并重定向到HTTPS端口。(作为一种优化,容器将以拒绝该请求为forbidden 并返回403 (SC_FORBIDDEN)状态码,如果知道该访问最终将被阻止 (通过没有指定角色的授权约束))
2. 请求的认证特性必须满足任何由约束定义的认证和角色要求。如果该规则不能满足是因为访问已经被阻止(通过没有指定角色的授权约束),则请求将被拒绝为forbidden 并返回403 (SC_FORBIDDEN)状态码给用户。如果访问是受限于许可的角色且请求还没有被认证,则请求将被拒绝为unauthorized 且401(SC_UNAUTHORIZED)状态码将被返回以导致身份认证。如果访问是受限于许可的角色且请求的认证身份不是这些角色中的成员,则请求将被拒绝为forbidden 且403状态码(SC_FORBIDDEN)将被返回到用户。
13.9 默认策略
默认情况下,身份认证并不需要访问资源。当安全约束(如果有)包含的url-pattern是请求URI的最佳匹配,且结合了施加在请求的HTTP方法上的auth-constraint(指定的角色),则身份认证是需要的。同样,一个受保护的传输是不需要的,除非应用到请求的安全约束结合了施加在请求的HTTP方法上的user-data-constraint(有一个受保护的transport-guarantee)。
13.10 登录和退出
容器在分派请求到servlet引擎之前建立调用者身份。在整个请求处理过程中或直到应用成功的在请求上调用authenticate、login或logout方法,调用者身份保持不变。对于异步请求,调用者身份建立在初始分派时,直到整个请求处理完成或直到应用成功的在请求上调用authenticate、login或logout方法,调用者身份保持不变。
在处理请求时登录到一个应用,精确地对应有一个有效的非空的与请求关联的调用者身份,可以通过调用请求的getRemoteUser或getUserPrincipal确定。这些方法的任何一个返回null值表示调用者没有登录到处理请求的应用。
容器可以创建HTTP Session对象用于跟踪登录状态。如果开发人员创建一个session而用户没有进行身份认证,然后容器认证用户,登录后,对开发人员代码可见的session必须是相同的session对象,该session是登录发生之前创建的,以便不丢失session信息。