天天看点

Servlet的安全(第十三篇)

web应用程序是由应用程序开发人员创建的,他们将应用提供,或出售或其他方式转移和部署,以安装到允许陈环境中,应用程序开发人员将安全要求传达给部署人员和部署系统,可以通过应用程序的部署描述符,使用应用程序代码内的声明方式传达此信息,或者通过ServletRegistration接口的setServletSecurity方法以编程方式传达此信息。

本章描述Servlet容器安全性机制和接口,以及用于传达应用程序安全性要求的部署描述符,注解和编程机制

文章目录

    • 13.1、简介
    • 13.2、声明式安全
    • 13.3、程序式安全
    • 13.4、程序安全策略配置
      • 13.4.1、@ServletSecurity 注解
        • 13.4.1.1、例子
        • 13.4.1.2、映射@ServletSecurity到security-constraint
        • 13.4.1.3、@HttpConstraint和@HttpMethodConstraint到XML
        • 13.4.2、通过setServletSecurity实现的ServletRegistation动态注册
    • 13.5、角色
    • 13.6、认证方式
      • 13.6.1、HTTP基础验证
      • 13.6.2、HTTP 摘要验证
      • 13.6.3、基于表单的身份验证
        • 13.6.3.1、登录表单注意事项
      • 13.6.4、HTTPS客户端验证
      • 13.6.5、其他容器身份验证机制
    • 13.7、服务跟踪身份验证信息
    • 13.8、指定的安全约束
      • 13.8.1、合并约束
      • 13.8.2、例子
      • 13.8.3、处理请求
      • 13.8.4、未覆盖HTTP协议的方法
      • 13.8.4.1、安全限制的配置规则
        • 13.8.4.2、处理未覆盖方法
    • 13.9、默认规则
    • 13.10、登录和登出

13.1、简介

  • Web应用程序包含许多用户可以访问的资源。这些资源通常遍历不受保护的开发网络,例如Internet,在这样的环境中,大量的web应用程序将具有安全性要求。尽管安全等级要求和实现细节可能有所不同,但是servlet容器具有满足这些要求的机制和基础结构,这些机制和基础结构具有以下一些特征:
    • 身份验证: 一种通信实体相互证明它们正在代表已授权访问的特定身份进行操作的方式
    • 资源的访问控制:为了加强完整性,机密性或可用性约束,与资源进行交互的方式仅限于用户或程序的集合
    • 数据完整性:用于证明信息在传输过程中未被第三方修改的手段
    • 机密性或数据隐私:用于确保信息仅提供给有权限用户进行访问。

13.2、声明式安全

  • 声明式安全性是指以应用程序外部的形式表示应用程序的安全模型或要求(包含角色,访问控制和身份验证要求)的方法,部署描述符是web应用程序中声明式安全性的主要手段
  • 部署程序将应用程序的逻辑安全要求映射到要求映射到特定于运行时环境的安全策略的表示形式,在运行时,Servlet容器使用安全策略表示来强制执行身份验证和授权。
  • 安全模型适用于web应用程序的静态内容部分,也适用于客户端请求的应用程序中的servlet和过滤器,当servlet使用RequestDispatcher通过转发或包含来调用静态资源或servlet时,安全模型不适用的。

