随笔 - 41  文章 - 7  trackbacks - 0
<2016年7月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

Servlets定义为JSR 340,可以下载完整规范.
servlet是托管于servlet容器中的web组件,并可生成动态内容.web clients可使用请求/响应模式同servlet交互. 
servlet容器负责处理servlet的生命周期事件,接收请求和发送响应,以及执行其它必要的编码/解码部分.
WebServlet
它是在POJO上使用@WebServlet注解定义,且必须继承javax.servlet.http.HttpServlet类的servlet.
这里有一个示例servlet定义:
@WebServlet("/account")
public class AccountServlet extends javax.servlet.http.HttpServlet {
//. . .
}
默认的servlet名称是全限定类名,可使用注解的name属性来进行覆盖. servlet可使用多个URL来部署:
@WebServlet(urlPatterns={"/account""/accountServlet"})
public class AccountServlet extends javax.servlet.http.HttpServlet {
//. . .
}
@WebInitParam 可用来指定初始化参数:
@WebServlet(urlPatterns="/account",
initParams={
@WebInitParam(name="type"value="checking")
}
)
译者注:通过注解方式来编写Servlet,不需要在web.xml中定义
public class AccountServlet extends javax.servlet.http.HttpServlet {
//. . .
}
HttpServlet 接口包含doXXX 相应的方法来处理HTTP GET, POST,PUT, DELETE, HEAD, OPTIONS, 以及 TRACE请求. 通常开发者比较关心的是覆盖doGet和doPost方法.下面的代码展示了一个用于处理GET请求的servlet:
@WebServlet("/account")
public class AccountServlet extends javax.servlet.http.HttpServlet {
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response) {
//. . .
}
}
在这个代码中:
• HttpServletRequest和HttpServletResponse用于捕获web client的请求/响应.
• 请求参数,HTTP headers; 路径的不同部分如host,port, 以及context以及更多的信息都可以从HttpServletRequest中获取.
HTTP cookies可以发送和获取.开发者需要负责填充(populating)HttpServletResponse, 然后容器传送捕获的HTTP  headers,消息主体(message body)给client.
下面的代码展示了servlet接收到HTTP  GET 请求后,如何向client显示简单响应的:
protected void doGet(HttpServletRequest request,HttpServletResponse response) {
try (PrintWriter out response.getWriter()) {
out.println("<html><head>");
out.println("<title>MyServlet</title>");
out.println("</head><body>");
out.println("<h1>My First Servlet</h1>");
//. . .
out.println("</body></html>");
finally {
//. . .
}
}
请求参数可在GET和POST请求中传递.在 GET请求中,这些参数是以查询字符串的name/value对进行传递的.这里有一个使用请求参数调用先前servlet的示例URL:
. . ./account?tx=10
在POST请求中,请求参数也可以通过在请求体中编码的posted数据来传递.在GET和POST请求中,这些参数都可从HttpServletRequest中获取:
protected void doGet(HttpServletRequest request,HttpServletResponse response) {
String txValue request.getParameter("tx");
//. . .
}
每个请求中的请求参数都可以不同.
Initialization parameter(初始化参数), 也称为init params,可在servlet启动和配置信息中定义.
正如先前说明的, @WebInitParam用来指定servlet的初始化参数:
String type null;
@Override
public void init(ServletConfig configthrows ServletException {
type config.getInitParameter("type");
//. . .
}
通过覆盖javax.servlet.Servlet接口的init,service,destroy方法,你可以操纵servlet生命调用方法的默认行为. 典型地,数据库连接在init方法中初始化,在destroy方法中释放.
你也可以在web程序的部署描述文件web.xml中使用servlet和servlet-mapping来定义一个servlet.
你可以在web.xml中定义Account Servlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
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">
<servlet>
<servlet-name>AccountServlet</servlet-name>
<servlet-class>org.sample.AccountServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AccountServlet</servlet-name>
<url-pattern>/account</url-pattern>
</servlet-mapping>
</web-app>
注解已覆盖了大多数常见情况,因此在那些情况下不需要web.xml。但在某些情况下,如要设置servlets的顺序,则只能使用web.xml完成(后面有讲解).
如果web.xml中的metadata-complete元素为true,那么类中的注解将不再处理(译者注:即忽略注解,此时servlet必须在web.xml中配置):
<web-app version="3.1"
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"
metadata-complete="true">
//. . .
</web-app>
在部署描述符中定义的值将覆盖注解中定义的值.
译者注:重要的事,再说一次,如果设置了 metadata-complete="true",而在web.xml中又没有配置servlet的url映射的话,访问servlet时会出现404错误.切记,切记.
在web程序中,servlet是打包在 .war 文件中的.多个servlets可以打包在一起,这样它们可共享一个servlet context. ServletContext提供了关于servlets执行环境的信息,常常用来与容器通信—例如,在web程序中读取资源包,写日志文件或转发请求.
ServletContext可从HttpServletRequest中获取:
protected void doGet(HttpServletRequest request,HttpServletResponse response) {
ServletContext context request.getServletContext();
//. . .
}
为了会话追踪,servlet可向client发送名为JSESSIONID的HTTP cookie. 此 cookie 可被标记为HttpOnly,这样可确保cookie不会暴露于客户端脚本代码,因此可帮助减轻某种形式的跨站点脚本攻击(注意这里只是减轻,并不能完全杜绝):
SessionCookieConfig config request.getServletContext().
getSessionCookieConfig();
config.setHttpOnly(true);
另外,Servlet可使用URL重写来作为一个基础的会话跟踪。
ServletContext.getSessionCookieConfig 方法会返回SessionCookieConfig, 它可以用来配置cookie的不同属性.
HttpSession 接口可用来查看关于会话标识符和创建时间的相关信息,也可用来绑定对象.
可用来创建新的session对象:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
HttpSession session request.getSession(true);
//. . .
}
session.setAttribute 和session.getAttribute方法用来在session上绑定对象.
如果需要更一步的处理,servlet可将请求转发它其它请求.
你可通过使用RequestDispatchercan转发请求来达到此目的 , RequestDispatchercan可通过HttpServletRequest.getRequestDispatcher或 ServletContext.getRequestDispatcher获取. 前者只接受相对路径,而后者可以接受一个相对于当前上下文的路径:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
request.getRequestDispatcher("bank").forward(requestresponse);
//. . .
}
在上面的代码中,bank是部署在相同上下文的另一个servlet.
ServletContext.getContext 方法可用来获取外部环境的ServletContext. 然后它可以用来获取一个RequestDispatcher,这样它就可以将请求转发到那个上下文中了.
通过调用 HttpServletResponse.sendRedirect方法,你可重定向servlet响应到其它资源.这会向client发送一个临时重定向响应,然后client再发出一个指定URL的新请求. 注意,在这种情况下,原始请求对象对于重定向URL是不可用的. 重定向(redirect)可能稍为会慢点,因为它需要两个客户端请求,而转发(forward)是在容器内进行:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
//. . .
response.sendRedirect("http://example.com/SomeOtherServlet");
}
这里,响应被重定向到了http://example.com/SomeOtherServlet URL.注意,这里的URL可以是不同主机/端口,也可以是容器相对的或绝对的路径.
除了使用@WebServlet和web.xml声明serlvet,你也可以通过编程使用ServletContext.addServlet方法来定义. 你可从ServletContainerInitializer.onStartup或ServletContextListener.contex
tInitialized方法中来做到这一点.在17页"Event Listeners”中,你可以读到更多关于这些的信息.
ServletContainerInitializer.onStartup 方法是在程序启动的时候调用的.addServlet方法将返回
ServletRegistration.Dynamic, 然后就可以用它(ServletRegistration.Dynamic)来创建URL映射,设置安全角色,设置初始化参数,以及管理其它配置项:
public class MyInitializer implements ServletContainerInitializer {
@Override
public void onStartup (Set<Class<?>> clazzServletContext context) {
ServletRegistration.Dynamic reg =
context.addServlet("MyServlet""org.example.MyServlet");
reg.addMapping("/myServlet");
}
}
Servlet 过滤器
servlet过滤器可用来修改请求和响应的负载以及头信息. 重要的是,要意识到过滤器不会创建响应—它们只修改或适配请求和响应. 认证,日志,数据压缩,数据加密通常都通过过滤器来实现. 过滤器同servlet一起打包,它可作用于动态或静态内容.
通过指定一种URL模式,过滤器可与servlet或一组servlet以及静态内容相关联. 可使用@WebFilter注解来定义过滤器:
@WebFilter("/*")
public class LoggingFilter implements javax.servlet.Filter {
public void doFilter(HttpServletRequest request,
HttpServletResponse response) {
//. . .
}
}
在上面展示的代码中,LoggingFilter将应用于web程序中所有servlets和静态内容页面上.
@WebInitParam 也可在这里指定初始化参数.
过滤器和目标servlet总是在同一个调用线程中执行. 多个过滤器可在过滤器链中安排执行.
在部署描述符中,你也可以使用<filter> 和<filter-mapping> 元素来定义过滤器:
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>org.sample.LoggingFilter</filter-class>
</filter>
. . .
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
除了使用@WebFilter 和web.xml来定义过滤器外,你可通过编程方式使用 ServletContext.addFilter 方法来定义.要做到这一点,你需要在ServletContainerInitializer.onStartup方法或 ServletContextLis
tener.contextInitialized方法中执行.addFilter方法将返回ServletRegistration.Dynamic, 可用它来URL模式映射,设置初始化参数,以及处理其它配置项:
public class MyInitializer implements ServletContainerInitializer {
public void onStartup (Set<Class<?>> clazzServletContext context) {
FilterRegistration.Dynamic reg =
context.addFilter("LoggingFilter","org.example.LoggingFilter");
reg.addMappingForUrlPatterns(nullfalse"/");
}
}
事件监听器
事件监听器为ServletContex,HttpSession,ServletRequest对象提供了生命周期回调事件. 
在那些对象中,这些监听器都是实现了支持状态变化事件通知接口的实现类.每个类都可通过
@WebListener注解, 或在web.xml中声明,或通过某个ServletContext.addListener方法注册.
这些监听器典型例子是用于额外servlet的编程注册对于程序员来说没有明确的必要,或者数据库连接的初始化或恢复需要在应用程序级恢复.
对于每种事件类型,可以有多个监听器类,容器在为每种事件类型调用监听器bean时可指定顺序. 
在应用程序关闭时,会以相反的顺序通知监听器.
Servlet 上下文监听器会在那个上下文中监听资源事件:
@WebListener
public class MyContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context sce.getServletContext();
//. . .
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//. . .
}
}
ServletContextAttributeListener用于监听上下文中属性变化:
public class MyServletContextAttributeListener
implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
//. . . event.getName();
//. . . event.getValue();
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
//. . .
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
//. . .
}
}
HttpSessionListener用于在session中监听资源事件:
@WebListener
public class MySessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent hse) {
HttpSession session hse.getSession();
//. . .
}
@Override
public void sessionDestroyed(HttpSessionEvent hse) {
//. . .
}
}
HttpSessionActivationListener 用于session钝化或激活事件监听:
public class MyHttpSessionActivationListener
implements HttpSessionActivationListener {
@Override
public void sessionWillPassivate(HttpSessionEvent hse) {
// ... hse.getSession();
}
@Override
public void sessionDidActivate(HttpSessionEvent hse) {
// ...
}
}
HttpSessionAttributeListener 用于监听session中属性变化:
public class MyHttpSessionAttributeListener
implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
//. . . event.getName();
//. . . event.getValue();
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
//. . .
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
//. . .
}
}
HttpSessionBindingListener用于监听session中绑定或解绑对象的事件:
public class MyHttpSessionBindingListener
implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
//. . . event.getName();
//. . . event.getValue();
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
//. . .
}
}
ServletRequestListener 用于在request中监听资源事件:
@WebListener
public class MyRequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequest request sre.getServletRequest();
//. . .
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
//. . .
}
}
ServletRequestAttributeListener用于在request中监听属性变化.
还有AsyncListener,它用来管理完成,超时或错误的异步事件.
除了使用@WebListener和web.xml来定义外, 你还可以使用ServletContext.addListener方法编程定义.在ServletContainerInitializer.onStartup或ServletContextListener.contextInitialized方法中可做到这一点.
ServletContainerInitializer.onStartup方法是当程序启动时调用的:
public class MyInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> clazzServletContext context) {
context.addListener("org.example.MyContextListener");
}
}
异步支持
服务器资源是保贵的,应该谨慎地使用. 考虑一个等待池中JDBC连接可用的servlet,接收JMS消息或从文件系统读取资源的servlet.等待长时间运行进程返回会完全阻塞线程—等待, 坐着,什么事都不做—它不是一个服务器资源的最佳使用. 这里服务器可以异步处理,例如,在等待长时间运行的进程完成时,控制(或线程)将返回给容器执行其他任务. 在响应从长时间处理过程中重新返回时,请求将继续在同一个线程中进行, 或者在长时间处理过程中,分配到新资源执行.
长时间处理的典型使用是聊天程序.
异步行为需要在servlet上明确地启用.你可以在@WebServlet中添加asyncSupported属性来做到这一点:
@WebServlet(urlPatterns="/async"asyncSupported=true)
public class MyAsyncServlet extends HttpServlet {
//. . .
}
你也可以在web.xml中通过设置<async-supported>为ture或在程序注册期间调用ServletRegistration.setAsyncSupported(true)来启用异步.
然后可在单独线程中在request上使用startAsync方法来启动异步处理.此方法会返回AsyncContext, 它代表的是异步请求的执行上下文.随后,你可以调用AsyncContext.complete (explicit) 来完成异步请求或将其调度到其它资源 (隐性地). 在后一种情况下,容器将完成异步请求的调用.
让我们实现长时间运行处理:
class MyAsyncService implements Runnable {
AsyncContext ac;
public MyAsyncService(AsyncContext ac) {
this.ac ac;
}
@Override
public void run() {
//. . .
ac.complete();
}
}
可从goGet方法中调用此service:
@Override
protected void doGet(HttpServletRequest request,HttpServletResponse response) {
AsyncContext ac request.startAsync();
ac.addListener(new AsyncListener() {
public void onComplete(AsyncEvent event)
throws IOException {
//. . .
}
public void onTimeout(AsyncEvent event)
throws IOException {
//. . .
}
//. . .
});
ScheduledThreadPoolExecutor executor new ScheduledThreadPoolExecutor(10);
executor.execute(new MyAsyncService(ac));
}
在上面的代码中,请求被放入了异步模式. AsyncListener注册为事件监听器,当请求处理完成时,超时时,或出现错误时,将会调用相关的事件方法.长时间运行的服务是在单独线程上调用的, 调用Async
Context.complete时就表示请求处理完成了.
一个请求可从异步servlet调度给同步servlet,但其它方式是非法的.
异步行为在servlet过滤中也可用.
非阻塞 I/O
Servlet 3.0只允许在传统的I/O上异步请求处理,这限制了应用程序的可扩展性。在典型应用中,这通过while循环来读取ServletInputStream:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
ServletInputStream input = request.getInputStream();
byte[] b = new byte[1024];
int len = -1;
while ((len = input.read(b)) != -1) {
//. . .
}
}
如果到来的数据是阻塞的或流速慢于服务器读取的速度,那么服务器线程会等待数据. 当将数据写入
ServletOutputStream时,也会发生同样的情况.这限制了Web容器的可扩展性.
非阻塞I/O允许开发者在数据可用时读取数据或者写数据. 这不仅增加了Web容器的可扩展性,而且增加了可以同时处理的连接数量. 非阻塞I/O只能工作Servlets,Servlets, Filters, 以及Upgrade Processing的异步处理过程中.
Servlet 3.1通过引入两个新接口来实现非阻塞I/O: ReadListener和WriteListener.这些监听器的回调 方法会在内容无阻塞地可读或可写时调用.
在这种情况下,需要重写doGet方法:
AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));
调用setXXXListener方法表示使用非阻塞I/O来代替传统I/O.
ReadListener有三个回调方法:
• onDataAvailable回调方法会在数据可无阻塞读取时调用.
• onAllDataRead回调方法会在当前请求数据完全读取后调用.
• onError回调会在处理请求时出现错误时调用
@Override
public void onDataAvailable() {
try {
StringBuilder sb new StringBuilder();
int len = -1;
byte b[] = new byte[1024];
while (input.isReady() && (len input.read(b)) != -1) {
String data new String(b0len);
}
catch (IOException ex) {
//. . .
}
}
@Override
public void onAllDataRead() {
context.complete();
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
context.complete();
}
在上面的代码中,onDataAvailable回调将在数据可无阻塞读取时调用. ServletInputStream.isReady 方法用于检查数据是否可以无阻塞地读以及是否准备好读. context.complete会在
onAllDataRead 和 onError 方法中调用以发出数据读取完成的信号. ServletInputStream.isFinished方法可用来检查非阻塞I/O读的状态.
在ServletInputStream上最多只能注册一个ReadListener.
WriteListener有两个回调方法:
• onWritePossible回调方法会在当数据可以无阻塞写时调用.
• onError回调会在处理响应错误时调用.
ServletOutputStream上最多可注册一个WriteListener. 
ServletOutputStream.canWrite是用于检查数据是否可以无阻塞写的新方法.
Web 片断(Fragment)
web 片断是包含在library或框架(framework )JAR中META-INF目录下的部分或全部web.xml 文件
如果这个框架绑定在WEB-INF/lib 目录下,容器会进行提取并配置,不需要开发明确地处理.
它几乎可以包含web.xml中能指定的所有元素,但其顶层元素必须是web-fragment,且相应的文件名必须是webfragment.xml. 这可用来逻辑分割web程序:
<web-fragment>
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>org.example.MyFilter</filter-class>
<init-param>
<param-name>myInitParam</param-name>
<param-value>...</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-fragment>
开发者在web.xml和web-fragment.xml中指定加载的顺序.web.xml中的<absolute-ordering>元素用于指定资源应该加载的准确顺序,web-fragment.xml 中的<ordering>元素用于指定相对顺序. 这两个顺序是互斥的,绝对顺序会覆盖相对顺序。absolute ordering可包含一个或多个<name> 元素(用于指定资源名称和加载顺序).指定<others/>允许其它资源在加载时不指定名称:
<web-app>
<name>MyApp</name>
<absolute-ordering>
<name>MyServlet</name>
<name>MyFilter</name>
</absolute-ordering>
</web-app>
在上面的代码中,在web.xml中指定的资源将首先加载,然后才是MyServlet和MyFilter.
在<ordering>中的0个或一个<before> ,<after>元素 用来在webfragment 指定需要之前和之后加载的资源顺序:
<web-fragment>
<name>MyFilter</name>
<ordering>
<after>MyServlet</after>
</ordering>
</web-fragment>
上面的代码会要求容器在MyServlet(其它地方定义)之后加载MyFilter.
如果web.xml中将metadata-complete设置为true,那么web-fragment.xml 文件将不被处理
当web.xml和web-fragment.xml配置冲突时,web.xml文件有最高优先级.
如果web-fragment.xml 文件中没有<ordering>元素,并且web.xml 没有<absolute-ordering>元素,资源将假设为没有任何依赖.
安全
Servlets通常都会通过Internet来访问,因此对于安全要求是常见的.你可以通过注解或web.xml中指定servlet的安全模型包括角色,访问控制,认证要求.
@ServletSecurity可为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方法是不受保护的.
如果HTTP方法没有在安全约束中列出,约束定义的保护将会应用于整个HTTP (extension)方法:
<security-constraint>
<web-resource-collection>
<url-pattern>/account/*</url-pattern>
</web-resource-collection>
. . .
</security-constraint>
在上面的代码中,所有 /account/*方法都将保护.
Servlet 3.1 定义了未覆盖(uncovered )HTTP 协议方法作为未列举在<security-constraint>中的方法,如果至少有一个<http-method>列举在<securityconstraint>中:
<security-constraint>
<web-resource-collection>
<url-pattern>/account/*</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
. . .
</security-constraint>
在这段代码片断中,只有HTTP GET 方法受保护,所有其它HTTP 协议方法如POST,PUT是未覆盖的.
<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方法不受保护,所有其它协议方法都将受保护.
<deny-uncovered-http-methods>元素(Servlet 3.1中的新元素),可用来拒绝未覆盖HTTP方法的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>
在这段代码中,<deny-uncovered-http-methods> 元素可确保HTTP GET方法可使用需要的安全约束访问,所有其它HTTP方法都会使用403状态码拒绝.
@RolesAllowed, @DenyAll, @PermitAll, 以及 @TransportProtected 提供了另外的注解来在特定资源或资源方法上指定安全角色:
@RolesAllowed("R2")
protected void doGet(HttpServletRequest requestHttpServletResponse response) {
//. . .
}
如果在类和方法上都指定了注解,那么方法上的注解将覆盖类上指定的.
Servlet 3.1 引入了两个新的预定义角色:
• * 匹配任何已定义角色.
• ** 可独立于角色匹配任何认证的用户.
这使得你可以站在比特定角色更高的层次上来指定安全约束.
最多只允许@RolesAllowed, @DenyAll, 或@PermitAll其中一个指定在目标上.
@TransportProtected 注解可以与@RolesAllowed与@PermitAll 组合使用.
servlets可为HTTP Basic, HTTP Digest, HTTPS Client,以及基于表单认证进行配置:
<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>
这段代码展示如何实现基于表单的认证.登录表单必须包含输入用户名和密码的字段.这些字段必须命名为j_username 和 j_password.表单的action总是j_security_check.
Servlet 3.1 要求要密码表单字段中使用autocomplete="off",这样表单的安全将更强键.
HttpServletRequest对于登录,登出,以及认证方法也提供了编程安全.
登录方法使用ServletContext中配置的密码验证机制来提供的用户名和密码。
这就确保了getuserprincipal,getremoteuser,和getauthtype方法返回有效值。
可以使用登录方法作为基于表单登录的替换。
验证方法使用容器登录机制配置ServletContext认证使用这个请求的用户。
资源打包
你可以使用ServletContext.getResource和.getResourceAsStream方法来访问绑定在.war文件中的资源.其资源路径是以前导"/.”开头的字符串.此路径是通过相对于上下文的root或相对于绑定于WEB-INF/lib下JAR文件中的META-INF/resources目录来解析的:
myApplication.war
WEB-INF
lib
library.jar
library.jar有下面的结构:
library.jar
MyClass1.class
MyClass2.class
stylesheets
common.css
images
header.png
footer.png
正常情况下,如果stylesheets和image 目录要在servlet中访问,你需要将它们手动抽取到web程序的root之下. Servlet 3.0允许将这些资源打包到META-INF/resources目录中:
library.jar
MyClass1.class
MyClass2.class
META-INF
resources
stylesheets
common.css
images
header.png
footer.png
在这种情况下,资源不需要抽取到程序的root下,相反可以直接访问. 这样就允许直接访问第三方jar包中META-INF/resources,而不用手动抽取.
应用程序会先查询root下的资源,然后再扫描WEB-INF/lib目录下JARs中的资源. 至于WEBINF/lib 目录下的JAR文件的扫描顺序却是不确定的.
错误处理
HTTP 错误码或servlet抛出的异常,可以通过自定义错误页面来完成. 
这些页面是通过<error-page>来定义的:
<error-page>
<error-code>404</error-code>
<location>/error-404.jsp</location>
</error-page>
在web.xml中加入上面的片段后,客户端访问不存在资源时,将显示/error-404.jsp 页面
你也可以很容易地添加其它<error-page>元素来映射其它HTTP状态码.
<exception-type>元素用来映射servlet抛出异常时跳转的页面:
<error-page>
<exception-type>org.example.MyException</exception-type>
<location>/error.jsp</location>
</error-page>
在web.xml中增加上面的片段后,当servlet抛出org.example.MyException异常时,将会显示/error.jsp 页面.你可以轻松地添加其它<error-page>元素来映射其它异常.
每个<error-page>声明对于类名和HTTP状态来说,必须是唯一的.
处理Multipart Requests
@MultipartConfig 可指定在servlet上,用于表示它希望multipart/form-data请求类型. HttpServletRequest.getParts和.getPart 方法可构造multipart request的不同部分:
@WebServlet(urlPatterns = {"/FileUploadServlet"})
@MultipartConfig(location="/tmp")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,HttpServletResponse response)
throws ServletExceptionIOException {
for (Part part request.getParts()) {
part.write("myFile");
}
}
}
在这段代码中:
• @MultipartConfig是在类上指定的,这就表明doPost方法可用来接收multipart/form-data类型请求.
• location属性用来指定文件存储的目录位置(根据java ee7指南,此属性指定的绝对路径,而且此目录在上传前需要提前创建)
• getParts 方法为这个multipart request提供了part集合
• part.write用来将上传的part写入磁盘.
Servlet 3.1增加了一个新方法, Part.getSubmittedFileName,用于获取客户端指定的文件名称.
This servlet can be invoked from a JSP page:
<form action="FileUploadServlet"
enctype="multipart/form-data"
method="POST">
<input type="file" name="myFile"><br>
<input type="Submit" value="Upload File"><br>
</form>
在这段代码中,表单使用multipart/form-data通过POST方法提交到了FileUploadServlet.
升级处理
HTTP 1.1 (RFC 2616)的 14.42章节定义了一种允许将HTTP1.1过度到其它不兼容协议的升级机制. 
协议更改后应用层通信的能力和性质完全依赖于所选择的新协议. 在客户端和服务器之间协商升级后,随后的请求使用新选择的消息协议交流.一个典型的例子是HTTP如何升级到WebSocket协议,在RFC 6455中开放的握手部分描述.
servlet容器提供了一个HTTP升级机制。然而,servlet容器本身没有任何关于升级协议知识。协议处理封装在HttpUpgradeHandler。数据读取或写入servlet容器和HttpUpgradeHandler之间是字节流。
决定升级是在servlet.service方法中决定的.
升级可通过添加新方法实现,即 HttpServletRequest.upgrade,和 两个新接口:javax.servlet.http.HttpUpgradeHandler 和 javax.servlet.http.WebConnection:
if (request.getHeader("Upgrade").equals("echo")) {
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
response.setHeader("Connection""Upgrade");
response.setHeader("Upgrade""echo");
request.upgrade(MyProtocolHandler.class);
System.out.println("Request upgraded to MyProtocolHandler");
}
请求会查找 Upgrade header,根据其值来做决定.
在这中情况下,如果Upgrade header等价于echo,连接就会升级,同时会设置响应状态和headers
升级方法是通过传递HttpUpgradeHandler实例在HttpServletRequest上调用的.
在退出servlet的service方法后, servlet容器会完成所有过滤器的处理,并且会使连接通过HttpUpgradeHandler实例处理:
public class MyProtocolHandler implements HttpUpgradeHandler {
@Override
public void init(WebConnection wc) {
//. . .
}
@Override
public void destroy() {
//. . .
}
}
这段代码展示了一个HttpUpgradeHandler的实现. servlet容器会调用HttpUpgradeHandler的初始化方法, 传递WebConnection以允许协议处理器可访问数据流.
当升级处理过程完成时,会调用其HttpUpgradeHandler.destroy方法.
servlet过滤器只处理初始的HTTP请求和响应,在后续通信中,将不再被调用.
posted on 2016-07-24 01:32 胡小军 阅读(745) 评论(0)  编辑  收藏 所属分类: Java EE7

只有注册用户登录后才能发表评论。


网站导航: