You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@shiro.apache.org by Tauren Mills <ta...@groovee.com> on 2011/06/06 22:08:00 UTC

Tracking last access dates

My application currently tracks the last login date of a user, but
doesn't keep track of the last accessed date. I don't really like the
way I implemented this long ago and would like some advice before
refactoring things.

Currently I'm doing this in my AuthorizingRealm to update when a user
manually logs in:

protected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken authcToken) throws
AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
    Member member = memberService.findMember(token.getUsername());
    if (member != null) {
	memberService.updateAccessed(member);
       	return new SimpleAuthenticationInfo(member.getId(),
member.getPassword(), getName());
    }
    return null;
}

This is in my CookieRememberMeManager to track remembered logins:

@Override
public PrincipalCollection getRememberedPrincipals(SubjectContext
subjectContext) {
	PrincipalCollection principals = super.getRememberedPrincipals(subjectContext);
	if ( principals != null ) {
		Long id = (Long) principals.getPrimaryPrincipal();
		memberService.updateAccessed(id);
	}
	return principals;
}

Hibernate is used to update the member's accessed date in MemberService:

public void updateAccessed(Member member) {
	log.info("Updating accessed date for "+member.getLogInfo());
	member.setAccessed(new Date());
	memberDao.save(member);
}

So currently, I'm really only storing the last *login* date, not the
last *accessed* date. I'd like to store the last accessed date.

I asked a similar question before, but it was long ago before the
Shiro 1.0 release, so I think things may have changed. At the time I
was advised to utilize SessionListener for some aspects of this. Now
that I'm looking at it again, it seems to me that I should pull the
memberService.updateAccess() calls out of my Realm and
RememberMeManager and put them into an AuthenticationListener and/or
SessionListener.

How do I best go about finding and saving the last accessed date? Are
all of the following statements accurate?

* SessionListener.onStart() happens when a session starts, but it
doesn't yet know WHO started that session. So it really doesn't help
me.

* SessionListener.onStop() and onExpiration() could be used to save
the last accessed time to the Member's table.

* AuthenticationListener.onSuccess() could be used to save the time a
user authenticates, but this doesn't help for rememberme logins.

* AuthenticationListener.onLogout() could be used to save the time a
user logs out, but won't help for sessions that time out.

* Should there be a SessionListener.onUserAssociated() method? Les
suggested I add a Jira for this, but that was long ago. Is there an
alternative solution now, or does this still make sense to add?

* Would it be possible for something like SessionListener.onAccess()
to be implemented?

Basically, I'd like to know what the best solution would be to always
maintain the last accessed date of a user, not just the last login
date.

Thanks!
Tauren

Re: Tracking last access dates

Posted by Kalle Korhonen <ka...@gmail.com>.
Using a filter would work whether or not you are using native sessions
(shiro wraps the request) if your filter executes after the shiro
filter. If you have a web-only application, I'd just use the existing
APIs meant for these purposes (sessionlistener, filters etc.). If you
had an application that used multiple communication protocols (HTTP,
JDBC, JMS, etc. etc.) only then I'd consider using some other
approaches (AOP if all else fails) to consolidate the logic into a
single module.

Kalle


On Mon, Jun 13, 2011 at 6:22 PM, Tauren Mills <ta...@groovee.com> wrote:
> This sounds like a great approach, and it totally reminded me that I'm
> using shiro native sessions, not standard web sessions. Am I correct
> in thinking that the suggestion Kalle made is best suited for apps
> that do not use native sessions and use standard HttpSessions?
> Although my app is web-based, wouldn't it be better to track the last
> accessed value from somewhere other than a filter since I'm using
> native sessions?
>
> If I go this route, I'm still not sure where the best place to add my
> session access logic would be. I like Kalle's suggestion of only
> updating the datastore if a sufficiently long period of time has
> passed, but where would this logic go? He's suggesting it go into a
> filter, but with native sessions where would it go?
>
> Thanks for the ideas,
> Tauren
>
>
> On Mon, Jun 13, 2011 at 2:42 PM, Les Hazlewood <lh...@apache.org> wrote:
>> I don't have much time to elaborate at the moment, but I like the
>> approach of using Shiro's native session mechanisms - I store the
>> Sessions in a data store I control that has query capabilities (write
>> a Shiro SessionDAO to talk to the data store).  When a user logs in, I
>> associate their session to their user account (the session records
>> have a 'userId' field).  This way, I can query the session data store
>> for all sessions associated with a particular user including the one
>> they're currently using...
>>
>> 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
>>
>

