Can't get ServletContext from Filter

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Can't get ServletContext from Filter

drmike01
I'm relatively new to Shiro (this week), and finding my way around, but have been using servlets for years.  I have a mobile app that interacts with my server via AJAX only that was originally developed using standard servlets, and now I'm implementing security in it using Shiro for the first time.

My problem is that I need to get the database's JNDI name from the web.xml where it is set as a context parameter, and use that at login to do a database update (if you're curious, I need to capture device ID from the app in my central database for Apple notifications).  With a regular Filter, I could have grabbed the ServletContext within the init() method, and pulled the parameter from that easily.  I understand that with Shiro that's not possible (can't override the superclass's init() method), and that I am supposed to use the onFilterConfigSet() method instead, because only there am I assured that ServletContext is available.

What happens, though, is that onFilterConfigSet() never gets called, and if I move the code to within the constructor, I get NullPointerExceptions thrown, presumably because ServletContext isn't available.  I've looked at using WebEnvironment as a possibility, but can't seem to figure out how to get the WebEnvironment without the ServletContext either.

Any help you can provide would be great.  Code segment below.  dbUrl is the JNDI name.  The first logger statement works, but the logger inside of onFilterConfigSet does not. When I call for dbUrl in getDbConnection, the logger statement outputs nothing, and when I use it to look up the DataSource I get a NullPointerException, so it's obviously not being set.

public class LoginFilter extends AuthenticatingFilter {
    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);
    private String dbUrl = null;
   
    public LoginFilter() {
        logger.info("LoginFilter being created");
    }
   
    @Override
    protected void onFilterConfigSet() {
        dbUrl = getServletContext().getInitParameter("dbUrl");
        logger.info("dbUrl set to: " + dbUrl);    
    }

// bunch of other code here (executeLogin, onAccessDenied, etc.)

// code used to get database connection - copied directly from original login servlet
        private Connection getDBConnection(Connection conn) throws Exception {
    try {
            // Datasource Section
            Context init = new InitialContext();
            Context ctx = (Context) init.lookup("java:/comp/env");
            logger.info(dbUrl);
            DataSource source = (DataSource) ctx.lookup(dbUrl);
            conn = source.getConnection();
            return conn;

        } catch (Exception e) {
            logger.error("Error establishing connection", e);
            throw new Exception("Couldn't open connection to database: "
                    + e.getMessage());
        }
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: Can't get ServletContext from Filter

drmike01
I did some debugging of this issue, and what I found was very interesting, and honestly confusing coming from a standard servlet frame of mind.

So AbstractFilter, which is a superclass of my LoginFilter, has an empty protected method called onFilterConfigSet(), put there for subclasses to initialize with, because the init() method there is final. Nowhere between that and AuthenticatingFilter, my filter's direct superclass, touches this method, so I tried to use it to get my servlet context. Incidentally, AbstractShiroFilter, the superclass for both ShiroFilter and the deprecated IniShiroFilter, makes the onFilterConfigSet() method final, however this shouldn't be relevant to me because I don't inherit from them (I'm in the AuthenticatingFilter chain).

What I discovered in debugging, though, is that AbstractFilter's init() and onFilterConfigSet() methods are never actually called within the context of my Filter.  What instead happens is that ShiroFilter's init() gets called, presumably as the only true Filter in my web.xml, and my LoginFilter, although it is ultimately used by the app, never goes through an init() function, and therefore never does the onFilterConfigSet() method. It's essentially a "fake filter" from the standpoint of the servlet container, and Shiro manages its own filter chain without calling inits on any of them.

My web.xml is below (right out of the manual):
  <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher> 
    <dispatcher>FORWARD</dispatcher> 
    <dispatcher>INCLUDE</dispatcher> 
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

And my shiro.ini below:
[main]
myRealm = com.domainname.MydomainJdbcRealm
myRealm.jndiDataSourceName = jdbc/MydomainDB
myRealm.permissionsLookupEnabled=true
myRealm.authenticationQuery = select password from users where email = ?

