[Java EE 7] Servlet 安全机制

一般说来,servlet 会部署到 internet 上,因此需要一些安全性的考虑。你可以制定 servlet 的安全模式,例如角色、访问控制、鉴权等。这些都可以用 annotation 或 web.xml 进行配置。

@ServletSecurity 定义了安全约束,它可以添加在 servlet 实现类上,这样对 servlet 中的所有方法都生效,也可以单独添加在某个 doXXX 方法上,这样只针对这个方法有效。容器会强制调整 doXXX 方法被指定角色的用户调用:

@WebServlet("/account")
@ServletSecurity(
  value=@HttpConstraint(rolesAllowed = {"R1"}),
  httpMethodConstraints={
    @HttpMethodConstraint(value="GET", 
                          rolesAllowed="R2"),
    @HttpMethodConstraint(value="POST", 
                          rolesAllowed={"R3", "R4"})
  }
)
public class AccountServlet 
             extends javax.servlet.http.HttpServlet {
  //. . .
}


在上面的代码段中,@HttpMethodConstraint 定义了 doGet 方法只能被角色为 R2 的用户调用,doPost 方法只能被角色为 R3 或 R4 的用户调用。@HttpConstraint 定义了其它的所有方法都能被角色为 R1 的用户调用。角色与用户映射容器的角色和用户。

安全约束也可以使用 web.xml 中的 <security-constraint> 元素来定义。在这个元素中,使用 <web-resource-collection> 元素来指定 HTTP 操作和 web 资源, 元素     <auth-constraint> 用来指定可以访问资源的角色,<user-data-constraint> 元素中使用 <transport-guarantee> 元素来指定客户端和服务器端的数据应该怎样被保护:

<security-constraint>
  <web-resource-collection>
    <url-pattern>/account/*</url-pattern>
    <http-method>GET</http-method>
  </web-resource-collection>

  <auth-constraint>
    <role-name>manager</role-name>
  </auth-constraint>

  <user-data-constraint>
    <transport-guarantee>INTEGRITY</transport-guarantee>
  </user-data-constraint>
</security-constraint>


上面这段部署描述符表示:在 /account/* URL 上使用 GET 请求将会受到保护,访问的用户必须是 manager 角色,并且需要数据完整性。所有 GET 之外的其它 HTTP 请求都不会受到保护。 如果在 <security-constraint> 中没有明确指定 HTTP 方法的约束,那么默认所有的 HTTP 方法都会受到保护:

<security-constraint>
  <web-resource-collection>
    <url-pattern>/account/*</url-pattern>
  </web-resource-collection>

  . . .
</security-constraint>


上面这段配置表示所有访问 /account/* URL 的 HTTP 方法都将受到保护。

从 Servlet 3.1 开始,规定了没有列入 <security-constraint> 中的 HTTP 方法称为“未被覆盖的方法”(uncovered,意思是在 security-constraint 中没有覆盖到此方法),并且在 <security-constraint> 中至少需要指定一个 <http-method> 元素:

<security-constraint>
  <web-resource-collection>
    <url-pattern>/account/*</url-pattern>
    <http-method>GET</http-method>
  </web-resource-collection>
  . . .
</security-constraint>


上面的配置中,只有 GET 方法会受到保护,所有其它的 HTTP 方法,比如 PUT、POST 都是“未被覆盖的方法”。

在 <http-method-omission> 元素中可以指定哪些 HTTP 方法不受保护:

<security-constraint>
  <web-resource-collection>
    <url-pattern>/account/*</url-pattern>
    <http-method-omission>GET</http-method-omission>
  </web-resource-collection>

  . . .
</security-constraint>


在上面的例子中,只有 HTTP GET 方法将不受保护,其它的 HTTP 方法将会受到保护。

<deny-uncovered-http-methods> 元素是 Servlet 3.1 中新增加的元素,用于阻止使用“未被覆盖的方法”进行访问。如果使用这个被阻止的 HTTP 方法进行访问,那么将会收到 403(SC_FORBIDDEN)错误状态码:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
           http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  <deny-uncovered-http-methods/>
  <web-resource-collection>
    <url-pattern>/account/*</url-pattern>
    <http-method>GET</http-method>
  </web-resource-collection>
  . . .
</web-app>


在上面的例子中,除了 HTTP GET 方法可以安全访问之外,其它所有的 HTTP 方法访问将会返回 403 错误码。

@RolesAllowed、@DenyAll、@PermitAll、和 @TransportProtected是用来进行安全配置的 Annotation:

@RolesAllowed("R2")
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
  //. . .
}


如果同样的 annotation 被添加到类和方法上,那么方法级别的配置将覆盖类级别的配置。

Servlet 3.1 包含了两个预定义的角色:

    * - 表示任意已定义的角色
    ** - 表示某个角色下的任意认证用户

这将允许你在更高的级别设置安全约束,而不仅仅只是针对某个角色。

同一个声明上,最多只能使用一个 @RolesAllowed、@DenyAll 或 @PermitAll 注释。@TransportProtected 可以和 @RolesAllowed 或 @PermitAll 进行组合。

可以将 Servlet 进行配置与基本 HTTP,HTTP Digest,HTTPS 客户端、或 form 表单进行权限认证。

<form method="POST" action="j_security_check">
  <input type="text" name="j_username">
  <input type="password" name="j_password" autocomplete="off">
  <input type="button" value="submit">
</form>


上面的例子演示了如何使用 form-based 的方法进行验证。登陆表单必须包含用户名和密码,并且用户名和密码字段的名称必须分别是“j_username”和“j_password”,form 表单提交的 action 必须是 “j_security_check”。

Servlet 3.1 需要在密码字段上添加 autocomplete="off",用于进一步加强 form 表单的安全性。

HttpServletRequest 也提供了 login、logout 和 authenticate 方法,方便使用编码的方式来进行权限验证。

login() 方法将验证用户名和密码是否匹配在 ServletContext 中的配置(依赖 web 容器进行配置)的用户名密码。这将保证 getUserPrincipal、getRemoteUser、和 getAuthType 返回正确的值,使用 HttpServletRequest 提供的 login 方法可以取代上面使用的 form-based 验证。

authenticate() 方法将使用 ServletContext 的容器登陆机制来验证用户发起的这次请求。

文章来源: http://www.aptusource.org/2014/04/java-ee-7-servlet-security/

你可能感兴趣的:(Java EE 7)