zoukankan      html  css  js  c++  java
  • 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);
    }
  • 相关阅读:
    linux修改键盘按键
    linux添加一个已经存在用户到一个用户组
    centos-6更新yum源(163)
    Fedora 19安装以后的优化
    centos永久性修改系统时间显示格式
    smb.conf文件详解
    Centos上部署文件共享
    centos上mysql开启远程访问
    centos安装mysql后默认密码修改
    centos上mysql的一种安装方式
  • 原文地址:https://www.cnblogs.com/BINGJJFLY/p/9107899.html
Copyright © 2011-2022 走看看