001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002// The Molly framework is freely distributable under the terms of an 003// MIT-style license. For details, see the molly pages web site at: 004// http://www.mollypages.org/. Use, modify, have fun ! 005 006package fc.web.servlet; 007 008import java.io.*; 009import java.net.*; 010import javax.servlet.*; 011import javax.servlet.http.*; 012 013import java.util.*; 014 015import fc.io.*; 016import fc.web.*; 017import fc.util.*; 018 019/** 020An abstract servlet filter that only allows authenticated access to a 021resource. This filter will redirect the user to the <tt>login_page</tt> if 022the user is not logged in. The originally requested target URL (if any) 023will be saved via the method. The saved URL will be complete, i.e., will 024start from the protocol string <tt>http..</tt> upto and including any query 025string. Note: if the original URL was submitted via POST, the POST data is 026<u>not</u> saved, only the target URL is saved. 027<p> 028There are 2 ways that the original URL can be saved. One is to save it as a 029temporary cookie. This implies that the client must have cookies enabled. 030The other way is to tack it on to the URL as a url parameter. <u>In either 031case, the original target is saved as a parameter with 032name=<tt>login.target</tt> and value=<tt>URLEncoder.encode(target_page)</tt></u> 033<p> 034The following keys must be specified as the init parameters. 035<ul> 036 <li>key name: <tt>appName</tt>, the name of the webapp (the name is an 037 arbitrary string but must be the same as is specified in the init parameter 038 of {@link WebApp}). 039 <li>key name: <tt>login_page</tt>, the value should be an absolute 040 path (from the website's document root) to the login page or URL. 041The following key is optional: 042 <li>key name: <tt>use_cookie</tt>, the value should be <tt>"true"</tt> 043 or <tt>"false"</tt>. <tt>true</tt> means save the original target page 044 as a cookie, <tt>false</tt> means save that page as part of the URL. 045 Defaults to <tt>false</tt> 046</ul> 047 048<b>Note:</b> this class cannot be directly instantiated (since it's 049abstract) and should not be specified as the name of some filter in the 050servlet container's web.xml file. 051 052@author hursh jain 053**/ 054public abstract class AuthFilter implements Filter 055{ 056private static final boolean dbg = false; 057 058boolean use_cookie = true; 059FilterConfig config; 060ServletContext context; 061String login_page; 062String appName; 063 064public void init(FilterConfig config) throws ServletException 065 { 066 this.config = config; 067 this.context = config.getServletContext(); 068 login_page = config.getInitParameter("login_page"); 069 070 if (login_page == null) 071 throw new ServletException("AuthFilter: the login_page parameter was missing"); 072 073 appName = config.getInitParameter("appName"); 074 if (appName == null) 075 throw new ServletException("AuthFilter: the appName parameter was missing"); 076 077 String tmp = config.getInitParameter("use_cookie"); 078 if (tmp != null) 079 this.use_cookie = Boolean.valueOf(tmp).booleanValue(); 080 } 081 082public void destroy() 083 { 084 config = null; 085 context = null; 086 } 087 088public void doFilter ( 089 final ServletRequest req, final ServletResponse res, final FilterChain chain) 090throws ServletException, IOException 091 { 092 if (dbg) System.out.println(">>>AuthFilter: doFilter() START"); 093 094 if ( (! (req instanceof HttpServletRequest)) || 095 (! (res instanceof HttpServletResponse)) ) 096 { 097 throw new ServletException("Request type was not HTTP/HTTPS"); 098 } 099 100 final HttpServletRequest request = (HttpServletRequest) req; 101 final HttpServletResponse response = (HttpServletResponse) res; 102 103 boolean loggedin = false; 104 try { 105 loggedin = isUserLoggedIn(request, response); 106 } 107 catch (Exception e) { 108 throw new ServletException(e); 109 } 110 111 if (! loggedin ) 112 { 113 if (dbg) System.out.println(">>>AuthFilter: user not logged in"); 114 showLoginPage(request, response); 115 } 116 else { 117 if (dbg) System.out.println(">>>AuthFilter: user logged in"); 118 //only if user is logged in 119 chain.doFilter(req, res); 120 } 121 122 if (dbg) System.out.println(">>>AuthFilter: doFilter() END"); 123 } 124 125/** 126Redirects the user to a login page. The login page must set login status in 127some way understandable by invokations of this method. 128*/ 129protected void showLoginPage( 130 final HttpServletRequest req, final HttpServletResponse res) 131throws ServletException, IOException 132 { 133 /* 134 requestURL gives us the protocol, port, the entire URL 135 (including path info) whereas requestURI would only give 136 us the entire path but not the protocol, port etc. We 137 save the full URL and the query string 138 */ 139 final StringBuffer buf = req.getRequestURL(); 140 141 final String query = req.getQueryString(); 142 if (query != null) { 143 buf.append('?'); 144 buf.append(query); 145 } 146 147 //we encode because the login target may itself contain 148 //chars like ? and = etc 149 String login_target = URLEncoder.encode(buf.toString()); 150 151 //set target to original request page 152 if (use_cookie) { 153 final Cookie cookie = new Cookie("target", login_target); 154 cookie.setPath("/"); 155 cookie.setMaxAge(-1); //only need it for browser session 156 res.addCookie(cookie); 157 if (dbg) System.out.println("setting cookie: " + cookie.getValue()); 158 } 159 else { 160 login_page = login_page + "?target=" + login_target; 161 } 162 163 res.sendRedirect(login_page); //login_page NOT target 164 } 165 166/** 167This method should somehow check to see if the user is logged in or not. 168Typically, this will be done via getting a session_id (either from a cookie 169or a URL) and using that session_id to search for a memory or database 170session data to see if that session still exists and has not expired. 171*/ 172public abstract boolean isUserLoggedIn( 173 HttpServletRequest req, HttpServletResponse res) 174throws Exception; 175 176} //~class AuthFilter 177