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>login_page</tt>, the value should be an absolute
037 path (from the website's document root) to the login page or URL.
038 The following key is optional:
039 <li>key name: <tt>use_cookie</tt>, the value should be <tt>"true"</tt>
040 or <tt>"false"</tt>. <tt>true</tt> means save the original target page
041 as a cookie, <tt>false</tt> means save that page as part of the URL.
042 Defaults to <tt>false</tt>
043 </ul>
044
045 <b>Note:</b> this class cannot be directly instantiated (since it's
046 abstract) and should not be specified as the name of some filter in the
047 servlet container's web.xml file.
048
049 @author hursh jain
050 **/
051 public abstract class AuthFilter implements Filter
052 {
053 private static final boolean dbg = false;
054
055 boolean use_cookie = true;
056 FilterConfig config;
057 ServletContext context;
058 String login_page;
059
060 public void init(FilterConfig config) throws ServletException
061 {
062 this.config = config;
063 this.context = config.getServletContext();
064 login_page = config.getInitParameter("login_page");
065
066 if (login_page == null)
067 throw new ServletException("AuthFilter: the login_page parameter was missing");
068
069 String tmp = config.getInitParameter("use_cookie");
070 if (tmp != null)
071 this.use_cookie = Boolean.valueOf(tmp).booleanValue();
072 }
073
074 public void destroy()
075 {
076 config = null;
077 context = null;
078 }
079
080 public void doFilter (
081 final ServletRequest req, final ServletResponse res, final FilterChain chain)
082 throws ServletException, IOException
083 {
084 if (dbg) System.out.println(">>>AuthFilter: doFilter() START");
085
086 if ( (! (req instanceof HttpServletRequest)) ||
087 (! (res instanceof HttpServletResponse)) )
088 {
089 throw new ServletException("Request type was not HTTP/HTTPS");
090 }
091
092 final HttpServletRequest request = (HttpServletRequest) req;
093 final HttpServletResponse response = (HttpServletResponse) res;
094
095 boolean loggedin = false;
096 try {
097 loggedin = isUserLoggedIn(request, response);
098 }
099 catch (Exception e) {
100 throw new ServletException(e);
101 }
102
103 if (! loggedin )
104 {
105 if (dbg) System.out.println(">>>AuthFilter: user not logged in");
106 showLoginPage(request, response);
107 }
108 else {
109 if (dbg) System.out.println(">>>AuthFilter: user logged in");
110 //only if user is logged in
111 chain.doFilter(req, res);
112 }
113
114 if (dbg) System.out.println(">>>AuthFilter: doFilter() END");
115 }
116
117 /**
118 Redirects the user to a login page. The login page must set login status in
119 some way understandable by invokations of this method.
120 */
121 protected void showLoginPage(
122 final HttpServletRequest req, final HttpServletResponse res)
123 throws ServletException, IOException
124 {
125 /*
126 requestURL gives us the protocol, port, the entire URL
127 (including path info) whereas requestURI would only give
128 us the entire path but not the protocol, port etc. We
129 save the full URL and the query string
130 */
131 final StringBuffer buf = req.getRequestURL();
132
133 final String query = req.getQueryString();
134 if (query != null) {
135 buf.append('?');
136 buf.append(query);
137 }
138
139 //we encode because the login target may itself contain
140 //chars like ? and = etc
141 String login_target = URLEncoder.encode(buf.toString());
142
143 //set target to original request page
144 if (use_cookie) {
145 final Cookie cookie = new Cookie("target", login_target);
146 cookie.setPath("/");
147 cookie.setMaxAge(-1); //only need it for browser session
148 res.addCookie(cookie);
149 if (dbg) System.out.println("setting cookie: " + cookie.getValue());
150 }
151 else {
152 login_page = login_page + "?target=" + login_target;
153 }
154
155 res.sendRedirect(login_page); //login_page NOT target
156 }
157
158 /**
159 This method should somehow check to see if the user is logged in or not.
160 Typically, this will be done via getting a session_id (either from a cookie
161 or a URL) and using that session_id to search for a memory or database
162 session data to see if that session still exists and has not expired.
163 */
164 public abstract boolean isUserLoggedIn(
165 HttpServletRequest req, HttpServletResponse res)
166 throws Exception;
167
168 } //~class AuthFilter
169