loginFilter = com.domainname.LoginFilter

# set rememberMe timeframe to 3 days (72 hours = 60 sec x 60 min x 72 hrs)
securityManager.rememberMeManager.cookie.maxAge = 259200

[users]

[roles]

[urls]
/logout = anon
/healthcheck.html = anon
/* = loginFilter

So, my thinking now is that the only way to do what I need to do is to have my own Filter, in addition to ShiroFilter, in the web.xml, and in that do the database query that I need to.  I could pull the WebEnvironment and all the related info that I need from there, because I can grab the ServletContext easily there. Alternately have a servlet that is the destination for login attempts that does this too, but regardless the same idea applies.

Am I missing something here, or is this what I should be expected to do? I had thought that the filters in shiro.ini were regular servlet container filters, especially because their AbstractFilter superclass actually implements the javax.servlet.Filter interface, and I could put them all in there in order to control the sequence of all of my filters better (the manual actually suggests this to be a benefit of using this approach).  If that's not the case, and what I'm seeing above is correct, then from a design standpoint should the filters in the AuthenticatingFilter chain really implement the Filter interface if they don't function in that way? I feel like I'm missing something obvious, though.
Reply | Threaded
Open this post in threaded view
|

Re: Can't get ServletContext from Filter

Les Hazlewood
Administrator
Hi,

Yes, this can be confusing for a first-time Shiro user, and it is
something Shiro could probably improve upon:

Shiro currently uses the javax.servlet.Filter API mostly for filtering
- not really for configuration/initialization.  Most config is
expected to be done in shiro.ini (or spring.xml or Guice .java or
whatever) since these mechanisms are already far more powerful than
web.xml configuration.  So, instead of relying on the
ServletContext/FilterConfig for config, where you have to 'pull'
values from a known location, you can inject them and your code does
not need to know how config functions.  E.g.:

myFilter.someProperty = someValue
...

That being said, it should probably be a configurable option to
propagate the ServletContext to filters declared in shiro.ini.  If you
feel like you'd like this behavior instead of using the other config
mechanisms, please open a Jira issue and we can address it for the
next release.

Note however that the Filter instances in shiro.ini are created when a
ServletContext is available - not when a FilterConfig is available.
This is because the Shiro WebEnvironment starts up via a Servlet
Context Listener - not as a result of the Shiro Filter starting up.
This could be addressed, it's just that no one has asked for it to
function differently than it does currently.

HTH,

--
Les Hazlewood
CTO, Stormpath | http://stormpath.com | 888.391.5282
twitter: @lhazlewood | http://twitter.com/lhazlewood
blog: http://leshazlewood.com
stormpath blog: http://stormpath.com/blog/

On Mon, Apr 16, 2012 at 1:25 PM, drmike01 <[hidden email]> wrote:

> I did some debugging of this issue, and what I found was very interesting,
> and honestly confusing coming from a standard servlet frame of mind.
>
> So AbstractFilter, which is a superclass of my LoginFilter, has an empty
> protected method called onFilterConfigSet(), put there for subclasses to
> initialize with, because the init() method there is final. Nowhere between
> that and AuthenticatingFilter, my filter's direct superclass, touches this
> method, so I tried to use it to get my servlet context. Incidentally,
> AbstractShiroFilter, the superclass for both ShiroFilter and the deprecated
> IniShiroFilter, makes the onFilterConfigSet() method final, however this
> shouldn't be relevant to me because I don't inherit from them (I'm in the
> AuthenticatingFilter chain).
>
> What I discovered in debugging, though, is that AbstractFilter's init() and
> onFilterConfigSet() methods are never actually called within the context of
> my Filter.  What instead happens is that ShiroFilter's init() gets called,
> presumably as the only true Filter in my web.xml, and my LoginFilter,
> although it is ultimately used by the app, never goes through an init()
> function, and therefore never does the onFilterConfigSet() method. It's
> essentially a "fake filter" from the standpoint of the servlet container,
> and Shiro manages its own filter chain without calling inits on any of them.
>
> My web.xml is below (right out of the manual):
>  <listener>
>
> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
> </listener>
> <filter>
>    <filter-name>ShiroFilter</filter-name>
>    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
> </filter>
> <filter-mapping>
>    <filter-name>ShiroFilter</filter-name>
>    <url-pattern>/*</url-pattern>
>    <dispatcher>REQUEST</dispatcher>
>    <dispatcher>FORWARD</dispatcher>
>    <dispatcher>INCLUDE</dispatcher>
>    <dispatcher>ERROR</dispatcher>
> </filter-mapping>
>
> And my shiro.ini below:
> [main]
> myRealm = com.domainname.MydomainJdbcRealm
> myRealm.jndiDataSourceName = jdbc/MydomainDB
> myRealm.permissionsLookupEnabled=true
> myRealm.authenticationQuery = select password from users where email = ?
>
> loginFilter = com.domainname.LoginFilter
>
> # set rememberMe timeframe to 3 days (72 hours = 60 sec x 60 min x 72 hrs)
> securityManager.rememberMeManager.cookie.maxAge = 259200
>
> [users]
>
> [roles]
>
> [urls]
> /logout = anon
> /healthcheck.html = anon
> /* = loginFilter
>
> So, my thinking now is that the only way to do what I need to do is to have
> my own Filter, in addition to ShiroFilter, in the web.xml, and in that do
> the database query that I need to.  I could pull the WebEnvironment and all
> the related info that I need from there, because I can grab the
> ServletContext easily there. Alternately have a servlet that is the
> destination for login attempts that does this too, but regardless the same
> idea applies.
>
> Am I missing something here, or is this what I should be expected to do? I
> had thought that the filters in shiro.ini were regular servlet container
> filters, especially because their AbstractFilter superclass actually
> implements the javax.servlet.Filter interface, and I could put them all in
> there in order to control the sequence of all of my filters better (the
> manual actually suggests this to be a benefit of using this approach).  If
> that's not the case, and what I'm seeing above is correct, then from a
> design standpoint should the filters in the AuthenticatingFilter chain
> really implement the Filter interface if they don't function in that way? I
> feel like I'm missing something obvious, though.
>
> --
> View this message in context: http://shiro-user.582556.n2.nabble.com/Can-t-get-ServletContext-from-Filter-tp7468957p7471676.html
> Sent from the Shiro User mailing list archive at Nabble.com.
Reply | Threaded
Open this post in threaded view
|

Re: Can't get ServletContext from Filter

drmike01
Thanks for the help.  After thinking about it more, it's okay as-is, I just have to think about Shiro in a slightly different way than I was, which is probably more appropriate anyway. Most of that is my being used to rolling my own authc/authz code using Servlets, so thinking about security as a Filter required a couple of days to sink in, and morphing my LoginServlet into a LoginFilter brought with it some application initialization baggage that really could have been elsewhere.

The one thing that I still think is strange, though, is that my onFilterConfigSet() method was never called, even though nowhere in the inheritance of my class was that method declared final, and it comes from a non-final version in AbstractFilter. Instead, the final onFilterConfigSet method in ShiroFilter was called, even though my filter doesn't inherit from there. As you know, ShiroFilter also inherits from AbstractFilter, but down a different inheritance tree. Now that I know this is the way it works I can avoid depending on this method, however I'd by lying if I said I understood why that is, and the Javadoc for AuthenticatingFilter implies that I should be able to override that method in my subclass. I suppose I also could have called it specifically in my init() method, similar to how AbstractFilter does, but it's better the way I have it now.

Like I said, though, it turns out for what I needed I didn't need to do this at all, and just baked the data access that I needed into the destination servlet, which ultimately makes more sense anyway.

Thanks again for the help.