Hi Allan,

When you configure any filter that subclasses AuthenticationFilter,
the Subject must be authenticated in order for the request to be
allowed through the filter chain on to its final destination.  If not
authenticated, the behavior is up to you - AuthenticatingFilter
subclasses will often perform a login attempt if the information can
be found to do so and all others will typically block the request.

I'll cover flow, but let me first show you the relevant parts of
Shiro's Filter hierarchy that helps you automate this:

AdviceFilter( allows AOP-style pre/post/finally 'advice' during filter
chain execution)
|-- PathMatchingFilter (allows filter to react to path-specific config
during a request)
    |-- AccessControlFilter (allows or denies a request based on some
Subject state)
        |-- AuthenticationFilter (specifically allows or denies a
request based on authentication state)
            |-- AuthenticatingFilter (can automatically perform a
login if not authenticated)

So, for AuthenticatingFilter instances, the flow works as followed:

1.  The AdviceFilter's onPreHandle method is invoked.  This is the
AOP-style method that is invoked to determine if the chain execution
should occur at all.
2.  The AccessControlFilter implementation of onPreHandle performs a check:
    if (isAccessAllowed(...)) {
        return true; //allow the request to continue
    }
    //otherwise access isn't allowed,
    //allow the filter to react to the denial:
    return onAccessDenied(...);

3.  For AuthenticationFilter instances, the isAccessAllowed method
implementation returns true if Subject.isAuthenticated(), false
otherwise.
4.  For AuthenticatingFilter instances, the onAccessDenied method
returns false (as expected, because the user isn't authenticated),
_unless_ it wishes to perform an authentication attempt based on the
current request.

It is often convenient to automatically perform an authentication
attempt during onAccessDenied, probably because you've determined that
although they're not yet authenticated, they're currently accessing
the authentication url and have provided proper principals/credentials
to authenticate.  If the url being accessed *does not* represent a
current authentication attempt however, the onAccessDenied method
should return false as expected to prevent the filter chain from
continuing.  You can also set headers and do other things in this case
too to indicate that the caller isn't authenticated and shouldn't be
accessing the URL until they log in (e.g. set HTTP 401 response code).

The HttpBasicAuthenticationFilter is a good example of this 'if
they're trying to login, then login, if not, deny access and challenge
for authentication' workflow.  For example, its onAccessDeniedMethod
looks like the following:

protected boolean onAccessDenied(ServletRequest request,
ServletResponse response) throws Exception {
    boolean loggedIn = false; //false by default or we wouldn't be in
this method
    if (isLoginAttempt(request, response)) {
        loggedIn = executeLogin(request, response);
    }
    if (!loggedIn) {
        sendChallenge(request, response);
    }
    return loggedIn;
}

The isLoginAttempt method and sendChallenge methods are specific to
that class, but I'm assuming you'll want to do similar things based on
your custom XML mechanisms.  The executeLogin method however is
already written and available from the AuthenticatingFilter
superclass, so you won't have to write that (executeLogin calls
createToken, so make sure your createToken method returns a valid
AuthenticationToken based on the inbound request).

That should explain the flow.  It sounds like all you'll need to do is
implement createToken and onAccessDenied to perform the 'auto login or
else deny' convenience workflow.

As for Realm authentication vs authorization, they two operations are
totally orthogonal.  Authentication can occur completely independent
of authorization and vice versa.

If you subclass AuthorizingRealm, most of the authc/authz logic is
written for you, you just need to provide the raw authc/authz data so
the superclass implementations can execute that logic.

The doGetAuthenticationInfo method returns information from your
Realm's data source that is only specific to authentication -
typically a username/password pair or some other principal/credential
pair (biometric, etc).  That's it.  Either the submitted
AuthenticationToken's credentials match the AuthenticationInfo
returned from the datasource (in which case the authentication is
considered successful), or they don't match, in which case the attempt
is considered failed.  The matching is performed separately by a
pluggable CredentialsMatcher component that is used by all
AuthenticatingRealm subclasses.

The doGetAuthorizationInfo method functions in the exact same way, but
instead of providing principals/credentials for authc matching, it
returns any roles and/or permissions attributed to the subject.  The
parent class can check the returned AuthorizationInfo to perform the
authorization logic.

The reason why these two are completely separate makes sense when you
see that they can happen independently at runtime.  Authorization only
needs a Subject identity to perform authz checks.  For example, when a
user is remembered from RememberMe services, they're not considered
authenticated (because they haven't proven their identity), but Shiro
can still use that remembered identity to perform authorization
checks.

If your Realm's doGetAuthorizationInfo always returns null, then that
effectively disables authorization for that realm and any role or
permission check delegated to that realm will always return 'false' by
the parent AuthorizingRealm subclass logic.  See the
AuthorizingRealm's getAuthorizationInfo JavaDoc for detailed
explanation on how this works.

Note that authz caching is recommended for big performance benefits
and authc caching will work in 1.2 (in trunk only at the moment).  If
you configure Shiro with a CacheManager, you'll receive these
benefits.  When a CacheManager is configured, authz caching is enabled
automatically by default, but authc caching must still be turned on
explicitly.

HTH!

-- 
Les Hazlewood
CTO, Katasoft | http://www.katasoft.com | 888.391.5282
twitter: http://twitter.com/lhazlewood
katasoft blog: http://www.katasoft.com/blogs/lhazlewood
personal blog: http://leshazlewood.com

Reply via email to