Re: Tracking last access dates

Posted by Tauren Mills <ta...@groovee.com>.
This sounds like a great approach, and it totally reminded me that I'm
using shiro native sessions, not standard web sessions. Am I correct
in thinking that the suggestion Kalle made is best suited for apps
that do not use native sessions and use standard HttpSessions?
Although my app is web-based, wouldn't it be better to track the last
accessed value from somewhere other than a filter since I'm using
native sessions?

If I go this route, I'm still not sure where the best place to add my
session access logic would be. I like Kalle's suggestion of only
updating the datastore if a sufficiently long period of time has
passed, but where would this logic go? He's suggesting it go into a
filter, but with native sessions where would it go?

Thanks for the ideas,
Tauren


On Mon, Jun 13, 2011 at 2:42 PM, Les Hazlewood <lh...@apache.org> wrote:
> I don't have much time to elaborate at the moment, but I like the
> approach of using Shiro's native session mechanisms - I store the
> Sessions in a data store I control that has query capabilities (write
> a Shiro SessionDAO to talk to the data store).  When a user logs in, I
> associate their session to their user account (the session records
> have a 'userId' field).  This way, I can query the session data store
> for all sessions associated with a particular user including the one
> they're currently using...
>
> 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
>

Re: Tracking last access dates

Posted by Les Hazlewood <lh...@apache.org>.
I don't have much time to elaborate at the moment, but I like the
approach of using Shiro's native session mechanisms - I store the
Sessions in a data store I control that has query capabilities (write
a Shiro SessionDAO to talk to the data store).  When a user logs in, I
associate their session to their user account (the session records
have a 'userId' field).  This way, I can query the session data store
for all sessions associated with a particular user including the one
they're currently using...

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

Re: Tracking last access dates

Posted by Tauren Mills <ta...@groovee.com>.
Kalle,

Thanks for the suggestion, and you bring up a good point about not
tying this to the security implementation. I was just thinking that
Shiro provides almost everything needed that it wouldn't much of a
stretch to provide this capability as well. I'll explore your ideas
further.

Tauren


