View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.realm;
20  
21  import org.apache.shiro.authc.credential.CredentialsMatcher;
22  import org.apache.shiro.authz.*;
23  import org.apache.shiro.authz.permission.*;
24  import org.apache.shiro.cache.Cache;
25  import org.apache.shiro.cache.CacheManager;
26  import org.apache.shiro.subject.PrincipalCollection;
27  import org.apache.shiro.util.CollectionUtils;
28  import org.apache.shiro.util.Initializable;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import java.util.*;
33  import java.util.concurrent.atomic.AtomicInteger;
34  
35  
36  /**
37   * An {@code AuthorizingRealm} extends the {@code AuthenticatingRealm}'s capabilities by adding Authorization
38   * (access control) support.
39   * <p/>
40   * This implementation will perform all role and permission checks automatically (and subclasses do not have to
41   * write this logic) as long as the
42   * {@link #getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} method returns an
43   * {@link AuthorizationInfo}.  Please see that method's JavaDoc for an in-depth explanation.
44   * <p/>
45   * If you find that you do not want to utilize the {@link AuthorizationInfo AuthorizationInfo} construct,
46   * you are of course free to subclass the {@link AuthenticatingRealm AuthenticatingRealm} directly instead and
47   * implement the remaining Realm interface methods directly.  You might do this if you want have better control
48   * over how the Role and Permission checks occur for your specific data source.  However, using AuthorizationInfo
49   * (and its default implementation {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}) is sufficient in the large
50   * majority of Realm cases.
51   *
52   * @see org.apache.shiro.authz.SimpleAuthorizationInfo
53   * @since 0.2
54   */
55  public abstract class AuthorizingRealm extends AuthenticatingRealm
56          implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
57  
58      //TODO - complete JavaDoc
59  
60      /*-------------------------------------------
61      |             C O N S T A N T S             |
62      ============================================*/
63      private static final Logger log = LoggerFactory.getLogger(AuthorizingRealm.class);
64  
65      /**
66       * The default suffix appended to the realm name for caching AuthorizationInfo instances.
67       */
68      private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache";
69  
70      private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
71  
72      /*-------------------------------------------
73      |    I N S T A N C E   V A R I A B L E S    |
74      ============================================*/
75      /**
76       * The cache used by this realm to store AuthorizationInfo instances associated with individual Subject principals.
77       */
78      private boolean authorizationCachingEnabled;
79      private Cache<Object, AuthorizationInfo> authorizationCache;
80      private String authorizationCacheName;
81  
82      private PermissionResolver permissionResolver;
83  
84      private RolePermissionResolver permissionRoleResolver;
85  
86      /*-------------------------------------------
87      |         C O N S T R U C T O R S           |
88      ============================================*/
89  
90      public AuthorizingRealm() {
91          this(null, null);
92      }
93  
94      public AuthorizingRealm(CacheManager cacheManager) {
95          this(cacheManager, null);
96      }
97  
98      public AuthorizingRealm(CredentialsMatcher matcher) {
99          this(null, matcher);
100     }
101 
102     public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
103         super();
104         if (cacheManager != null) setCacheManager(cacheManager);
105         if (matcher != null) setCredentialsMatcher(matcher);
106 
107         this.authorizationCachingEnabled = true;
108         this.permissionResolver = new WildcardPermissionResolver();
109 
110         int instanceNumber = INSTANCE_COUNT.getAndIncrement();
111         this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
112         if (instanceNumber > 0) {
113             this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
114         }
115     }
116 
117     /*-------------------------------------------
118     |  A C C E S S O R S / M O D I F I E R S    |
119     ============================================*/
120 
121     public void setName(String name) {
122         super.setName(name);
123         String authzCacheName = this.authorizationCacheName;
124         if (authzCacheName != null && authzCacheName.startsWith(getClass().getName())) {
125             //get rid of the default class-name based cache name.  Create a more meaningful one
126             //based on the application-unique Realm name:
127             this.authorizationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
128         }
129     }
130 
131     public void setAuthorizationCache(Cache<Object, AuthorizationInfo> authorizationCache) {
132         this.authorizationCache = authorizationCache;
133     }
134 
135     public Cache<Object, AuthorizationInfo> getAuthorizationCache() {
136         return this.authorizationCache;
137     }
138 
139     public String getAuthorizationCacheName() {
140         return authorizationCacheName;
141     }
142 
143     @SuppressWarnings({"UnusedDeclaration"})
144     public void setAuthorizationCacheName(String authorizationCacheName) {
145         this.authorizationCacheName = authorizationCacheName;
146     }
147 
148     /**
149      * Returns {@code true} if authorization caching should be utilized if a {@link CacheManager} has been
150      * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
151      * <p/>
152      * The default value is {@code true}.
153      *
154      * @return {@code true} if authorization caching should be utilized, {@code false} otherwise.
155      */
156     public boolean isAuthorizationCachingEnabled() {
157         return isCachingEnabled() && authorizationCachingEnabled;
158     }
159 
160     /**
161      * Sets whether or not authorization caching should be utilized if a {@link CacheManager} has been
162      * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
163      * <p/>
164      * The default value is {@code true}.
165      *
166      * @param authenticationCachingEnabled the value to set
167      */
168     @SuppressWarnings({"UnusedDeclaration"})
169     public void setAuthorizationCachingEnabled(boolean authenticationCachingEnabled) {
170         this.authorizationCachingEnabled = authenticationCachingEnabled;
171         if (authenticationCachingEnabled) {
172             setCachingEnabled(true);
173         }
174     }
175 
176     public PermissionResolver getPermissionResolver() {
177         return permissionResolver;
178     }
179 
180     public void setPermissionResolver(PermissionResolver permissionResolver) {
181         if (permissionResolver == null) throw new IllegalArgumentException("Null PermissionResolver is not allowed");
182         this.permissionResolver = permissionResolver;
183     }
184 
185     public RolePermissionResolver getRolePermissionResolver() {
186         return permissionRoleResolver;
187     }
188 
189     public void setRolePermissionResolver(RolePermissionResolver permissionRoleResolver) {
190         this.permissionRoleResolver = permissionRoleResolver;
191     }
192 
193     /*--------------------------------------------
194     |               M E T H O D S               |
195     ============================================*/
196 
197     /**
198      * Initializes this realm and potentially enables a cache, depending on configuration.
199      * <p/>
200      * When this method is called, the following logic is executed:
201      * <ol>
202      * <li>If the {@link #setAuthorizationCache cache} property has been set, it will be
203      * used to cache the AuthorizationInfo objects returned from {@link #getAuthorizationInfo}
204      * method invocations.
205      * All future calls to {@code getAuthorizationInfo} will attempt to use this cache first
206      * to alleviate any potentially unnecessary calls to an underlying data store.</li>
207      * <li>If the {@link #setAuthorizationCache cache} property has <b>not</b> been set,
208      * the {@link #setCacheManager cacheManager} property will be checked.
209      * If a {@code cacheManager} has been set, it will be used to create an authorization
210      * {@code cache}, and this newly created cache which will be used as specified in #1.</li>
211      * <li>If neither the {@link #setAuthorizationCache (org.apache.shiro.cache.Cache) cache}
212      * or {@link #setCacheManager(org.apache.shiro.cache.CacheManager) cacheManager}
213      * properties are set, caching will be disabled and authorization look-ups will be delegated to
214      * subclass implementations for each authorization check.</li>
215      * </ol>
216      */
217     protected void onInit() {
218         super.onInit();
219         //trigger obtaining the authorization cache if possible
220         getAvailableAuthorizationCache();
221     }
222 
223     protected void afterCacheManagerSet() {
224         super.afterCacheManagerSet();
225         //trigger obtaining the authorization cache if possible
226         getAvailableAuthorizationCache();
227     }
228 
229     private Cache<Object, AuthorizationInfo> getAuthorizationCacheLazy() {
230 
231         if (this.authorizationCache == null) {
232 
233             if (log.isDebugEnabled()) {
234                 log.debug("No authorizationCache instance set.  Checking for a cacheManager...");
235             }
236 
237             CacheManager cacheManager = getCacheManager();
238 
239             if (cacheManager != null) {
240                 String cacheName = getAuthorizationCacheName();
241                 if (log.isDebugEnabled()) {
242                     log.debug("CacheManager [" + cacheManager + "] has been configured.  Building " +
243                             "authorization cache named [" + cacheName + "]");
244                 }
245                 this.authorizationCache = cacheManager.getCache(cacheName);
246             } else {
247                 if (log.isDebugEnabled()) {
248                     log.debug("No cache or cacheManager properties have been set.  Authorization cache cannot " +
249                             "be obtained.");
250                 }
251             }
252         }
253 
254         return this.authorizationCache;
255     }
256 
257     private Cache<Object, AuthorizationInfo> getAvailableAuthorizationCache() {
258         Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
259         if (cache == null && isAuthorizationCachingEnabled()) {
260             cache = getAuthorizationCacheLazy();
261         }
262         return cache;
263     }
264 
265     /**
266      * Returns an account's authorization-specific information for the specified {@code principals},
267      * or {@code null} if no account could be found.  The resulting {@code AuthorizationInfo} object is used
268      * by the other method implementations in this class to automatically perform access control checks for the
269      * corresponding {@code Subject}.
270      * <p/>
271      * This implementation obtains the actual {@code AuthorizationInfo} object from the subclass's
272      * implementation of
273      * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) doGetAuthorizationInfo}, and then
274      * caches it for efficient reuse if caching is enabled (see below).
275      * <p/>
276      * Invocations of this method should be thought of as completely orthogonal to acquiring
277      * {@link #getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) authenticationInfo}, since either could
278      * occur in any order.
279      * <p/>
280      * For example, in &quot;Remember Me&quot; scenarios, the user identity is remembered (and
281      * assumed) for their current session and an authentication attempt during that session might never occur.
282      * But because their identity would be remembered, that is sufficient enough information to call this method to
283      * execute any necessary authorization checks.  For this reason, authentication and authorization should be
284      * loosely coupled and not depend on each other.
285      * <h3>Caching</h3>
286      * The {@code AuthorizationInfo} values returned from this method are cached for efficient reuse
287      * if caching is enabled.  Caching is enabled automatically when an {@link #setAuthorizationCache authorizationCache}
288      * instance has been explicitly configured, or if a {@link #setCacheManager cacheManager} has been configured, which
289      * will be used to lazily create the {@code authorizationCache} as needed.
290      * <p/>
291      * If caching is enabled, the authorization cache will be checked first and if found, will return the cached
292      * {@code AuthorizationInfo} immediately.  If caching is disabled, or there is a cache miss, the authorization
293      * info will be looked up from the underlying data store via the
294      * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} method, which must be implemented
295      * by subclasses.
296      * <h4>Changed Data</h4>
297      * If caching is enabled and if any authorization data for an account is changed at
298      * runtime, such as adding or removing roles and/or permissions, the subclass implementation should clear the
299      * cached AuthorizationInfo for that account via the
300      * {@link #clearCachedAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) clearCachedAuthorizationInfo}
301      * method.  This ensures that the next call to {@code getAuthorizationInfo(PrincipalCollection)} will
302      * acquire the account's fresh authorization data, where it will then be cached for efficient reuse.  This
303      * ensures that stale authorization data will not be reused.
304      *
305      * @param principals the corresponding Subject's identifying principals with which to look up the Subject's
306      *                   {@code AuthorizationInfo}.
307      * @return the authorization information for the account associated with the specified {@code principals},
308      *         or {@code null} if no account could be found.
309      */
310     protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
311 
312         if (principals == null) {
313             return null;
314         }
315 
316         AuthorizationInfo info = null;
317 
318         if (log.isTraceEnabled()) {
319             log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
320         }
321 
322         Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
323         if (cache != null) {
324             if (log.isTraceEnabled()) {
325                 log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
326             }
327             Object key = getAuthorizationCacheKey(principals);
328             info = cache.get(key);
329             if (log.isTraceEnabled()) {
330                 if (info == null) {
331                     log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
332                 } else {
333                     log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
334                 }
335             }
336         }
337 
338 
339         if (info == null) {
340             // Call template method if the info was not found in a cache
341             info = doGetAuthorizationInfo(principals);
342             // If the info is not null and the cache has been created, then cache the authorization info.
343             if (info != null && cache != null) {
344                 if (log.isTraceEnabled()) {
345                     log.trace("Caching authorization info for principals: [" + principals + "].");
346                 }
347                 Object key = getAuthorizationCacheKey(principals);
348                 cache.put(key, info);
349             }
350         }
351 
352         return info;
353     }
354 
355     protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
356         return principals;
357     }
358 
359     /**
360      * Clears out the AuthorizationInfo cache entry for the specified account.
361      * <p/>
362      * This method is provided as a convenience to subclasses so they can invalidate a cache entry when they
363      * change an account's authorization data (add/remove roles or permissions) during runtime.  Because an account's
364      * AuthorizationInfo can be cached, there needs to be a way to invalidate the cache for only that account so that
365      * subsequent authorization operations don't used the (old) cached value if account data changes.
366      * <p/>
367      * After this method is called, the next authorization check for that same account will result in a call to
368      * {@link #getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) getAuthorizationInfo}, and the
369      * resulting return value will be cached before being returned so it can be reused for later authorization checks.
370      * <p/>
371      * If you wish to clear out all associated cached data (and not just authorization data), use the
372      * {@link #clearCache(org.apache.shiro.subject.PrincipalCollection)} method instead (which will in turn call this
373      * method by default).
374      *
375      * @param principals the principals of the account for which to clear the cached AuthorizationInfo.
376      */
377     protected void clearCachedAuthorizationInfo(PrincipalCollection principals) {
378         if (principals == null) {
379             return;
380         }
381 
382         Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
383         //cache instance will be non-null if caching is enabled:
384         if (cache != null) {
385             Object key = getAuthorizationCacheKey(principals);
386             cache.remove(key);
387         }
388     }
389 
390     /**
391      * Retrieves the AuthorizationInfo for the given principals from the underlying data store.  When returning
392      * an instance from this method, you might want to consider using an instance of
393      * {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}, as it is suitable in most cases.
394      *
395      * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved.
396      * @return the AuthorizationInfo associated with this principals.
397      * @see org.apache.shiro.authz.SimpleAuthorizationInfo
398      */
399     protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals);
400 
401     //visibility changed from private to protected per SHIRO-332
402     protected Collection<Permission> getPermissions(AuthorizationInfo info) {
403         Set<Permission> permissions = new HashSet<Permission>();
404 
405         if (info != null) {
406             Collection<Permission> perms = info.getObjectPermissions();
407             if (!CollectionUtils.isEmpty(perms)) {
408                 permissions.addAll(perms);
409             }
410             perms = resolvePermissions(info.getStringPermissions());
411             if (!CollectionUtils.isEmpty(perms)) {
412                 permissions.addAll(perms);
413             }
414 
415             perms = resolveRolePermissions(info.getRoles());
416             if (!CollectionUtils.isEmpty(perms)) {
417                 permissions.addAll(perms);
418             }
419         }
420 
421         if (permissions.isEmpty()) {
422             return Collections.emptySet();
423         } else {
424             return Collections.unmodifiableSet(permissions);
425         }
426     }
427 
428     private Collection<Permission> resolvePermissions(Collection<String> stringPerms) {
429         Collection<Permission> perms = Collections.emptySet();
430         PermissionResolver resolver = getPermissionResolver();
431         if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) {
432             perms = new LinkedHashSet<Permission>(stringPerms.size());
433             for (String strPermission : stringPerms) {
434                 Permission permission = resolver.resolvePermission(strPermission);
435                 perms.add(permission);
436             }
437         }
438         return perms;
439     }
440 
441     private Collection<Permission> resolveRolePermissions(Collection<String> roleNames) {
442         Collection<Permission> perms = Collections.emptySet();
443         RolePermissionResolver resolver = getRolePermissionResolver();
444         if (resolver != null && !CollectionUtils.isEmpty(roleNames)) {
445             perms = new LinkedHashSet<Permission>(roleNames.size());
446             for (String roleName : roleNames) {
447                 Collection<Permission> resolved = resolver.resolvePermissionsInRole(roleName);
448                 if (!CollectionUtils.isEmpty(resolved)) {
449                     perms.addAll(resolved);
450                 }
451             }
452         }
453         return perms;
454     }
455 
456     public boolean isPermitted(PrincipalCollection principals, String permission) {
457         Permission p = getPermissionResolver().resolvePermission(permission);
458         return isPermitted(principals, p);
459     }
460 
461     public boolean isPermitted(PrincipalCollection principals, Permission permission) {
462         AuthorizationInfo info = getAuthorizationInfo(principals);
463         return isPermitted(permission, info);
464     }
465 
466     //visibility changed from private to protected per SHIRO-332
467     protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
468         Collection<Permission> perms = getPermissions(info);
469         if (perms != null && !perms.isEmpty()) {
470             for (Permission perm : perms) {
471                 if (perm.implies(permission)) {
472                     return true;
473                 }
474             }
475         }
476         return false;
477     }
478 
479     public boolean[] isPermitted(PrincipalCollection subjectIdentifier, String... permissions) {
480         List<Permission> perms = new ArrayList<Permission>(permissions.length);
481         for (String permString : permissions) {
482             perms.add(getPermissionResolver().resolvePermission(permString));
483         }
484         return isPermitted(subjectIdentifier, perms);
485     }
486 
487     public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) {
488         AuthorizationInfo info = getAuthorizationInfo(principals);
489         return isPermitted(permissions, info);
490     }
491 
492     protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) {
493         boolean[] result;
494         if (permissions != null && !permissions.isEmpty()) {
495             int size = permissions.size();
496             result = new boolean[size];
497             int i = 0;
498             for (Permission p : permissions) {
499                 result[i++] = isPermitted(p, info);
500             }
501         } else {
502             result = new boolean[0];
503         }
504         return result;
505     }
506 
507     public boolean isPermittedAll(PrincipalCollection subjectIdentifier, String... permissions) {
508         if (permissions != null && permissions.length > 0) {
509             Collection<Permission> perms = new ArrayList<Permission>(permissions.length);
510             for (String permString : permissions) {
511                 perms.add(getPermissionResolver().resolvePermission(permString));
512             }
513             return isPermittedAll(subjectIdentifier, perms);
514         }
515         return false;
516     }
517 
518     public boolean isPermittedAll(PrincipalCollection principal, Collection<Permission> permissions) {
519         AuthorizationInfo info = getAuthorizationInfo(principal);
520         return info != null && isPermittedAll(permissions, info);
521     }
522 
523     protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) {
524         if (permissions != null && !permissions.isEmpty()) {
525             for (Permission p : permissions) {
526                 if (!isPermitted(p, info)) {
527                     return false;
528                 }
529             }
530         }
531         return true;
532     }
533 
534     public void checkPermission(PrincipalCollection subjectIdentifier, String permission) throws AuthorizationException {
535         Permission p = getPermissionResolver().resolvePermission(permission);
536         checkPermission(subjectIdentifier, p);
537     }
538 
539     public void checkPermission(PrincipalCollection principal, Permission permission) throws AuthorizationException {
540         AuthorizationInfo info = getAuthorizationInfo(principal);
541         checkPermission(permission, info);
542     }
543 
544     protected void checkPermission(Permission permission, AuthorizationInfo info) {
545         if (!isPermitted(permission, info)) {
546             String msg = "User is not permitted [" + permission + "]";
547             throw new UnauthorizedException(msg);
548         }
549     }
550 
551     public void checkPermissions(PrincipalCollection subjectIdentifier, String... permissions) throws AuthorizationException {
552         if (permissions != null) {
553             for (String permString : permissions) {
554                 checkPermission(subjectIdentifier, permString);
555             }
556         }
557     }
558 
559     public void checkPermissions(PrincipalCollection principal, Collection<Permission> permissions) throws AuthorizationException {
560         AuthorizationInfo info = getAuthorizationInfo(principal);
561         checkPermissions(permissions, info);
562     }
563 
564     protected void checkPermissions(Collection<Permission> permissions, AuthorizationInfo info) {
565         if (permissions != null && !permissions.isEmpty()) {
566             for (Permission p : permissions) {
567                 checkPermission(p, info);
568             }
569         }
570     }
571 
572     public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
573         AuthorizationInfo info = getAuthorizationInfo(principal);
574         return hasRole(roleIdentifier, info);
575     }
576 
577     protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
578         return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
579     }
580 
581     public boolean[] hasRoles(PrincipalCollection principal, List<String> roleIdentifiers) {
582         AuthorizationInfo info = getAuthorizationInfo(principal);
583         boolean[] result = new boolean[roleIdentifiers != null ? roleIdentifiers.size() : 0];
584         if (info != null) {
585             result = hasRoles(roleIdentifiers, info);
586         }
587         return result;
588     }
589 
590     protected boolean[] hasRoles(List<String> roleIdentifiers, AuthorizationInfo info) {
591         boolean[] result;
592         if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) {
593             int size = roleIdentifiers.size();
594             result = new boolean[size];
595             int i = 0;
596             for (String roleName : roleIdentifiers) {
597                 result[i++] = hasRole(roleName, info);
598             }
599         } else {
600             result = new boolean[0];
601         }
602         return result;
603     }
604 
605     public boolean hasAllRoles(PrincipalCollection principal, Collection<String> roleIdentifiers) {
606         AuthorizationInfo info = getAuthorizationInfo(principal);
607         return info != null && hasAllRoles(roleIdentifiers, info);
608     }
609 
610     private boolean hasAllRoles(Collection<String> roleIdentifiers, AuthorizationInfo info) {
611         if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) {
612             for (String roleName : roleIdentifiers) {
613                 if (!hasRole(roleName, info)) {
614                     return false;
615                 }
616             }
617         }
618         return true;
619     }
620 
621     public void checkRole(PrincipalCollection principal, String role) throws AuthorizationException {
622         AuthorizationInfo info = getAuthorizationInfo(principal);
623         checkRole(role, info);
624     }
625 
626     protected void checkRole(String role, AuthorizationInfo info) {
627         if (!hasRole(role, info)) {
628             String msg = "User does not have role [" + role + "]";
629             throw new UnauthorizedException(msg);
630         }
631     }
632 
633     public void checkRoles(PrincipalCollection principal, Collection<String> roles) throws AuthorizationException {
634         AuthorizationInfo info = getAuthorizationInfo(principal);
635         checkRoles(roles, info);
636     }
637 
638     public void checkRoles(PrincipalCollection principal, String... roles) throws AuthorizationException {
639         checkRoles(principal, Arrays.asList(roles));
640     }
641 
642     protected void checkRoles(Collection<String> roles, AuthorizationInfo info) {
643         if (roles != null && !roles.isEmpty()) {
644             for (String roleName : roles) {
645                 checkRole(roleName, info);
646             }
647         }
648     }
649 
650     /**
651      * Calls {@code super.doClearCache} to ensure any cached authentication data is removed and then calls
652      * {@link #clearCachedAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} to remove any cached
653      * authorization data.
654      * <p/>
655      * If overriding in a subclass, be sure to call {@code super.doClearCache} to ensure this behavior is maintained.
656      *
657      * @param principals the principals of the account for which to clear any cached AuthorizationInfo
658      * @since 1.2
659      */
660     @Override
661     protected void doClearCache(PrincipalCollection principals) {
662         super.doClearCache(principals);
663         clearCachedAuthorizationInfo(principals);
664     }
665 }