`

【shiro权限管理】4.shiro如何发挥作用

 
阅读更多

 shiro发生作用的关键在于代理过滤器DelegatingFilterProxy以及实际发生作用的过滤器ShiroFilterFactoryBean

 

一、DelegatingFilterProxy

web项目的一般filter配置如下:

<filter>
    <filter-name>permissionFilter</filter-name>
    <filter-class>com.taobao.riskm.filter.PermissionFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>permissionFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 而现在的大部分web项目都是spring项目,如果这个过滤器需要注入spring bean的话,传统的filter过滤方式就不适用,因为filter初始化在spring bean之前。spring提供一种解决方案:

在web.xml中配置spring提供的过滤器代理类DelegatingFilterProxy

<filter>
    <filter-name>permission</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
 <filter-mapping>
    <filter-name>permission</filter-name>
    <url-pattern>*.htm</url-pattern>
</filter-mapping>

 然后在spring配置文件中配置id与上面过滤器名称一致的真实发生作用的过滤器bean

<bean id="permission" class="com.taobao.kfc.kwb.web.permission.PermissionHttpServlet"></bean>

 

现在贴出DelegatingFilterProxy几个关键方法看它如何发生作用

//DelegatingFilterProxy过滤器doFilter方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            Object var5 = this.delegateMonitor;
            synchronized(this.delegateMonitor) {
                if (this.delegate == null) {
                    WebApplicationContext wac = this.findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                    }
                  //获取spring中的真实发生作用的过滤器bean
                    this.delegate = this.initDelegate(wac);
                }
                delegateToUse = this.delegate;
            }
        }
      //实际起作用的是这个invokeDelegate方法,如下面此方法调用delegateToUse的doFilter方法
        this.invokeDelegate(delegateToUse, request, response, filterChain);
    }

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
      //获取配置spring上下文中beanId与targetBeanName相同的bean,如没有配置targetBeanName则根据过滤器名寻找
        Filter delegate = (Filter)wac.getBean(this.getTargetBeanName(), Filter.class);
      //即配置中的targetFilterLifecycle如果为true则实际发生作用的过滤器即spring中的过滤器bean的init和destroy方法发生作用否则不调用他们的方法
        if (this.isTargetFilterLifecycle()) {
            delegate.init(this.getFilterConfig());
        }
        return delegate;
    }

protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        delegate.doFilter(request, response, filterChain);
    }

 

二、ShiroFilterFactoryBean

 它是个FactroyBean,所以上面的delegate真正的对象是通过它的getObject()获取的

public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }

 这里是真正创建对象的方法

protected AbstractShiroFilter createInstance() throws Exception {
 
        log.debug("Creating Shiro Filter instance.");
 
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }
 
        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }
 
        FilterChainManager manager = createFilterChainManager();
 
        //Expose the constructed FilterChainManager by first wrapping it in a
        // FilterChainResolver implementation. The AbstractShiroFilter implementations
        // do not know about FilterChainManagers - only resolvers:
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);
 
        //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
        //FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class
        //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
        //injection of the SecurityManager and FilterChainResolver:
        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

 

SpringShiroFilter: ShiroFilterFactoryBean的内部类,继承AbstractShiroFilter

private static final class SpringShiroFilter extends AbstractShiroFilter {
        protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
            super();
            if (webSecurityManager == null) {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            }
            setSecurityManager(webSecurityManager);
            if (resolver != null) {
                setFilterChainResolver(resolver);
            }
        }
    }

 

 OncePerRequestFilter : AbstractShiroFilter的父类

  关键方法

protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException;

  这个方法有过滤器中调用:

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
        if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(request)) {
            log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());
            // Proceed without invoking this filter...
            filterChain.doFilter(request, response);
        } else {
            // Do invoke this filter...
            log.trace("Filter '{}' not yet executed.  Executing now.", getName());
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
 
            try {
                doFilterInternal(request, response, filterChain);
            } finally {
                // Once the request has finished, we're done and we don't
                // need to mark as 'already filtered' any more.
                request.removeAttribute(alreadyFilteredAttributeName);
            }
        }
    }

  doFilterInternal这个方法有2处实现,1是AbstractShiroFilter的实现,2是AdviceFilter的实现。通过查看shiro的内定义的Filter继承结构可以看出,除了SpringShiroFilter这个内部类是继承前者,其他所有的用到的Filter都是继承后者。SpringShiroFilter是每次请求的第一个真正处理实际工作的Filter(主要是创建一个Subject并绑定相关数据)。

 

 AbstractShiroFilter:OncePerRequestFilter的第一个子类

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {
        Throwable t = null;
        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
 
            final Subject subject = createSubject(request, response);
 
            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    executeChain(request, response, chain);
                    return null;
                }
            });
        } catch (ExecutionException ex) {
            t = ex.getCause();
        } catch (Throwable throwable) {
            t = throwable;
        }
 
        if (t != null) {
            if (t instanceof ServletException) {
                throw (ServletException) t;
            }
            if (t instanceof IOException) {
                throw (IOException) t;
            }
            //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
            String msg = "Filtered request failed.";
            throw new ServletException(msg, t);
        }
    }

   这段代码表示每次经过AbstractShiroFilter的doFilterInternal方法(具体的类也就是上面的内部类SpringShiroFilter)都会创建一个新的Subject,具体分析里面的代码可以发现,这个Subject的数据会从SubjectContext或Session中获取过来。这意味着每次经过Shiro过滤器的HTTP请求,都会创建一次新的Subject.

   Suject里面的数据,主要是从SubjectContext中获取,但是获取方式不一样,如SecurityManager总是从SubjectContext中直接获取,而其他数据则主要从Session中获取。只有在登录操作的时候数据会都从SubjectContext上下文中获取。因为登录成功后还会有一个绑定操作,它会把当前用户的相关信息写入Session中去。

 

DefaultSecurityManager代码如下:

protected void bind(Subject subject) {
        // TODO consider refactoring to use Subject.Binder.
        // This implementation was copied from SessionSubjectBinder that was removed
        PrincipalCollection principals = subject.getPrincipals();
        if (principals != null && !principals.isEmpty()) {
            Session session = subject.getSession();
            bindPrincipalsToSession(principals, session);
        } else {
            Session session = subject.getSession(false);
            if (session != null) {
                session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            }
        }
 
        if (subject.isAuthenticated()) {
            Session session = subject.getSession();
            session.setAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY, subject.isAuthenticated());
        } else {
            Session session = subject.getSession(false);
            if (session != null) {
                session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
            }
        }
    }

 

private void bindPrincipalsToSession(PrincipalCollection principals, Session session) throws IllegalArgumentException {
        if (session == null) {
            throw new IllegalArgumentException("Session argument cannot be null.");
        }
        if (CollectionUtils.isEmpty(principals)) {
            throw new IllegalArgumentException("Principals cannot be null or empty.");
        }
        session.setAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY, principals);
    }

 其他登录相关的信息绑定到SubjectContext的操作代码如下,每个set方法的调用都将数据保存到SubjectContext:

protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {
        SubjectContext context = createSubjectContext();
        context.setAuthenticated(true);
        context.setAuthenticationToken(token);
        context.setAuthenticationInfo(info);
        if (existing != null) {
            context.setSubject(existing);
        }
        return createSubject(context);
    }

 

 AdviceFilter:OncePerRequestFilter的第二个子类 

  它是全部的验证与授权Filter的父类,其doFilterInternal方法承担此类过滤器的核心逻辑。

public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException {
 
        Exception exception = null;
 
        try {
 
            boolean continueChain = preHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");
            }
 
            if (continueChain) {
                executeChain(request, response, chain);
            }
 
            postHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Successfully invoked postHandle method");
            }
 
        } catch (Exception e) {
            exception = e;
        } finally {
            cleanup(request, response, exception);
        }
    }

 从上面的代码可以看出,其核心的逻辑是3个部分: preHandle, executeChain,postHandle。后2者都只有该类中有唯一的实现,子类并不覆盖,而preHandle则由一个子类PathMatchingFilter中覆盖,代码如下:

public boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
 
        if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
            if (log.isTraceEnabled()) {
                log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");
            }
            return true;
        }
 
        for (String path : this.appliedPaths.keySet()) {
            // If the path does match, then pass on to the subclass implementation for specific checks
            //(first match 'wins'):
            if (pathsMatch(path, request)) {
                if (log.isTraceEnabled()) {
                    log.trace("Current requestURI matches pattern [" + path + "].  Performing onPreHandle check...");
                }
                Object config = this.appliedPaths.get(path);
                return onPreHandle(request, response, config);
            }
        }
 
        //no path matched, allow the request to go through:
        return true;
    }

 这个方法根据用户请求的地址是否与该Filter配置的地址匹配来决定是否调用内部的onPreHandler方法。从shiroFilter中的属性filterChainDefinitions配置中可以看出,shiro默认的那些过滤器如user,roles,perms等等都可以统一使用这种方式,对于内部的处理则分别由各个Filter的onPreHandler(其实是由内部的isAccessAllowed和onAccessDenied方法)来决定了。

 

举2个例子

第一个是AuthenticationFilter的isAccessAllowed方法,它只检测用户是否通过验证

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        Subject subject = getSubject(request, response);
        return subject.isAuthenticated();
    }

 第二个是RolesAuthorizationFilter的isAccessAllowed方法,它检测用户的角色是否满足

 public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
 
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
 
        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return true;
        }
 
        Set<String> roles = CollectionUtils.asSet(rolesArray);
        return subject.hasAllRoles(roles);
    }

 

部分转自:https://blog.csdn.net/jacky_zuo/article/details/7000402

分享到:
评论

相关推荐

    springboot-shiro权限管理系统.zip

    《SpringBoot-Shiro权限管理系统详解》 在现代的Web开发中,权限管理和用户认证是不可或缺的重要环节。SpringBoot作为一款轻量级的Java框架,以其快速开发、易于配置的特性深受开发者喜爱。Shiro则是Apache组织提供...

    shiro权限管理框架.pdf

    Shiro是一个开源的Java...在配置和使用Shiro进行权限管理时,需要对框架有一个全面的理解,包括上述提到的认证、授权、会话管理等核心概念,以及框架的架构细节,这样才能有效地利用Shiro构建出安全稳定的应用程序。

    移动互联网场景下服务端Shiro权限管理研究.pdf

    移动互联网场景下服务端Shiro权限管理研究 1. 背景介绍 在移动互联网时代,Web系统、App和微信小程序等多种形式的混合应用层出不穷。随之而来的是权限管理的复杂性显著增加。当前的系统权限管理存在授权方式复杂、...

    管理系统系列--Spring Boot &amp; Shiro 权限管理系统.zip

    【标题】"管理系统系列--Spring Boot & Shiro 权限管理系统.zip" 提供了一个基于Spring Boot和Apache Shiro实现的权限管理系统的实例。这个压缩包可能包含了一个完整的项目结构,用于展示如何在Java环境中构建一个...

    shiro1.7.1.zip

    Apache Shiro 是一个强大且易用的Java安全框架,提供了身份认证、授权、加密和会话管理功能,简化了企业级应用的安全实现。标题中的"shiro1.7.1.zip"表明这是一个包含Apache Shiro 1.7.1版本所有核心模块的压缩包,...

    shiro_tool.zip

    Apache Shiro 是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可以非常轻松地开发出足够安全的应用。在"shiro_tool.zip"这个压缩包中,我们可以推测可能包含了一些关于Shiro使用的工具类、...

    shiro.freemarker.ShiroTags已打包

    Apache Shiro 是一个强大且易用的 Java 安全框架,它提供了认证、授权、加密和会话管理功能,简化了开发人员在应用程序中处理安全问题的复杂性。ShiroTags 是 Shiro 提供的一套用于模板引擎的标签库,如 Freemarker ...

    SpringMVC+Shiro权限管理.

    ### SpringMVC+Shiro权限管理系统详解 #### 权限管理概述 权限管理在软件开发中扮演着至关重要的角色,特别是在涉及多用户交互的应用场景下。通过合理的权限控制,可以确保不同用户仅能访问和操作他们被授权的功能...

    shiro-1.7.1.zip

    Apache Shiro是一个强大的Java安全框架,它为应用程序提供了身份验证(Authentication)、授权(Authorization)、会话管理(Session Management)和加密(Cryptography)等核心功能。`shiro-1.7.1.zip`是一个包含...

    基于SpringBoot + Vue 开发的前后端分离外卖点单系统,采用Shiro进行权限管理.zip

    基于SpringBoot + Vue 开发的前后端分离外卖点单系统,采用Shiro进行权限管理.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子...

    Shiro核心jar包.rar

    Apache Shiro是一个强大的Java安全框架,它提供了身份验证、授权、加密和会话管理功能,为开发人员构建安全的应用程序提供了便利。在这个“Shiro核心jar包.rar”压缩包中,包含的是Apache Shiro的核心库文件“shiro-...

    shiro1.7.1全包修补漏洞.rar

    Apache Shiro是一个强大的Java安全框架,它为应用程序提供了身份验证、授权、会话管理和加密服务。这个"shiro1.7.1全包修补漏洞.rar"文件包含了针对Apache Shiro 1.7.1版本的一些安全修复和更新,旨在解决可能存在的...

    【SpringBoot】廿三、SpringBoot中整合Shiro实现权限管理.pdf

    【SpringBoot】与Apache Shiro整合进行权限管理是构建安全Web应用的一种常见方式。Shiro是一个轻量级、易用的Java安全框架,提供身份验证、授权、密码管理和会话管理功能。它通过简单的API使得开发者能快速为各种...

    【SpringBoot】廿三、SpringBoot中整合Shiro实现权限管理.docx

    - 引入`shiro-spring`以集成Shiro权限管理。 - 引入`thymeleaf-extras-shiro`,以便在HTML模板中使用Shiro标签。 注意:依赖版本应与你的SpringBoot版本兼容,这里使用的是SpringBoot 2.3.1。 2. **配置Shiro**...

    管理系统系列--SpringBoot+Shiro权限管理系统脚手架.zip

    【SpringBoot+Shiro权限管理系统脚手架】是基于Spring Boot和Apache Shiro框架构建的高效、便捷的企业级权限管理解决方案。Spring Boot以其简洁的配置和快速开发的特点,已经成为Java Web开发的主流选择,而Shiro则...

    1.JAVA后台管理系统,企业开发首选脚手架2.后台采用Springboot框架+ Shiro权限+ Mybatis.zip

    系统的核心是采用了Springboot框架,Shiro权限管理和Mybatis持久层框架。下面我们将详细探讨这些技术栈的知识点。 **Springboot框架** Springboot是由Pivotal团队提供的全新框架,旨在简化Spring应用的初始搭建以及...

    shiro 2.alpha.0

    Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密和会话管理功能,可简化企业级应用的安全实现。Shiro 2.alpha.0 是该框架的一个早期版本,包含了框架的核心组件和相关源代码,便于开发者理解...

    shiro实战教程资料.zip

    Apache Shiro 是一个强大且易用的 Java 安全框架,提供身份认证、授权、加密以及会话管理功能,简化了开发者在应用安全方面的开发工作。这个"shiro实战教程资料.zip"压缩包包含了多个与 Shiro 相关的学习资源,帮助...

Global site tag (gtag.js) - Google Analytics