Lecture 3 - California State University, East Bay

Download Report

Transcript Lecture 3 - California State University, East Bay

Servlet Filters
L. Grewe
Filters




New with Servlet Specification 2.3
Lightweight framework for filtering
dynamic or static content
A filter is a reusable piece of code that can
transform the content of HTTP requests,
responses, and header information.
Example uses:
•
•
•
•
•
•
•
Authentication filters
Logging and auditing filters
Image conversion filters
Data compression filters
Encryption filters
XSL/T filters that transform XML content
Caching filters
Servlets: Filters
Transform HTTP requests and
responses
servlet
filter
request
request
response
response
Example 1: Facebook Authenticaton



You have a facebook application built with various jsps
and/or servlets.
Before someone can use your facebook application they
must be logged into facebook as an authenticated user
before your application can get any user data from
facebook about them.
SOLUTION 1:
• Create a servlet that serves as the “interface servlet” (callback
url) between facebook and your application. This servlet must
make sure that the user is logged into facebook/autheticated
and if not forwards them to a URL for facebook log- in.

PROBLEM with SOLUTION:
• Now need separate Servlet layer for each time user makes
request of your app.

BETTER SOLUTION:……..FILTERS……next….
Facebook Authenticaton w/ Filters


Instead of a separate servlet, create
a filter used before each of your apps
jsps/servlets.
This filter, FaceBookAuthFilter, makes
sure the user is authentic and
• if so passes on this information to your
main apps jsps/servlets as parameters
in request.
• If NOT then forwards request on to
facebook log-in page.
FaceBookAuthFilter Setup
Facebook App JSPs/servlets
FaceBookAuthFilter
request
request
OR
response
response
request
Facebook login
FaceBookAuthFilter Code
(see website for complete code)
/**
* The servlet filter that makes sure that the user is logged in before
* letting the requests reach the application code.
* @author theliveweb.net
*
*/
public class FaceBookAuthFilter implements Filter {
private String _apiKey;
private String _secretKey;
//read in some parameters passed via web.xml file
//these parameters are unique to each facebook application and
// are required when you make facebook api calls in your webapp
public void init(final FilterConfig filterConfig){
_apiKey = filterConfig.getInitParameter("api_key");
_secretKey = filterConfig.getInitParameter("secret_key");
}
/**
* Verifies whether user is logged in. If not, sends user to the login page.
*/
public void doFilter(final ServletRequest request, final ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpRes = (HttpServletResponse) response;
try {
//determine if user authenticated and if not throw an exception
httpReq.getParameter(FacebookParam.SESSION_KEY.toString())+"<br><br>");
FacebookXmlRestClient authClient =
FaceBookAuthHandler.getAuthenticatedClient(httpReq, _apiKey, _secretKey);
//if user authenticated set as request param and call next filter or the Servlet
request.setAttribute("facebook.client", authClient);
chain.doFilter(request, response);
} catch (FailedLoginException fle) {
//user not logged in, this will forward request to facebook login page
forceLogin(httpRes);
}
} catch (Exception e) {
//handle other exceptions
}
Facebook example…partial web.xml file
<display-name>Facebook Suchana</display-name>
<filter>
<filter-name>
FaceBookAuthFilter
</filter-name>
<filter-class>
net.theliveweb.facebook.FaceBookAuthFilter
</filter-class>
<init-param>
<param-name>api_key</param-name>
<param-value>b70966a3bbf411cd67e12b052f159e9a</param-value>
</init-param>
<init-param>
<param-name>secret_key</param-name>
<param-value>3eac91f71a179f810d4e2495cc3bace1</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FaceBookAuthFilter</filter-name>
<url-pattern>/T.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FaceBookAuthFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FaceBookAuthFilter</filter-name>
<url-pattern>/avatar</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>avatar</servlet-name>
<jsp-file>/avatar.jsp</jsp-file>
</servlet>
<servlet>
<servlet-name>T</servlet-name>
<jsp-file>/T.jsp</jsp-file>
</servlet>
Declare our web-app servlets/
jsps use our FaceBookAuthFilte
through the web.xml file
Here we are declaring the
filter, you can have more
than one if you want.
Here we are saying what
filters are applied to which
webapp url patterns
Here we have are
servlet declarations…
only partial info here
Filter class methods:

init
• called before the servlet engine begins using the filter.

destroy
• called before the engine removes a filter from service. If
you need to clean up filter-specific resources, you can do
that with the destroy method.

doFilter
• meat of the filter, call each time user invokes filtered
servlets/jsps
• where you have access to the request and response
objects, just as you would in a normal servlet's doGet or
doPost method. You can query or modify these objects
as needed.
• Then, you can forward the request to the next filter in
the chain (or to the servlet if this is deployed as the last
filter) by calling filterChain.doFilter.
Generic Filter deployment