On Tue, Jun 7, 2011 at 5:09 PM, Kalle Korhonen
<ka...@gmail.com> wrote:
> How about this: write a filter that keeps track of most recent users
> (e.g. an LRUMap) and when last access time was last stored
> persistently, compares that timestamp to
> HttpSession.getLastAccessedTime() and re-writes to database if
> sufficiently long time has passed (e.g. > 15 mins) to keep it
> performing well. Also update last access time on on session expiration
> (from SessionListener). I wouldn't try to tie this too tightly with
> your security implementation (request.getRemoteUser() seems to be all
> you need if you want to track authenticated users only).
>
> Kalle
>
>
> On Tue, Jun 7, 2011 at 4:44 PM, Tauren Mills <ta...@groovee.com> wrote:
>> Les,
>>
>> Thanks for the help. I voted for SHIRO-286. However, now I have a few
>> more questions.
>>
>> How would you keep the database up-to-date with current access times?
>> Without a SessionListener.onAccess() method or something similar, the
>> only time the database will get updated is when the user logs in, logs
>> out, or their session times out. What about all of the time
>> in-between? Some users can spend all day using the site, but never hit
>> my 30 minute session timeout.
>>
>> Also, I'm concerned that code to update the database with the last
>> accessed date is going to be scattered all over. It would be nice if
>> it could all be done inside listener methods, but I don't see how that
>> is possible for remember me cases:
>>
>> When logging in:
>> * AuthenticationListener.onSuccess()
>> * OR AuthorizingRealm.doGetAuthenticationInfo()
>>
>> When being remembered:
>> * CookieRememberMeManager.getRememberedPrincipals()
>> * OR ???Listener???
>>
>> When logging out:
>> * AuthenticationListener.onLogout()
>>
>> When session expiring:
>> * SessionListener.onExpiration()
>> * OR? SessionListener.onStop()
>>
>> When session is accessed by subject:
>> * ???
>>
>> If you were implementing this, where would you put the code to update
>> the database and keep track of last accessed dates?
>>
>> Note that in reality, I don't need exact last accessed dates, but I
>> need them with at least 30 minutes of accuracy. My UI displays this
>> information from a hibernate query, so it is probably easiest to
>> update the database regularly instead of combining the hibernate query
>> results with shiro's session store information. It would get messy to
>> go through the result set, check if each user has an active shiro
>> session, and update the output to show that user's
>> session.getLastAccessTime.
>>
>> Thanks,
>> Tauren
>>
>>
>>
>> On Mon, Jun 6, 2011 at 2:54 PM, Les Hazlewood <lh...@apache.org> wrote:
>>> Hi Tauren,
>>>
>>>> How do I best go about finding and saving the last accessed date? Are
>>>> all of the following statements accurate?
>>>>
>>>> * SessionListener.onStart() happens when a session starts, but it
>>>> doesn't yet know WHO started that session. So it really doesn't help
>>>> me.
>>>
>>> Correct - that just lets you know that a session has been started, but
>>> a user isn't associated at that point.
>>>
>>>> * SessionListener.onStop() and onExpiration() could be used to save
>>>> the last accessed time to the Member's table.
>>>
>>> Correct, this is probably the best approach since you can call
>>> session.getLastAccessTime during one of those methods.  This sounds
>>> like the best approach.
>>>
>>>> * AuthenticationListener.onSuccess() could be used to save the time a
>>>> user authenticates, but this doesn't help for rememberme logins.
>>>
>>> Correct - AuthenticationListeners are only triggered on
>>> authentication.  RememberMe isn't valid authentication.
>>>
>>>> * AuthenticationListener.onLogout() could be used to save the time a
>>>> user logs out, but won't help for sessions that time out.
>>>
>>> Correct.
>>>
>>>> * Should there be a SessionListener.onUserAssociated() method? Les
>>>> suggested I add a Jira for this, but that was long ago. Is there an
>>>> alternative solution now, or does this still make sense to add?
>>>
>>> I think it might be better to represent this in the form of a
>>> SubjectListener: https://issues.apache.org/jira/browse/SHIRO-286
>>>
>>> Please vote for it if you'd like it implemented sooner rather than
>>> later (While I can't speak for other Shiro devs, I personally pay
>>> attention to the vote count as an indicator of community need).
>>>
>>> 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
>>>
>>
>

Re: Tracking last access dates

Posted by Kalle Korhonen <ka...@gmail.com>.
How about this: write a filter that keeps track of most recent users
(e.g. an LRUMap) and when last access time was last stored
persistently, compares that timestamp to
HttpSession.getLastAccessedTime() and re-writes to database if
sufficiently long time has passed (e.g. > 15 mins) to keep it
performing well. Also update last access time on on session expiration
(from SessionListener). I wouldn't try to tie this too tightly with
your security implementation (request.getRemoteUser() seems to be all
you need if you want to track authenticated users only).

Kalle


