Shiro 权限验证原理
概要
实现权限验证行为的前提需要实现横切拦截设计(Spring的AOP)参考:https://www.cnblogs.com/BINGJJFLY/p/9066524.html
spring-shiro.xml配置文件的配置
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true"/> </bean> <bean id="authorizationAttributeSourceAdvisor" class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean>
AuthorizationAttributeSourceAdvisor
public AuthorizationAttributeSourceAdvisor() { setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor()); }
AopAllianceAnnotationsAuthorizingMethodInterceptor
public Object invoke(MethodInvocation methodInvocation) throws Throwable { assertAuthorized(methodInvocation); return methodInvocation.proceed(); }
获得各个方法拦截器如RoleAnnotationMethodInterceptor
protected void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException { //default implementation just ensures no deny votes are cast: Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors(); if (aamis != null && !aamis.isEmpty()) { for (AuthorizingAnnotationMethodInterceptor aami : aamis) { if (aami.supports(methodInvocation)) { aami.assertAuthorized(methodInvocation); } } } }
RoleAnnotationMethodInterceptor
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { try { ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi)); } catch(AuthorizationException ae) { // Annotation handler doesn't know why it was called, so add the information here if possible. // Don't wrap the exception here since we don't want to mask the specific exception, such as // UnauthenticatedException etc. if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod())); throw ae; } }
RoleAnnotationHandler
public void assertAuthorized(Annotation a) throws AuthorizationException { if (!(a instanceof RequiresRoles)) return; RequiresRoles rrAnnotation = (RequiresRoles) a; String[] roles = rrAnnotation.value(); if (roles.length == 1) {
// 获得WebDelegatingSubject校验会员角色权限 getSubject().checkRole(roles[0]); return; } if (Logical.AND.equals(rrAnnotation.logical())) { getSubject().checkRoles(Arrays.asList(roles)); return; } if (Logical.OR.equals(rrAnnotation.logical())) { // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first boolean hasAtLeastOneRole = false; for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true; // Cause the exception if none of the role match, note that the exception message will be a bit misleading if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]); } }
DelegatingSubject专门校验会员权限
Subject校验权限 --> SecurityManager校验权限 --> Realm校验权限
public void checkRole(String role) throws AuthorizationException { assertAuthzCheckPossible();
// getPrincipals()获得会员存储在数据库的信息 securityManager.checkRole(getPrincipals(), role); }
SecurityManager校验权限
AuthorizingSecurityManager使用授权器校验会员权限
public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException { this.authorizer.checkRole(principals, role); }
ModularRealmAuthorizer
public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException { assertRealmsConfigured(); if (!hasRole(principals, role)) { throw new UnauthorizedException("Subject does not have role [" + role + "]"); } }
获得所有的实现了Authorizer的Realm领域,让领域去进一步校验,例如AuthorizingRealm
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) { assertRealmsConfigured(); for (Realm realm : getRealms()) { if (!(realm instanceof Authorizer)) continue; if (((Authorizer) realm).hasRole(principals, roleIdentifier)) { return true; } } return false; }
AuthorizingRealm
public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
// 获得会员权限信息,后文详解#1 AuthorizationInfo info = getAuthorizationInfo(principal);
// 根据会员拥有的权限和目标权限进行比对,判断会员是否有目标权限,后文详解#2 return hasRole(roleIdentifier, info); }
书接前文#1
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { // 没有会员的信息直接返回null if (principals == null) { return null; } AuthorizationInfo info = null; if (log.isTraceEnabled()) { log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); } // 试图从缓存中获得会员权限信息 Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); if (cache != null) { if (log.isTraceEnabled()) { log.trace("Attempting to retrieve the AuthorizationInfo from cache."); } Object key = getAuthorizationCacheKey(principals); info = cache.get(key); if (log.isTraceEnabled()) { if (info == null) { log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]"); } else { log.trace("AuthorizationInfo found in cache for principals [" + principals + "]"); } } } if (info == null) { // Call template method if the info was not found in a cache // 具体的获得会员权限信息让子类去实现例如自定的Realm领域
info = doGetAuthorizationInfo(principals); // If the info is not null and the cache has been created, then cache the authorization info. if (info != null && cache != null) { if (log.isTraceEnabled()) { log.trace("Caching authorization info for principals: [" + principals + "]."); }
// 将会员的权限信息放入到缓存中 Object key = getAuthorizationCacheKey(principals); cache.put(key, info); } } return info; }
自定义的Realm领域
package com.wjz.demo; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; public class CustomRealm extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { return new SimpleAuthenticationInfo("wjz", "123", getName()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addRole("buyGoods"); return authorizationInfo; } }
书接前文#2
判断会员有的权限是否包含目标权限
protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) { return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier); }