Deploy this class with webapp that use this
filter.
Modify the webapp’s web.xml
• Add filter definition to bottom of list of filters (if
any):
<filter>
<filter-name>Request Blocker</filter-name>
<filter-class>com.develop.filters.RequestBlocker</filter-class>
</filter>
• Apply filter to any servlets/jsps by indicating the urlpattern:
<filter-mapping>
<filter-name>Request Blocker</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filters can Modify BOTH the
request and response objects!

Our facebook example modified the
request parameter…..lets look at an
example that modifies the response
parameter……
Example 2: XSLTFilter

Automatically performs a transform
on an XML document returned the
servlet it calls (chains to), rendering
the document into HTML before
returning it to the caller.
How XSLTFilter Works

STEPS of doFilter()
1. Setup A SPECIAL response object we can use easily for translation:
•


Instead of passing the response object to the next filter in the chain, XSLTFilter's call
to doFilter specifies a customized response object named wrappedResp
To make it easy to replace the response object, the filter architecture provides a helper
class, named HttpServletResponseWrapper, that wraps the original response object,
and simply passes through every method call. XSLTFilter creates an anonymous
subclass of HttpServletResponseWrapper, overriding three methods: getOutputStream,
getWriter, and setContentType.
The getOutputStream and getWriter methods use an instance of the nested helper
class ByteArrayPrintWriter, named pw.
2. Call downstream filter or Servlet
•
When a downstream filter or servlet writes into the "response," it actually writes into
the instance of pw (which is the writer for the wrappedResp object) AND this pw object
is stored in the Filter class.
3. Change response type to “text/html”

The setContentType method checks to see if the content being returned is "text/xml".
If a downstream servlet or filter tries to set the content type to "text/xml", the
overridden setContentType changes it to "text/html" instead, and sets a flag,
xformNeeded[0], indicating that the transform needs to run.
4. Translate current response from xml to html