On Tue, Jun 7, 2011 at 4:44 PM, Tauren Mills <ta...@groovee.com> wrote:
> Les,
>
> Thanks for the help. I voted for SHIRO-286. However, now I have a few
> more questions.
>
> How would you keep the database up-to-date with current access times?
> Without a SessionListener.onAccess() method or something similar, the
> only time the database will get updated is when the user logs in, logs
> out, or their session times out. What about all of the time
> in-between? Some users can spend all day using the site, but never hit
> my 30 minute session timeout.
>
> Also, I'm concerned that code to update the database with the last
> accessed date is going to be scattered all over. It would be nice if
> it could all be done inside listener methods, but I don't see how that
> is possible for remember me cases:
>
> When logging in:
> * AuthenticationListener.onSuccess()
> * OR AuthorizingRealm.doGetAuthenticationInfo()
>
> When being remembered:
> * CookieRememberMeManager.getRememberedPrincipals()
> * OR ???Listener???
>
> When logging out:
> * AuthenticationListener.onLogout()
>
> When session expiring:
> * SessionListener.onExpiration()
> * OR? SessionListener.onStop()
>
> When session is accessed by subject:
> * ???
>
> If you were implementing this, where would you put the code to update
> the database and keep track of last accessed dates?
>
> Note that in reality, I don't need exact last accessed dates, but I
> need them with at least 30 minutes of accuracy. My UI displays this
> information from a hibernate query, so it is probably easiest to
> update the database regularly instead of combining the hibernate query
> results with shiro's session store information. It would get messy to
> go through the result set, check if each user has an active shiro
> session, and update the output to show that user's
> session.getLastAccessTime.
>
> Thanks,
> Tauren
>
>
>
> On Mon, Jun 6, 2011 at 2:54 PM, Les Hazlewood <lh...@apache.org> wrote:
>> Hi Tauren,
>>
>>> How do I best go about finding and saving the last accessed date? Are
>>> all of the following statements accurate?
>>>
>>> * SessionListener.onStart() happens when a session starts, but it
>>> doesn't yet know WHO started that session. So it really doesn't help
>>> me.
>>
>> Correct - that just lets you know that a session has been started, but
>> a user isn't associated at that point.
>>
>>> * SessionListener.onStop() and onExpiration() could be used to save
>>> the last accessed time to the Member's table.
>>
>> Correct, this is probably the best approach since you can call
>> session.getLastAccessTime during one of those methods.  This sounds
>> like the best approach.
>>
>>> * AuthenticationListener.onSuccess() could be used to save the time a
>>> user authenticates, but this doesn't help for rememberme logins.
>>
>> Correct - AuthenticationListeners are only triggered on
>> authentication.  RememberMe isn't valid authentication.
>>
>>> * AuthenticationListener.onLogout() could be used to save the time a
>>> user logs out, but won't help for sessions that time out.
>>
>> Correct.
>>
>>> * Should there be a SessionListener.onUserAssociated() method? Les
>>> suggested I add a Jira for this, but that was long ago. Is there an
>>> alternative solution now, or does this still make sense to add?
>>
>> I think it might be better to represent this in the form of a
>> SubjectListener: https://issues.apache.org/jira/browse/SHIRO-286
>>
>> Please vote for it if you'd like it implemented sooner rather than
>> later (While I can't speak for other Shiro devs, I personally pay
>> attention to the vote count as an indicator of community need).
>>
>> 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
>>
>

Re: Tracking last access dates

Posted by Tauren Mills <ta...@groovee.com>.
Les,

Thanks for the help. I voted for SHIRO-286. However, now I have a few
more questions.

How would you keep the database up-to-date with current access times?
Without a SessionListener.onAccess() method or something similar, the
only time the database will get updated is when the user logs in, logs
out, or their session times out. What about all of the time
in-between? Some users can spend all day using the site, but never hit
my 30 minute session timeout.

Also, I'm concerned that code to update the database with the last
accessed date is going to be scattered all over. It would be nice if
it could all be done inside listener methods, but I don't see how that
is possible for remember me cases:

