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.isInfoEnabled()) {
248                     log.info("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     private Collection<Permission> getPermissions(AuthorizationInfo info) {
402         Set<Permission> permissions = new HashSet<Permission>();
403 
404         if (info != null) {
405             Collection<Permission> perms = info.getObjectPermissions();
406             if (!CollectionUtils.isEmpty(perms)) {
407                 permissions.addAll(perms);
408             }
409             perms = resolvePermissions(info.getStringPermissions());
410             if (!CollectionUtils.isEmpty(perms)) {
411                 permissions.addAll(perms);
412             }
413 
414             perms = resolveRolePermissions(info.getRoles());
415             if (!CollectionUtils.isEmpty(perms)) {
416                 permissions.addAll(perms);
417             }
418         }
419 
420         if (permissions.isEmpty()) {
421             return Collections.emptySet();
422         } else {
423             return Collections.unmodifiableSet(permissions);
424         }
425     }
426 
427     private Collection<Permission> resolvePermissions(Collection<String> stringPerms) {
428         Collection<Permission> perms = Collections.emptySet();
429         PermissionResolver resolver = getPermissionResolver();
430         if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) {
431             perms = new LinkedHashSet<Permission>(stringPerms.size());
432             for (String strPermission : stringPerms) {
433                 Permission permission = getPermissionResolver().resolvePermission(strPermission);
434                 perms.add(permission);
435             }
436         }
437         return perms;
438     }
439 
440     private Collection<Permission> resolveRolePermissions(Collection<String> roleNames) {
441         Collection<Permission> perms = Collections.emptySet();
442         RolePermissionResolver resolver = getRolePermissionResolver();
443         if (resolver != null && !CollectionUtils.isEmpty(roleNames)) {
444             perms = new LinkedHashSet<Permission>(roleNames.size());
445             for (String roleName : roleNames) {
446                 Collection<Permission> resolved = resolver.resolvePermissionsInRole(roleName);
447                 if (!CollectionUtils.isEmpty(resolved)) {
448                     perms.addAll(resolved);
449                 }
450             }
451         }
452         return perms;
453     }
454 
455     public boolean isPermitted(PrincipalCollection principals, String permission) {
456         Permission p = getPermissionResolver().resolvePermission(permission);
457         return isPermitted(principals, p);
458     }
459 
460     public boolean isPermitted(PrincipalCollection principals, Permission permission) {
461         AuthorizationInfo info = getAuthorizationInfo(principals);
462         return isPermitted(permission, info);
463     }
464 
465     private boolean isPermitted(Permission permission, AuthorizationInfo info) {
466         Collection<Permission> perms = getPermissions(info);
467         if (perms != null && !perms.isEmpty()) {
468             for (Permission perm : perms) {
469                 if (perm.implies(permission)) {
470                     return true;
471                 }
472             }
473         }
474         return false;
475     }
476 
477     public boolean[] isPermitted(PrincipalCollection subjectIdentifier, String... permissions) {
478         List<Permission> perms = new ArrayList<Permission>(permissions.length);
479         for (String permString : permissions) {
480             perms.add(getPermissionResolver().resolvePermission(permString));
481         }
482         return isPermitted(subjectIdentifier, perms);
483     }
484 
485     public boolean[] isPermitted(PrincipalCollection principals, List<Permission> permissions) {
486         AuthorizationInfo info = getAuthorizationInfo(principals);
487         return isPermitted(permissions, info);
488     }
489 
490     protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) {
491         boolean[] result;
492         if (permissions != null && !permissions.isEmpty()) {
493             int size = permissions.size();
494             result = new boolean[size];
495             int i = 0;
496             for (Permission p : permissions) {
497                 result[i++] = isPermitted(p, info);
498             }
499         } else {
500             result = new boolean[0];
501         }
502         return result;
503     }
504 
505     public boolean isPermittedAll(PrincipalCollection subjectIdentifier, String... permissions) {
506         if (permissions != null && permissions.length > 0) {
507             Collection<Permission> perms = new ArrayList<Permission>(permissions.length);
508             for (String permString : permissions) {
509                 perms.add(getPermissionResolver().resolvePermission(permString));
510             }
511             return isPermittedAll(subjectIdentifier, perms);
512         }
513         return false;
514     }
515 
516     public boolean isPermittedAll(PrincipalCollection principal, Collection<Permission> permissions) {
517         AuthorizationInfo info = getAuthorizationInfo(principal);
518         return info != null && isPermittedAll(permissions, info);
519     }
520 
521     protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) {
522         if (permissions != null && !permissions.isEmpty()) {
523             for (Permission p : permissions) {
524                 if (!isPermitted(p, info)) {
525                     return false;
526                 }
527             }
528         }
529         return true;
530     }
531 
532     public void checkPermission(PrincipalCollection subjectIdentifier, String permission) throws AuthorizationException {
533         Permission p = getPermissionResolver().resolvePermission(permission);
534         checkPermission(subjectIdentifier, p);
535     }
536 
537     public void checkPermission(PrincipalCollection principal, Permission permission) throws AuthorizationException {
538         AuthorizationInfo info = getAuthorizationInfo(principal);
539         checkPermission(permission, info);
540     }
541 
542     protected void checkPermission(Permission permission, AuthorizationInfo info) {
543         if (!isPermitted(permission, info)) {
544             String msg = "User is not permitted [" + permission + "]";
545             throw new UnauthorizedException(msg);
546         }
547     }
548 
549     public void checkPermissions(PrincipalCollection subjectIdentifier, String... permissions) throws AuthorizationException {
550         if (permissions != null) {
551             for (String permString : permissions) {
552                 checkPermission(subjectIdentifier, permString);
553             }
554         }
555     }
556 
557     public void checkPermissions(PrincipalCollection principal, Collection<Permission> permissions) throws AuthorizationException {
558         AuthorizationInfo info = getAuthorizationInfo(principal);
559         checkPermissions(permissions, info);
560     }
561 
562     protected void checkPermissions(Collection<Permission> permissions, AuthorizationInfo info) {
563         if (permissions != null && !permissions.isEmpty()) {
564             for (Permission p : permissions) {
565                 checkPermission(p, info);
566             }
567         }
568     }
569 
570     public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
571         AuthorizationInfo info = getAuthorizationInfo(principal);
572         return hasRole(roleIdentifier, info);
573     }
574 
575     protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
576         return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
577     }
578 
579     public boolean[] hasRoles(PrincipalCollection principal, List<String> roleIdentifiers) {
580         AuthorizationInfo info = getAuthorizationInfo(principal);
581         boolean[] result = new boolean[roleIdentifiers != null ? roleIdentifiers.size() : 0];
582         if (info != null) {
583             result = hasRoles(roleIdentifiers, info);
584         }
585         return result;
586     }
587 
588     protected boolean[] hasRoles(List<String> roleIdentifiers, AuthorizationInfo info) {
589         boolean[] result;
590         if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) {
591             int size = roleIdentifiers.size();
592             result = new boolean[size];
593             int i = 0;
594             for (String roleName : roleIdentifiers) {
595                 result[i++] = hasRole(roleName, info);
596             }
597         } else {
598             result = new boolean[0];
599         }
600         return result;
601     }
602 
603     public boolean hasAllRoles(PrincipalCollection principal, Collection<String> roleIdentifiers) {
604         AuthorizationInfo info = getAuthorizationInfo(principal);
605         return info != null && hasAllRoles(roleIdentifiers, info);
606     }
607 
608     private boolean hasAllRoles(Collection<String> roleIdentifiers, AuthorizationInfo info) {
609         if (roleIdentifiers != null && !roleIdentifiers.isEmpty()) {
610             for (String roleName : roleIdentifiers) {
611                 if (!hasRole(roleName, info)) {
612                     return false;
613                 }
614             }
615         }
616         return true;
617     }
618 
619     public void checkRole(PrincipalCollection principal, String role) throws AuthorizationException {
620         AuthorizationInfo info = getAuthorizationInfo(principal);
621         checkRole(role, info);
622     }
623 
624     protected void checkRole(String role, AuthorizationInfo info) {
625         if (!hasRole(role, info)) {
626             String msg = "User does not have role [" + role + "]";
627             throw new UnauthorizedException(msg);
628         }
629     }
630 
631     public void checkRoles(PrincipalCollection principal, Collection<String> roles) throws AuthorizationException {
632         AuthorizationInfo info = getAuthorizationInfo(principal);
633         checkRoles(roles, info);
634     }
635 
636     public void checkRoles(PrincipalCollection principal, String... roles) throws AuthorizationException {
637         checkRoles(principal, Arrays.asList(roles));
638     }
639 
640     protected void checkRoles(Collection<String> roles, AuthorizationInfo info) {
641         if (roles != null && !roles.isEmpty()) {
642             for (String roleName : roles) {
643                 checkRole(roleName, info);
644             }
645         }
646     }
647 
648     /**
649      * Calls {@code super.doClearCache} to ensure any cached authentication data is removed and then calls
650      * {@link #clearCachedAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} to remove any cached
651      * authorization data.
652      * <p/>
653      * If overriding in a subclass, be sure to call {@code super.doClearCache} to ensure this behavior is maintained.
654      *
655      * @param principals the principals of the account for which to clear any cached AuthorizationInfo
656      * @since 1.2
657      */
658     @Override
659     protected void doClearCache(PrincipalCollection principals) {
660         super.doClearCache(principals);
661         clearCachedAuthorizationInfo(principals);
662     }
663 }