Note: The answer addresses the general case where an external authorization system is to be integrated with the JVM, by means of the standard security framework. It is not Shiro- or JMX-specific, as I am familiar with neither.
Conceptually, it appears that you are after the policy decision point (PDP) — the facility where authorization queries (“is entity X allowed to do Y?”) are evaluated, that is. The JDK offers several of these:
- The effective
SecurityManager, specifically itscheckXXXgroup of methods. - The
ProtectionDomainclass, particularly itsimplies(Permission)method. - The key
implies(ProtectionDomain, Permission)method of the effectivePolicy. - Secondarily, the
impliesmethods ofCodeSource,PermissionCollection,Permission, andPrincipal.
Any of the aforementioned methods may be overridden in order to customize, at ascending granularity, the functionality of the conceptual PDP. It should be noted that JAAS did (contrary to what its name suggests) not really bring its own PDP along; rather, it provided the means for the domain and policy to support principal-based queries, in addition to the original trust factor of code origin. Hence, to my eye, your requirement of remaining “JAAS-compatible” basically translates to wanting to use the (original-plus-JAAS) Java SE authorization model, a.k.a. the sandbox, which I doubt to be what you desire. Frameworks such as Shiro tend to be employed when the standard model is deemed either too low-level and/or performance-intensive; in other words, when authorization logic need not evaluate every single stack frame for a given set of trust factors, due to those factors being more frequently context-insensitive than not. Depending on the validity of my assumption, three main cases arise for examination:
- Authorization is
AccessControlContext-independent. Shiro-native authorization attributes (SNAAs), whatever they are, apply to the entire thread. Code origin is irrelevant. - Code origin matters, mandating use of the sandbox. SNAAs are still
AccessControlContext-independent. - Code origin and SNAAs are both relevant and
AccessControlContext–dependent.
1. Authorization based solely on SNAAs
-
Manage authentication however you see fit. If you wish to continue using JAAS’
javax.security.authSPI for authentication, forget about establishing a standardSubjectas the authentication outcome, instead directly tying the Shiro-specific one to thread-local storage. This way you get more convenient access to the SNAAs, and avoid having to useAccessControlContext(and suffer the potential performance penalty), for their retrieval. -
Subclass
SecurityManager, overriding at least the twocheckPermissionmethods such that they- translate, if necessary, the
Permissionargument into something Shiro’s PDP (SPDP) understands, prior to - delegating to the SPDP with the thread-local SNAAs and permission (and throwing a
SecurityExceptionshould the SPDP signal access denial).
The security context-receiving overload may simply disregard the corresponding argument. At application initialization time, instantiate and install (
System::setSecurityManager) your implementation. - translate, if necessary, the
2. Hybrid authorization, combining code origin with context-insensitive SNAAs
- Manage authentication however you see fit; once again associate the Shiro-specific
Subjectwith the thread itself. - Subclass
SecurityManager, overriding at least the twocheckPermissionmethods, this time around such that they delegate to both the SPDP and/or the overridden implementation (which in turn callscheckPermissionon, accordingly, the current or supplied access control context). Which one(s) and in what order should be consulted for any given permission is of course implementation-dependent. When both are to be invoked, the SPDP should be queried first, since it will likely respond faster than the access control context. - If the SPDP is to additionally handle evaluation of permissions granted to code originating from a certain location and/or set of code signers, you will also have to subclass
Policy, implementingimplies(ProtectionDomain, Permission)such that, likeSecurityManager::checkPermissionabove, it passes some intelligible representation of the domain (typically only itsCodeSource) and permission arguments — but logically not the SNAAs — to the SPDP. The implementation should be efficient to the extent possible, since it will be invoked once per domain per access control context atcheckPermissiontime. Instantiate and install (Policy::setPolicy) your implementation.
3. Hybrid authorization, combining code origin with SNAAs, both context-sensitive
-
Manage authentication however you see fit. Unfortunately the subject handling part is not as trivial as creating a
ThreadLocalin this case. -
Subclass, instantiate, and install a
Policythat performs the combined duties ofSecurityManager::checkPermissionandPolicy::implies, as individually described in the second case. -
Instantiate and install a standard
SecurityManager. -
Create a
ProtectionDomainsubclass, capable of storing and exposing the SNAAs. -
Author1 a
DomainCombinerthat-
is constructed with the SNAAs;
-
implements
combine(ProtectionDomain[], ProtectionDomain[])such that- it replaces the first (the “current” context) array argument’s domains with equivalent instances of the custom implementation;
- then appends the second (the “assigned” or “inherited” context) argument’s ones, if any, to the former as-is; and lastly
- returns the concatenation.
Like
Policy::implies, the implementation should be efficient (e.g. by eliminating duplicates), as it will be invoked every time thegetContextandcheckPermissionAccessControllermethods are. -
-
Upon successful authentication, create a new
AccessControlContextthat wraps the current one, along with an instance of the customDomainCombiner, in turn wrapping the SNAAs. Wrap code to be executed beyond
that point “within” anAccessController::doPrivilegedWithCombinerinvocation, also passing along the replacement access control context.
1 Instead of using custom domains and your own combiner implementation, there is also the seemingly simpler alternative of translating the SNAAs into Principals and, using the standard SubjectDomainCombiner, binding them to the current AccessControlContext‘s domains (as above, or simply via Subject::doAs). Whether this approach reduces the policy’s efficiency depends primarily on the call stack’s depth (how many distinct domains the access control context comprises). Eventually the caching optimizations you thought you could avoid implementing as part of the domain combiner will hit you back when authoring the policy, so this is essentially a design decision you will have to make at that point.