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 its checkXXX
group of methods.
- The
ProtectionDomain
class, particularly its implies(Permission)
method.
- The key
implies(ProtectionDomain, Permission)
method of the effective Policy
.
- Secondarily, the
implies
methods of CodeSource
, PermissionCollection
, Permission
, and Principal
.
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.auth
SPI for authentication, forget about establishing a standard Subject
as 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 use AccessControlContext
(and suffer the potential performance penalty), for their retrieval.
Subclass SecurityManager
, overriding at least the two checkPermission
methods such that they
- translate, if necessary, the
Permission
argument into something Shiro's PDP (SPDP) understands, prior to
- delegating to the SPDP with the thread-local SNAAs and permission (and throwing a
SecurityException
should 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.
2. Hybrid authorization, combining code origin with context-insensitive SNAAs
- Manage authentication however you see fit; once again associate the Shiro-specific
Subject
with the thread itself.
- Subclass
SecurityManager
, overriding at least the two checkPermission
methods, this time around such that they delegate to both the SPDP and/or the overridden implementation (which in turn calls checkPermission
on, 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
, implementing implies(ProtectionDomain, Permission)
such that, like SecurityManager::checkPermission
above, it passes some intelligible representation of the domain (typically only its CodeSource
) 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 at checkPermission
time. 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 ThreadLocal
in this case.
Subclass, instantiate, and install a Policy
that performs the combined duties of SecurityManager::checkPermission
and Policy::implies
, as individually described in the second case.
Instantiate and install a standard SecurityManager
.
Create a ProtectionDomain
subclass, capable of storing and exposing the SNAAs.
Author1 a DomainCombiner
that
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 the getContext
and checkPermission
AccessController
methods are.
Upon successful authentication, create a new AccessControlContext
that wraps the current one, along with an instance of the custom DomainCombiner
, in turn wrapping the SNAAs. Wrap code to be executed beyond
that point "within" an AccessController::doPrivilegedWithCombiner
invocation, 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 Principal
s 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.