13.3、程序式安全

  • 当仅声明性安全不足以表示应用程序的安全模型时,安全性感知应用程序将使用程序安全性,程序安全性由HttpServletRequest接口的以下方法组成
    • authenticate(验证)
    • login(登录)
    • logout(注销)
    • getRemoteUser (获取远程用户)
    • isUserInRole ( 用户是否具有这个角色)
    • getUserPrincipal ( 获取用户主体信息)
  • login 登录方法允许应用程序要求输入用户名和密码(作为基于表单的登录的替代方法)
  • authenticate 身份验证方法允许应用程序在不受到约束的请求上下文情况下,由容器发起对请求调用方的身份验证。
  • logout 注销方法,以允许应用程序重置请求的呼叫者身份。
  • getRemoteUser方法返回容器与请求相关联的远程用户(即调用者)是否处于指定的安全角色
  • getUserPrincipal方法确定远程用户(即调用方)的主体名称,并返回与该远程用户相对应的java.security.Pricipal对象,在getUserPrincipal返回Principal上调用getName方法将返回远程用户的名称,这些API允许Servlet根据获得的信息做出业务逻辑决策。
  • 如果没有用户通过身份验证,getRemoteUser方法将返回null, isUserInRole方法始终返回false,而getUserPrincipal方法将返回null。
  • isUserInRole方法采用String参数引用应用程序角色。对于在调用isUserInRole中使用的每个不同的角色引用。应在部署描述符中声明一个具体角色名称对应于该角色引用的security-role-ref元素,每个security-role-ref应该包含一个role-link子元素,其值是应用程序嵌入式角色引用所链接到的应用程序安全角色的名称。容器使用角色名称等于角色引用的security-role-ref来确定要测试用于的成员身份的安全性角色
  • 例子:将角色FOO映射到manager角色,语法如下
    • <security-role-ref>
      	<role-name>FOO</role-name>
        <role-link>manager</role-link>
      </security-role-ref>
                 
  • 在这种情况下,如果属于“管理者”安全角色的用户调用的servlet调用isUserInRole(“Foo”),则结果为true。
  • 如果在对isUserInRole的调用中使用的角色引用不存在匹配的security-role-ref,则容器必须默认在角色名称等于在调用中使用的角色的安全角色中测试用户的成员资格
  • 角色名称”*“绝对不能作为调用isUserInRole的参数,使用"*“对isUserInRole的任何调用都必须返回false,如果测试的安全角色的角色名称为”**“, 并且应用程序尚未声明角色名称为”**"的应用程序安全角色,则isUserInRole必须仅返回true
  • 如果用户已通过身份验证,也就是说,仅当getRemoteUser和getUserPrincipal都将返回非null值时,否则,容器必须检查用户是否具有应用程序角色的成员身份。
  • security-role-ref 元素的声明将应用程序使用的角色必须为其定义映射的信息

13.4、程序安全策略配置

  • 本部分定义提供的注解和API,以配置Servlet容器强制实施的安全性约束