When logging in:
* AuthenticationListener.onSuccess()
* OR AuthorizingRealm.doGetAuthenticationInfo()

When being remembered:
* CookieRememberMeManager.getRememberedPrincipals()
* OR ???Listener???

When logging out:
* AuthenticationListener.onLogout()

When session expiring:
* SessionListener.onExpiration()
* OR? SessionListener.onStop()

When session is accessed by subject:
* ???

If you were implementing this, where would you put the code to update
the database and keep track of last accessed dates?

Note that in reality, I don't need exact last accessed dates, but I
need them with at least 30 minutes of accuracy. My UI displays this
information from a hibernate query, so it is probably easiest to
update the database regularly instead of combining the hibernate query
results with shiro's session store information. It would get messy to
go through the result set, check if each user has an active shiro
session, and update the output to show that user's
session.getLastAccessTime.

Thanks,
Tauren



On Mon, Jun 6, 2011 at 2:54 PM, Les Hazlewood <lh...@apache.org> wrote:
> Hi Tauren,
>
>> How do I best go about finding and saving the last accessed date? Are
>> all of the following statements accurate?
>>
>> * SessionListener.onStart() happens when a session starts, but it
>> doesn't yet know WHO started that session. So it really doesn't help
>> me.
>
> Correct - that just lets you know that a session has been started, but
> a user isn't associated at that point.
>
>> * SessionListener.onStop() and onExpiration() could be used to save
>> the last accessed time to the Member's table.
>
> Correct, this is probably the best approach since you can call
> session.getLastAccessTime during one of those methods.  This sounds
> like the best approach.
>
>> * AuthenticationListener.onSuccess() could be used to save the time a
>> user authenticates, but this doesn't help for rememberme logins.
>
> Correct - AuthenticationListeners are only triggered on
> authentication.  RememberMe isn't valid authentication.
>
>> * AuthenticationListener.onLogout() could be used to save the time a
>> user logs out, but won't help for sessions that time out.
>
> Correct.
>
>> * Should there be a SessionListener.onUserAssociated() method? Les
>> suggested I add a Jira for this, but that was long ago. Is there an
>> alternative solution now, or does this still make sense to add?
>
> I think it might be better to represent this in the form of a
> SubjectListener: https://issues.apache.org/jira/browse/SHIRO-286
>
> Please vote for it if you'd like it implemented sooner rather than
> later (While I can't speak for other Shiro devs, I personally pay
> attention to the vote count as an indicator of community need).
>
> 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
>

Re: Tracking last access dates

Posted by Les Hazlewood <lh...@apache.org>.
Hi Tauren,

> How do I best go about finding and saving the last accessed date? Are
> all of the following statements accurate?
>
> * SessionListener.onStart() happens when a session starts, but it
> doesn't yet know WHO started that session. So it really doesn't help
> me.

Correct - that just lets you know that a session has been started, but
a user isn't associated at that point.

> * SessionListener.onStop() and onExpiration() could be used to save
> the last accessed time to the Member's table.

Correct, this is probably the best approach since you can call
session.getLastAccessTime during one of those methods.  This sounds
like the best approach.

> * AuthenticationListener.onSuccess() could be used to save the time a
> user authenticates, but this doesn't help for rememberme logins.

Correct - AuthenticationListeners are only triggered on
authentication.  RememberMe isn't valid authentication.

> * AuthenticationListener.onLogout() could be used to save the time a
> user logs out, but won't help for sessions that time out.

Correct.

> * Should there be a SessionListener.onUserAssociated() method? Les
> suggested I add a Jira for this, but that was long ago. Is there an
> alternative solution now, or does this still make sense to add?

I think it might be better to represent this in the form of a
SubjectListener: https://issues.apache.org/jira/browse/SHIRO-286

Please vote for it if you'd like it implemented sooner rather than
later (While I can't speak for other Shiro devs, I personally pay
attention to the vote count as an indicator of community need).

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