We have happily been using Shiro for some years now for our Spring MVC web
application authentication and authorisation using standard Shiro filters to
login and logout users.
Recently we implemented an asynchronous 'export' feature where a request
launches a background thread to perform a possibly long running ( a few
minutes) task. The request returns a job ID that the client app can use to
interrogate progress. We bind the Subject to the Callable using <code>task
= subject.associateWith(task);</code> and it all works fine, as in the web
application the user generally remains logged in while the export is
We now expose this export feature through an API. Here is where we get a
problem, maybe we are not using Shiro correctly here.
- We have implemented a Realm that checks for access token validity
- we have set noSessionCreation to be active as the API requests are
supposed to be stateless
- if all ok we call SecurityUtils.getSubject().login so that there is an
authenticated principal to do permissions checks on access to resources that
are going to be exported.
- When the API call is finished, in a filter we call
SecurityUtils.getSubject().logout() before returning the response. This has
the effect of setting the principalsCollection to null, so all subsequent
permission lookups fail for the Subject. This means that permission lookups
performed by the background thread now fail.
Does anyone have any suggestions for how to keep a subject usable in a
background thread after logout() has been called? There seem to be several
- not call logout() at the end of each API request. Would this be bad
practice? Would there be some accumulation of Subject or Http Session
instances over time and 000s of API requests?
- stop using Shiro for API permissions lookups - we would prefer to use the
same permissions mechanism for all clients, so this option is not attractive
- Use reflection to set the PrincipalCollection back into the Subject after
calling logout - this seems a bit hacky and potentially fragile
Any advice or examples of using Shiro to secure APIs would be greatly
appreciated, thanks Richard
Sent from: http://shiro-user.582556.n2.nabble.com/
A couple of things stick out:
1.) You shouldn't need to call `subject.login()` directly. This is almost always handled by some framework for you (like the Shiro Servlet Filter). The same can be said for `.logout()` though to a lesser extent.
2.) As for the "logout" issue, I think this is a misuse of logout.
If you truly don't have any session/state, then the "logout" doesn't _really_ have anything to clean up (like a session cache, or container related session)
You _shouldn't_ be logging a user out if you expect the subject to remain active (as you still need the context of the given subject). My guess (based on minimal data) is that you are forcing a logout, because you are also managing the "login"?
Things get a little tricky when you with async tasks though, and the best solution might depend on what you do when the processing is done. Are you emailing the user some results? Does the user poll an endpoint until the job is finished?
If the user does need to logout (and I can see some valid use cases for this anyway, for example, If the job runs for 3 hours and the result is emailed to the user) then there is no reason for a subject to be kept alive.
There are a few ways to work around this, but can you give us some details on your async task? And why you are calling login/logout? (Maybe because you don't have a custom Filter? https://shiro.apache.org/web.html#Web-DefaultFilters)
On Thu, Nov 28, 2019 at 7:38 AM otter606 <[hidden email]> wrote:
On 02 December 2019 at 21:49 Brian Demers <[hidden email]> wrote:
This just seems a good entry point to encapsulate authenticating the token - we have an ApiTokenRealm to authenticate the token and by using this approach we can reuse getAuthorizationInfo() method that we use for username-password logins. Also if authentication fails we want to throw a 401 status rather than redirect to a login page for the web UI which the ShiroServlet filter does
You are probably right, as much as anything it seems symmetrical, if we are logging users in, to log them out again after the response is generated
In the web application, the user can continue using the application while the background job is running. When the job is finished, it notifies the user either by email, in-app messaging or slack depending on preferences.
For API invocations, the client gets a jobId which they can use to query for progress - we implement a thin wrapper around Spring Batch which does the bulk of the job management.
Our application is a specialist content management system for scientific researchers. The background job is an export task which iterates over user's content (typically 10s to 10_000s of items) and generates HTML, PDF or XML exports. The end result is a download link. For each candidate item to export we do a permissions lookup to see if the user has permission to read that item (hence the need for Shiro's permissions lookups).
From what you are saying it sounds like we can just not call logout() for the API calls. But how to handle a user logging out of web application while background process is running? They may have a valid reason to logout(e.g. they are working on a shared computer and they have to leave it unattended) but would not want their background jobs to be stopped/cancelled. Is their some way to clone a Shiro subject and bind it to another thread?
Where are your permission checks located? Can you move to check into the request instead of checking late in the batch process?
Not really - a user's content is a series of documents which can contain links to other documents; also documents belonging to other people that have been shared; these linked /shared documents(that require permission checks) are only identified during the traversal of user's content during the export process.
It's possible we could do some initial scan to check permissions in the request thread, and then switch off permission checking in the background thread, but this initial scan is still likely to take some time and doesn't solve the issue for really large exports where this would probably still be too slow.
Have you thought about using Shiro's "RunAs" feature?
On Tue, Dec 3, 2019 at 3:41 AM Richard Adams <[hidden email]> wrote:
Ah, good idea! We do use that already for admins / support staff to impersonate users, but hadn't thought of its use here - we'll have an explore of how this could work...
|Free forum by Nabble||Edit this page|