13.4.1、@ServletSecurity 注解

  • @ServletSecurity注解提供了一种替代机制,用于定义与访问约束相同的访问控制约束,否则,这些约束可以通过可移植部署描述符中的安全约束元素以声明方法表示,或者通过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();
    }
               
    • ServletSecurity 接口参数解释
    • 元素 描述 默认值
      value 定义一个HttpConstraint约束对象应用所有Http方法,但是不包括定义在httpMethodConstraints的方法数组 @HttpConstraint
      httpMethodConstraints 具体某些HTTP方法定义具体约束 {}
  • @HttpConstraint 注解
    • @ServletSecurity注解中使用@HttpConstraint注解,应用安全约束所有HTTP协议方法,但是不包括在@HttpMethodConstraint定义的方法
    • 对于特殊情况,如果一个@HttpConstraint返回所有默认值,并且至少有一个@HttpMethodConstraint返回所有默认值,则@HttpConstraint对于任何HTTP协议方法都是没有安全性约束的,设置此异常是为了确保@HttpConstraint的此类潜在非特定用途不会产生约束,而这些约束将显式建立此类方法的不受保护的访问,假设它们不会被约束所覆盖。
    • package javax.servlet.annotation;
      @Documented
      @Retention(value=RUNTIME)
      public @interface HttpConstraint{
        ServletSecurity.EmptyRoleSemantic value() default EmptyRoleSemantic.PERMIT;
        java.lang.String[] rolesAllowed();
        ServletSecurity.TransportGuarantee transportGuarantee();
      }
                 
    • HttpConstraint 接口 属性
      • 元素 描述 默认值
        value 角色授权时适用的默认授权语义,仅当返回一个空数组 PERMIT (允许)
        rolesAllowed 包含授权角色名称的数组 {}
        transportGuarantee 必须在请求达到的位置进行数据保护 NONE
  • @HttpMethodConstraint
    • @ServletSecurity注解中使用@HttpMethodConstraint注解来表示特定HTTP协议消息的安全约束
    • package javax.servlet.annotation;
      @Documented
      @Retention(value=RUNTIME)
      public @interface HttpMethodConstraint{
        String value();
        EmptyRoleSemantic emptyRoleSemantic() default EmptyRoleSemantic.PERMIT;
        java.lang.String[] rolesAllowed();
        ServletSecurity.TransportGuarantee transportGurantee();
      }
                 
    • HttpMethodConstraint接口
      • 元素 描述 默认
        value HTTP协议方法名称
        emptyRoleSemantic 角色授权时适用的默认授权语义,仅当返回一个空数组 PERMIT(允许)
        rolesAllowed 包含授权角色名称的数组 {}
        transportGuarantee 必须在请求达到的位置进行数据保护 NONE
        @ServletSecurity注解可以在Servlet实现类上指定,其子类根据为@Inherited元注解定义的规则继承,@ServletSecurity注解的Servlet实现类最多只有一个实例,并且@ServletSecurity注解一定不能在Java方法上指定。
      • 在@ServletSecurity注解中定义一个或多个@HttpMethodConstraint注解时,每个@HttpMethodConstraint都定义适用于@HttpMethodConstraint中标识的HTTP协议方法的安全性约束。
      • 便携式部署描述符中定义的security-constraint元素对应所有url-patterns检验的权威性
      • 当可移植部署描述符中的security-constraint 包含一个url-pattern,该url-pattern完全映射到某个@ServletSecurity上,该注解必须对Servlet容器对该模式实施的约束没有任何影响。
      • @ServletSecurity注解不适用于使用ServletContext接口的addServlet(String, Servlet)方法创建的ServletRegistration的url模式,除非Servlet是通过ServletContext接口的createServlet方法构建的
      • 当为可移植的部署描述符定义了metadata-complete=true时,@ServletSecurity注解不适用与映射到部署描述符中带注解的类的任何url-patterns(映射到任何servlet)
      • 除了上面列出的例外,当使用@ServletSecurity注解Servlet类时,注解定义了安全约束,这些约束适用于所有映射到该类的所有Servlet的url模式。
      • 当未使用@ServletSecurity注解对类进行注解时,将通过相应的可移植部署描述符中的适用安全约束元素来建立应用于从该类映射的servlet的访问策略,或者禁止任何此类行为。或以编程方式通过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没有角色限制,其他都需要R1角色
    • @ServletSecurity(value=@HttpConstraint(rolesAllowed="R1"),
                      httpMethodConstraints = @HttpMethodConstraint("GET"))
      public class Example6 extends HttpServlet{}
                 
  • 例子13-7 Http除了TRACE,其他都需要角色R1才能访问,对于TRACE,直接拒绝
    • @ServletSecurity(value=@HttpConstraint(rolesAllowed="R1"),
                      httpMethodConstraints= @HttpMethodConstraints(value="TRACE", emptyRoleSemantic=EmptyRolesSemantic.DENY))
      public class Example7 extends HttpServlet{}
                 