After calling filerChain.doFilter, XSLTFilter checks to see if it needs to transform the
response. If it does, it takes the downstream response from pw, and transforms it into
the "real" response, resp. Of course, resp might not be the "real" response either,
because XSLTFilter might be downstream from yet another filter.
Transformation done w/ XSLT, which is loaded using the Java API for XML Parsing
(JAXP) TransformerFactory class. Setting up the transform illustrates another filter
feature: initialization parameters. The XSLTFilter expects to be configured with an
initialization parameter named "xslt" that specifies which transform to run.
XSLTFilter code
//class com.develop.filters.XSLTFilter
package com.develop.filters;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class
private
private
private
private
XSLTFilter implements Filter {
ServletContext ctx;
String xslt;
TransformerFactory tf = TransformerFactory.newInstance();
Transformer xform;
private static class ByteArrayServletStream extends ServletOutputStream
{ ByteArrayOutputStream baos;
ByteArrayServletStream(ByteArrayOutputStream baos) { this.baos =
baos; }
public void write(int param) throws java.io.IOException {
baos.write(param); } }
private static class ByteArrayPrintWriter {
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private PrintWriter pw = new PrintWriter(baos);
private ServletOutputStream sos = new ByteArrayServletStream(baos);
public PrintWriter getWriter()
{ return pw; }
public ServletOutputStream getStream()
{ return sos; }
}
byte[] toByteArray()
{ return baos.toByteArray(); }
public void init(FilterConfig filterConfig) throws ServletException {
ctx = filterConfig.getServletContext();
xslt = filterConfig.getInitParameter("xslt");
ctx.log("Filter " + filterConfig.getFilterName() + " using xslt " + xslt);
try {
}
xform = tf.newTransformer(new StreamSource( ctx.getResourceAsStream(xslt))); }
catch (Exception e)
{ ctx.log("Could not intialize transform", e); throw new ServletException( "Could
not initialize transform", e); }
public void doFilter(javax.servlet.ServletRequest servletRequest,
javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain
filterChain) throws java.io.IOException, javax.servlet.ServletException {
HttpServletRequest hsr = (HttpServletRequest)servletRequest;
final HttpServletResponse resp = (HttpServletResponse)servletResponse;
ctx.log("Accessing filter for " + httpReqLine(hsr) + " " + hsr.getMethod());
final ByteArrayPrintWriter pw = new ByteArrayPrintWriter();
final boolean[] xformNeeded = new boolean[1];
//STEP 1 – SETUP SPECIAL RESPONSE OBJECT
HttpServletResponse wrappedResp = new HttpServletResponseWrapper(resp) {
public PrintWriter getWriter() { return pw.getWriter(); }
public ServletOutputStream getOutputStream() { return pw.getStream(); }
public void setContentType(String type) {
if (type.equals("text/xml"))
{ ctx.log("Converting xml to html");
resp.setContentType("text/html");
xformNeeded[0] = true; }
else { resp.setContentType(type); }
//STEP 3: set content
}
type of response
};
//STEP 2 – call downstream filter or Servlet
filterChain.doFilter(servletRequest, wrappedResp);
Call chain to get xml data
byte[] bytes = pw.toByteArray();
if (bytes == null || (bytes.length == 0))
{ ctx.log("No content!"); }
if (xformNeeded[0] == true)
{ try {
//Note: This can be _very_ inefficient for large
//transforms such transforms should be pre//calculated.
ByteArrayOutputStream baos = new
STEP
ByteArrayOutputStream();
xform.transform(new StreamSource(new
ByteArrayInputStream(bytes)),
new StreamResult(baos));
4: Translate xml to HTML
byte[] xformBytes = baos.toByteArray(); /*This fixes a bug in the original published
tip, which did not set the content length to the _new_ length implied by the xform. */
resp.setContentLength(xformBytes.length);
resp.getOutputStream().write(xformBytes);
}
}
ctx.log("XML -> HTML conversion completed");
} catch (Exception e)
{ throw new ServletException("Unable to transform document", e); }
else { resp.getOutputStream().write(bytes); }
public void destroy() {
}
Writing out the newly
Transformed xml to html
Data to response
ctx.log("Destroying filter..."); }
public String httpReqLine(HttpServletRequest req) {
StringBuffer ret = req.getRequestURL();
String query = req.getQueryString();
}
if (query != null) { ret.append("?").append(query); }
return ret.toString();
//get header info
public String getHeaders(HttpServletRequest req) throws IOException {
Enumeration en = req.getHeaderNames();
StringBuffer sb = new StringBuffer();
while (en.hasMoreElements()) {
String name = (String) en.nextElement();
sb.append(name).append(": ").append(
req.getHeader(name)).append("\n");
}
return sb.toString();
}
XSLTransform Filter web.xml partial
file
<filter>
<filter-name>XSLT Filter</filter-name>
<filter-class>com.develop.filters.XSLTFilter</filter-class>
<init-param>
<param-name>xslt</param-name>
<!-- Change the param-value to the XSLT you want to use -->
<param-value>/xform2.xsl</param-value>
</init-param>
NOTE: The transform is performed
using XSLT, which is loaded using
the Java API for XML Parsing (JAXP)
TransformerFactory class. Setting up
the transform illustrates another filter
<filter-mapping>
<filter-name>XSLT Filter</filter-name> feature: initialization parameters.
The XSLTFilter expects to be
<url-pattern>/*</url-pattern>
configured with an initialization
</filter-mapping>
parameter named "xslt" that specifies
which transform to run.
</filter>
XSLTransform example….the rest
1.
Make sure that your servlet engine is configured to set
the content type for XML files. In Tomcat you will edit the
{yourTomcat}/conf/web.xml
<!-- add to the list of mime-mappings already present -->
<mime-mapping>
<extension>xml</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
2.
Install some XML and XSL files. The web.xml before
assumes that you are using xform2.xsl and next slide is
an Index.xml to do the filter on. Copy the files to
{YourTomcat}/webapps/examples
Index.xml to do the translation on
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- File Index.xml -->
<tips>
<author id="stu" fullName="Stuart Halloway"/>
<author id="glen" fullName="Glen McCluskey"/>
<tip title="Using the SAX API"
author="stu"
htmlURL="http://java.sun.com/jdc/TechTips/2000/tt0627.html#tip2"
textURL="http://java.sun.com/jdc/TechTips/txtarchive/June00_Stu.txt">
</tip>
<tip title="Random Access for Files"
This is the XML: document
root tag is tips, which has
2 tips in it and 2 authors
author="glen"
htmlURL="http://java.sun.com/jdc/TechTips/2000/tt0509.html#tip1"
textURL="http://java.sun.com/jdc/TechTips/txtarchive/May00_GlenM.txt">
</tip>
</tips>
<!-- File Xform2.xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<HTML><BODY><H1>JDC Tech Tips Archive</H1> <xsl:apply-templates/> </BODY></HTML>
</xsl:template>
<!-- list the title of a tip --> <
xsl:template match="tip">
<br><xsl:apply-templates select="@*"/><xsl:value-of select="@title"/></br>
</xsl:template>
<!-- create a link to any htmlURL -->
<xsl:template match="@htmlURL">
<A HREF="/developer/JDCTechTips/2001/{.}"> HTML </A> |
</xsl:template>
<!-- create a link to any textURL -->
<xsl:template match="@textURL">
<A HREF="/developer/JDCTechTips/2001/{.}"> TEXT </A> |
</xsl:template>
<!-- ignore other attributes -->
<xsl:template match="@*"/>
</xsl:stylesheet>
This is the XSL for use in
translating the xml to html