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);
}
原文地址:https://www.cnblogs.com/BINGJJFLY/p/9107899.html