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 006 package fc.web.servlet; 007 008 import java.io.*; 009 import java.net.*; 010 import javax.servlet.*; 011 import javax.servlet.http.*; 012 013 import java.util.*; 014 015 import fc.io.*; 016 import fc.web.*; 017 import fc.util.*; 018 019 /** 020 An abstract servlet filter that only allows authenticated access to a 021 resource. This filter will redirect the user to the <tt>login_page</tt> if 022 the user is not logged in. The originally requested target URL (if any) 023 will be saved via the method. The saved URL will be complete, i.e., will 024 start from the protocol string <tt>http..</tt> upto and including any query 025 string. 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> 028 There are 2 ways that the original URL can be saved. One is to save it as a 029 temporary cookie. This implies that the client must have cookies enabled. 030 The other way is to tack it on to the URL as a url parameter. <u>In either 031 case, the original target is saved as a parameter with 032 name=<tt>login.target</tt> and value=<tt>URLEncoder.encode(target_page)</tt></u> 033 <p> 034 The 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. 041 The 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 049 abstract) and should not be specified as the name of some filter in the 050 servlet container's web.xml file. 051 052 @author hursh jain 053 **/ 054 public abstract class AuthFilter implements Filter 055 { 056 private static final boolean dbg = false; 057 058 boolean use_cookie = true; 059 FilterConfig config; 060 ServletContext context; 061 String login_page; 062 String appName; 063 064 public 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 082 public void destroy() 083 { 084 config = null; 085 context = null; 086 } 087 088 public void doFilter ( 089 final ServletRequest req, final ServletResponse res, final FilterChain chain) 090 throws 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 /** 126 Redirects the user to a login page. The login page must set login status in 127 some way understandable by invokations of this method. 128 */ 129 protected void showLoginPage( 130 final HttpServletRequest req, final HttpServletResponse res) 131 throws 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 /** 167 This method should somehow check to see if the user is logged in or not. 168 Typically, this will be done via getting a session_id (either from a cookie 169 or a URL) and using that session_id to search for a memory or database 170 session data to see if that session still exists and has not expired. 171 */ 172 public abstract boolean isUserLoggedIn( 173 HttpServletRequest req, HttpServletResponse res) 174 throws Exception; 175 176 } //~class AuthFilter 177