13.4.1.2、映射@ServletSecurity到security-constraint

  • 本节描述@ServletSecurity注解等效于security-constraint 元素,它提供了给容器复用现有配置security-constraint元素。
  • @ServletSecurity注解用于定义一个与方法无关的@HttpConstraint, 后面可以跟0个或多个@HttpMethodConstraint(具体方法)约束列表。
  • 如果不包含@HttpMethodConstraint元素,则@ServletSecurity注解对应于单个安全约束元素,该元素包含一个不包含http方法元素的web-resource-collection,因此适用于所有的HTTP方法
  • 例子13-8
    • @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>
                 
  • 例子13-9 @ServletSecurity 包含@HttpMethodConstraint
    • @ServletSecurity([email protected](rolesAllowed="Role1"), 
      	[email protected](value="TRACE", 
      	emptyRoleSemantic = EmptyRoleSemantic.DENY)") 等同于
      <security-constraint>
      	<web-resource-collection>
        	<url-pattern>...</url-pattern>
          <!-- 过滤 TRACE方法 -->
          <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元素,该模型由以下三个元素组成:
    • emptyRoleSemantic
      • 当在rolesAllowed没有设置任何角色时,将应用授权语义(PERMIT或DENY),该元素的默认值为PERMIT,并且不支持将DENY与非空roleAllowed列表合并操作
    • rolesAllowed
      • 包含授权角色名称的列表,当此列表为空时,其含义取决于emptyRoleSemantic的值,当角色名称"*“包含在允许角色列表中时,没有特殊含义,当特殊角色名称”**"出现在rolesAllowed中时,表名独立于角色的用户身份验证是必须且足够的,此元素的默认值为一个空列表
    • transportGuarantee
      • 数据保护要求(无或保密)必须通过请求到达的连接来满足,该元素在含义上等同于包含具有相应值的传输保证的user-data-constraint. 此元素的默认值为NONE
  • 下面描述@HttpConstraint模型与auth-constraint和user-data-constraint的例子
    • 例子 13-10 emptyRoleSemantic=PERMIT, rolesAllowed={}, transportGuarantee=NONE
      • no constraints(没有限制)
    • 例子13-11 传输需要保密
      • emptyRoleSemantic=PERMIT, rolesAllowed={}, transportGuarantee = CONFIDENTIAL
        
        <user-data-constraint>
        	<transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
                   
    • 例子13-12 需要role1角色
      • emptyRoleSemantic=PERMIT, rolesAllowed={Role1}, transportGuarantee=NONE
        <auth-cosntraint>
        	<security-role-name>Role1</security-role-name>
        </auth-cosntraint>
                   
    • 例子13-13 角色需要role1且数据保密传输
      • emptyRoleSemantic=PERMIT, rolesAllowed={Role1}, transportGuarantee=CONFIDENTIAL
         
        <auth-cosntraint>
        	<security-role-name>Role1</security-role-name>
        </auth-cosntraint>
        <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、通过setServletSecurity实现的ServletRegistation动态注册

  • setServletSecurity方法可以在ServletContextListener中使用,以定义要应用于为ServletRegistration定义的映射安全约束。
  • Collection setServletSecurity(ServletSecurityElement arg);
  • setServletSecurity的javax.servlet.ServletSecurityElement参数在结构和模型类似于@ServletSecurity注解的ServletServletSecurity接口,因此, ServletSecurityElement包含HttpConstraintElement和HttpMethodConstraintElement值,等同于security-constraint
  • setServletSecurity方法返回(可能为空)URL集合,如果这些URL集合已经在可移植部署描述符中security-constraint已经配置了,因此不受调用影响
  • 如果从其获取ServletRegistration的ServletContext已经初始化, 则此方法将引发IllegalStateException
  • 当可移动部署描述符中的安全性约束包括与ServletRegistration映射的模式完全匹配的url-pattern时,对ServletRegistration上的setServletSecurity的调用必须对Servlet容器对该模式没有任何影响
  • 除了上面列出的例外,包括使用@ServletSecurity注解Servlet类时,在ServletRegistration上调用setServletSecurity时,它将建立适用于注册url-pattern的安全约束。

13.5、角色

  • 安全角色是由应用程序开发人员或功能组装人员定义的逻辑分组,部署应用程序后,部署程序会将角色映射到运行时环境中的主体或组。
  • Servlet容器根据主体安全性属性,为与传入请求关联的主体实施声明性或程序安全性,这可能通过以下两种方式之一发生:
    1. 部署者已将安全角色映射到操作环境中的用户组,从其安全属性中查询出所对应是属于哪个用户组,仅当部署者的账号有安全角色。
    2. 部署者已将安全角色映射到安全策略域中的主体名的主体名称,在这种情况下,从其安全属性中检索调用主体的主体名称,仅当主体名称与安全角色映射到的主体名称相同时,主体才处于安全角色

13.6、认证方式

  • 一个web客户可以使用以下机制之一向web服务器认证用户:
    • HTTP Basic Authentication (HTTP 基础验证)
    • HTTP Digest Auhtentication (HTTP 摘要验证)
    • HTTPS Client Authentication(HTTPS 客户端验证)
    • Form Based Authentication (基于表单的身份验证)

13.6.1、HTTP基础验证

  • 简单来说就用户名、密码验证方式
  • 基本身份验证不是安全的身份验证协议,用户密码以简单的base64编码发送,并且未认证目标服务器。可以额外增加一些保护机制来缓解这些问题:比如说在部署方案中加入HTTPS(安全传输机制)或者网络级别的安全性(例如IPSEC协议或VPN策略)

13.6.2、HTTP 摘要验证

  • 与HTTP基本身份验证类似,HTTP摘要身份验证基于用户名和密码对用户进行身份验证,但是与HTTP基本身份验证不同,HTTP摘要身份验证不会通过网络发送用户密码,在HTTP摘要式身份验证中,客户端发送密码(和其他数据)的单向加密hash值,尽管密码不是通过网络发送的,但是HTTP摘要式身份验证要求明文密码等效项可用于身份验证容器,以便它可以通过计算预期摘要来验证收到的身份验证器,Servlet容器应支持HTTP_DIGEST身份验证

13.6.3、基于表单的身份验证

  • 使用网络浏览器的内置身份验证机制无法更改”登录屏幕“的外观,该规范引入了必须的基于表单的身份验证机制,该机制允许开放人员控制登录屏幕的外观。
  • web应用程序部署描述符包含登录表单和错误页面的条目,登录表单必须包含用于输入用户名和密码的字段,这些字段必须分别命名为j_username和j_password
  • 当用户尝试访问受保护的网络资源时,容器会检查用户的身份验证,如果用户已通过身份验证并拥有访问资源的权限,则将激活所请求的web资源并返回对其的引用,如果用户未通过身份验证,则会执行以下所有步骤:
    1. 将与安全性约束相关联的登录表单发送到客户端,并由容器存储触发身份验证的URL路径和HTTP协议方法。
    2. 要求用户填写表格,包括用户名和密码字段
    3. 客户端发送这个表格到后端服务器
    4. 容器尝试使用表单的 信息来认证用户。
    5. 如果身份验证失败,则使用转发或重定向返回错误页面,并且响应的状态码设置为200,错误页面包含有关失败的信息。
    6. 如果身份验证成功,则使用储存的URL路径将客户端重定向到对应的资源
    7. 重定向并通过身份验证的请求到达容器后,容器将还原请求和HTTP协议方法,并检查已验证用户的主体是否有访问资源的对应授权角色。
    8. 如果用户被授权,则该请求被容器接受以进行处理
  • 在步骤7中到达的重定向请求的HTTP协议方法可能不同于触发身份验证的请求的HTTP方法。这样,在步骤6的重定向后,即使请求到达的HTTP方法不需要身份验证,表单身份验证器也必须处理重定向的请求。为了提高重定向请求的HTTP方法的可预测性,除非需要与HTTP1.0用户代理进行互操作,在这情况下,应该使用302状态码,否则容器应使用303(SC_SEE_OHTER)状态代码进行重定向(在步骤中6中)【小贴士: http1.0协议中只有302码,没有303状态码;

    http1.1,在默认情况下,很多服务端基础程序,为了兼容http1.0,在遇到本应响应303时,也给客户端响应了302。】

  • 在不受保护的传输上进行时,基于表单的身份验证会遭受与基本身份验证相同的某些漏铜。
  • 当触发身份验证的请求通过安全传输到达,或者登陆页面受到user-data-constraint为CONFIDENTIAL(机密的要求)时,必须将登录页面返回给用户,并通过安全传输提交给容器
  • 一个登录页面应该有user-data-constraint的CONFIDENTIAL(机密性),一个机密的user-data-constraint 应该包含每个security-constraint对应的验证规则
  • HttpServletRequest接口的登录方法为应用程序提供了另一种方法来控制器登录屏幕的外观

13.6.3.1、登录表单注意事项

  • 基于表单的登录和基于URL的会话跟踪可能难以实现。仅当通过cookie或SSL会话信息维护会话时,才应使用基于表单的登录。
  • 为了使得验证正确进行,登录表单的操作必须始终为j_security_check,进行此项限制是为了使得登录表单无论使用哪种资源都可以用,同时要避免要求服务器去指定表单外字段,登录表单应在密码字段加上autocomplete=“off”
    • 例如:下面展示html如何写表单格式
    • <form method="POST" action="j_security_check">
        <input type="text" name="j_username">
        <input type="password" name="j_password" autocomplete="off">
      </form>
                 
  • 如果基于表单的登录是由于HTTP请求而调用的,则原始请求参数必须由容器保留,以便在身份验证成功之后将调用重定向到请求的资源时使用。
  • 如果使用表单登录对用户进行身份验证并创建了HTTP会话,则该会话的超时或无效会导致用户被注销,从某种意义上说,后续请求必须使用用户重新获得身份验证。注销的范围与身份验证的范围相同:例如如果容器支持单点登录(例如符合Java EE 技术的web容器),则用户将需要使用Web容器上托管的任何web应用程序重新进行身份验证。

13.6.4、HTTPS客户端验证

  • 使用HTTPS(基于SSL的HTTP)的终端用户身份验证是一种强大的身份验证机制。此机制要求客户端拥有公共密钥证书(Public Key Certificate PKC), 当前 PKC在电子商务应用程序使用,也可以用在浏览器的单点登录上

13.6.5、其他容器身份验证机制

  • Servlet容器应提供可用于整合和配置其他HTTP消息层身份验证机制的公共接口,以供容器对应程序使用,这些接口应提供给容器供应商以外的各方(包括应用程序开发人员,系统管理员和系统集成商)使用
  • 为了促进可移植的实现和其他容器身份验证机制的集成,建议所有Servlet容器都实现针对容器的身份验证SPI的Servlet容器概要文件(即JSR196),可以从以下地址下载 JSR196

13.7、服务跟踪身份验证信息

  • 由于在运行时环境中映射到角色的基础安全身份(例如用户和组)取决于特定环境,而不是取决于特定应用程序的,因此希望:
    • 使得登录机制和策略成为web应用程序所部署环境的属性
    • 能够使用相同的身份验证信息来验证部署在同一容器中的所有应用程序,
    • 仅当跨越安全策略边界时才需要对用户进行重新认证
  • 因此,需要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 方法(http-method or http-method-omssion)
  • 授权约束建立了对身份验证的要求,并命名了允许执行约束请求的授权角色。用户必须是至少一个指定角色的成员才能被允许执行受约束的请求。特殊角色名称"*“是部署描述符中定义的所有角色名称的简写。特殊角色名称”**" 是任何独立于角色的经过身份验证的用户的缩写,当特殊角色名称"**"出现在授权约束中时,表示任何独立于角色的已认证用户均有权执行受限请求,没有角色命名的授权约束表名在任何情况下都不允许访问受约束的请求,授权约束由以下元素组成:
    • role name (role-name)
  • 用户数据约束建立了通过受保护的传输层连接接收约束请求的要求,所需要保护的强度由运输保证的值定义,INTEGRAL的传输保证用于建立内容完整性的要求,而CONFIDENTIAL的传输保证用于建立保密性的要求。transport-guarantee= NONE表示在任何连接(包括未受保护的连接)上接收到时,容器必须接受受约束的请求。容器可以根据INTEGRAL值限制传输加密保证需求,用户数据约束由以下元素组成:
    • 传输保证(transport-guarantee)
    • 如果没有授权约束适用于请求, 则容器必须接受请求而无需用户身份验证。如果没有用户数据约束适用于请求,则当通过任何连接(包括未受保护的连接)接收到容器时,容器必须接受该请求。

13.8.1、合并约束

  • 为了合并约束,如果在集合中未命名任何HTTP方法,或者该集合在所包含的http-method元素或该集合中专门命名HTTP方法,则称HTTP方法出现在web-resource-collection中包含一个多个http-method-omission元素。
  • 当URL模式和HTTP方法对在多个安全约束中组合出现(即,在web资源集合内)时,通过组合各个约束来定义(在模式和方法上)约束,组合出现相同模式和方法的约束规则如下:
  • 命名角色或通过名称"*“暗示角色的授权约束条件的组合应将各个约束条件中的角色名称并集作为允许的角色。将角色命名为”**"的授权约束应与命名或暗示角色的授权约束结合,以允许任何独立于角色的已认证用户,不包含授权约束的安全约束应与命名或暗示角色的授权约束相结合,以允许未经验证的访问,授权约束的特殊情况,即不命名角色,应与任何其他约束结合使用,以覆盖其作用并阻止访问。

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>
    </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>
               
  • 上述描述可以总结如下权限(重复时候取并集的原则)
  • url-pattern(url模式) http-method(http方法) 允许角色 支持连接限制
    /* 除了GET和POST其他都有权限访问 排除访问 没有限制
    /acme/wholesale/* 除了GET和POST其他都有权限访问 排除访问 没有限制
    /acme/wholesale/* GET CONTRACTOR/SALESCLERK 没有限制
    /acme/wholesale/* POST CONTRACTOR CONFIDENTIAL
    /acme/retail/* 除了GET和POST其他都有权限访问 排除访问 没有限制
    /acme/retail/* GET/POST CONTRACTOR/HOMEOWNER 没有限制

13.8.3、处理请求

  • servlet容器收到请求时,会按照URL路径映射中描述的算法选择在url-pattern与请求URI最匹配的约束进行匹配,如果未选择任何约束,则容器应接受该请求,否则,容器将确定请求的HTTP方法是否受到所选模式的约束。如果不是,则接受请求。否则,该请求必须满足url-pattern处适用于HTTP方法的约束,必须满足以下两个规则,请求才能被接受并分发到相关的servlet:
    • 在其上接收到请求的连接的特性必须满足约束定义的至少一种受支持的连接类型。如果不满足此规则,则容器应拒绝该请求并将其重定向到HTTPS端口
    • 请求的认证特性必须满足约束定义的任何认证和角色要求。如果由于禁止访问(不指定任何角色的授权约束)而不能满足该规则,则该请求将被拒绝,并被拒绝,并向用户返回403(SC_FORBIDDEN)状态码,如果访问权限仅限于允许的角色,并且请求尚未经过身份验证,则该请求将被拒绝为未经授权,并且应返回401(SC_UNAUTHORIZED)状态代码以进行身份验证,如果访问仅限于允许的角色,并且请求的身份验证不是这些角色中的任意一个,则该请求将被禁止,并被拒绝,并将403(SC_FORBIDDEN)状态码返回用户

13.8.4、未覆盖HTTP协议的方法

  • 在配置限制的时候,由于枚举是HTTP 方法,有的HTTP方法并没有枚举到,这些方法将不受保护了
  • 例子1
    • <security-constraint>
      	<web-resource-collection>
        	<web-resource-name>wholesale</web-resource-name>
          <url-pattern>/acme/wholesale/*</url-pattern>
          <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
        	<role-name>SALESCLERK</role-name>
        </auth-constraint>
      </security-constraint>
                 
    • 除了GET方法,其方法将不受保护
  • 例子2 忽略的方法将不会保护,下面是GET方法
    • <security-constraint>
      	<web-resource-collection>
        	<web-resource-name>wholesale</web-resource-name>
          <url-pattern>/acme/wholesale/*</url-pattern>
          <http-method-omission>GET</http-method-omission>
        </web-resource-collection>
        <auth-constraint/>
      </security-constraint>
                 
  • 例子3,GET和POST不会保护到
    • @ServletSecurity(httpMethodConstraints={
        @HttpMethodConstraint(value="GET", rolesAllowed="R1"),
      	@HttpMethodConstraint(value="POST", rolesAllowed="R1",
      	transportGuarantee = TransportGuarantee.CONFIDENTIAL})
        public class Example5 extends HttpServlet{
          
        }
      
                 

13.8.4.1、安全限制的配置规则

  • 目标:确保所有受约束的URL 模式下的所有HTTP方法都具有预期的安全保护(即已覆盖)
    1. 不要在使用HTTP方法进行安全约束设置,如果非要这样,就需要为了URL模式定义的安全保护将应用于所有HTTP方法
    2. 如果您不能遵守规则1,请求添加 并声明(使用http-method元素或等效的注解)拒绝没有配置HTTP方法,其他的HTTP方法(带有安全保护)
    3. 如果您不能遵守规则2,安全限制不能覆盖所有HTTP方法,就需要配置显式忽略HTTP方法,或者使用HttpMethodConstraint注解HttpConstraint设置EmptyRolesSemantic=DENY(没有配置方法都拒绝)

13.8.4.2、处理未覆盖方法

  • 在应用程序部署期间,应用程序安全约束配置中存在的任何未发现的HTTP方法,需要提供信息必须标识未发现的HTTP协议方法以及在其中发现HTTP方法的相应URL模式,可以通过记录所需信息来满足通知部署者的要求。
  • 如果在web.xml文件中配置了deny-uncovered-http-methods标签的时候,所有未配置在安全约束的请求都必须拒绝,且返回403(SC_FORBIDDEN)
  • Servlet容器可以提供可配置的选项,以选择未发现方法的默认行为是ALLOW还是DENY,可以按每个应用程序粗粒度或更大粒度配置此选项,请注意,将此默认设置为DENY可能会导致某些应用程序失败。

13.9、默认规则

  • 默认情况下,访问资源不需要身份验证,当包含于请求URI最匹配的url-pattern的安全性约束(如果有的话)组合在一起,以在请求的HTTP方法上施加身份验证约束(命名角色)时,需要进行身份验证,同样除非适用于该请求的安全约束组合在一起,已在请求的HTTP方法上施加用户数据约束(具有受保护的传输保证),否则不需要受保护的传输。

13.10、登录和登出

  • 容器在将请求分派到Servlet引擎之前建立请求的调用者身份,在整个请求处理过程中,或者直到应用程序成功调用对请求的身份验证,登录或注销之前,调用者身份均保持不变,对于异步请求,在初始化分配时,建立的调用者身份保持不变,直到整个请求的处理完成,或应用程序成功地对该请求调用身份验证,登录或注销。
  • 在处理请求期间登录到应用程序中,恰好对应于存在与该请求相关的有效调用者身份,这可以通过在请求上调用getRemoteUser或getUserPricipal来确定,这些方法中的任意一个返回值为空,表示相对于请求的处理,调用者为登录到应用程序中。
  • 容器可以创建HTTP会话对象以跟踪登录状态。如果开发人员在未验证用户身份的情况下创建了会话,然后容器对用户进行了身份验证,则登录后对开发人员代码可见的会话必须与登录之前创建的会话对象相同,这样就不会丢失会话信息。

继续阅读