`
amigo
  • 浏览: 50858 次
  • 来自: ...
社区版块
存档分类
最新评论

Shiro框架Web环境下过滤器结构分析

 
阅读更多
http://blog.csdn.net/jacky_zuo/article/details/7000402



Shiro的过滤器的配置是结合使用Spring的DelegatingFilterProxy与FactoryBean2种技术来完成自身过滤器的植入的,所以理解Shiro的过滤器首先要理解这2者的使用。



1. DelegatingFilterProxy

Spring提供的一个简便的过滤器的处理方案,它将具体的操作交给内部的Filter对象delegate去处理,而这个delegate对象通过Spring IOC容器获取,这里采用的是Spring的FactoryBean的方式获取这个对象。

DelegatingFilterProxy的配置如下
[html] view plaincopyprint?
<filter>  
    <filter-name>shiroFilter</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,但是它并做任何实际的工作,而是把工作交由Spring中容器为bean的名字shiroFilter的类,即ShiroFilterFactoryBean;

2. ShiroFilterFactoryBean

配置如下

[html] view plaincopyprint?
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 
    .. 
</bean> 
由于它是个FactroyBean,所以上面的delegate真正的对象是通过它的getObject()获取的。

这里是FactoryBean接口获取实例的标准方法
[html] view plaincopyprint?
public Object getObject() throws Exception { 
        if (instance == null) { 
            instance = createInstance(); 
        } 
        return instance; 
    } 

这里是真正创建对象的方法
[html] view plaincopyprint?
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,这个对象才是真正的delegate。

3. SpringShiroFilter: ShiroFilterFactoryBean的内部类,继承AbstractShiroFilter
[html] view plaincopyprint?
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); 
            } 
        } 
    } 

4. OncePerRequestFilter : AbstractShiroFilter的父类
关键方法

[html] view plaincopyprint?
protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws ServletException, IOException; 
这个方法有过滤器中调用:
[html] view plaincopyprint?
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并绑定相关数据)。
5. AbstractShiroFilter:OncePerRequestFilter的第一个子类

[html] view plaincopyprint?
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代码如下:
[html] view plaincopyprint?
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); 
            } 
        } 
    } 

[html] view plaincopyprint?
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:

[html] view plaincopyprint?
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); 
    } 

6. AdviceFilter:OncePerRequestFilter的第二个子类

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

[java] view plaincopyprint?
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中覆盖,代码如下:
[java] view plaincopyprint?
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方法,它只检测用户是否通过验证

[java] view plaincopyprint?
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 
        Subject subject = getSubject(request, response); 
        return subject.isAuthenticated(); 
    } 
第二个是RolesAuthorizationFilter的isAccessAllowed方法,它检测用户的角色是否满足
[java] view plaincopyprint?
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); 
   } 
分享到:
评论

相关推荐

    shiro安全框架

    Shiro具有内置的Web过滤器,可以方便地集成到SpringMVC或任何其他Web框架中。这些过滤器可以处理认证、授权以及会话管理,简化了Web应用的安全实现。 6. **生命周期管理** Shiro的组件都遵循统一的生命周期管理,...

    shiro源文件

    这些Filter可以方便地配置在Web应用的过滤器链中。 6. **缓存管理**: Shiro 使用缓存来提高性能,例如,将验证和授权信息存储在缓存中,减少对数据库的访问。Shiro支持多种缓存实现,包括本地缓存(如Guava Cache...

    shiro+spring的Demo

    - **Filter配置**:Shiro通过Filter实现Web层的拦截,Spring可以帮助管理这些Filter并整合到Spring MVC的过滤器链中。 3. **数据库设计**: - 用户表:通常包含用户名、密码、角色等信息,用于认证和授权。 - ...

    基于springboot的shiro完整项目案例

    在本案例中,你可能会发现自定义的过滤器,这些过滤器可能扩展了Shiro的内置Filter类,以适应项目的特定需求,例如登录检查、权限校验等。 **自定义Realm** Realm是Shiro的核心组件,负责与应用程序的数据源交互,...

    ssm整合shiro的的demo,附带数据库

    4. **Apache Shiro**:学习Shiro的配置,包括Realm(认证和授权的实现)、SecurityManager、过滤器链等,以及如何进行用户登录、权限控制和会话管理。 5. **数据库集成**:了解如何使用MySQL或其他数据库,以及编写...

    shiro入门

    4. **过滤器配置**:在 Web 应用中配置 Shiro 过滤器链,拦截并处理请求。 5. **测试**:编写测试用例,模拟用户登录、权限检查等场景,确保 Shiro 功能正常运行。 通过以上步骤,你将能够掌握 Apache Shiro 的基本...

    shiro认证绕过漏洞分析(CVE-2020-13933)1

    接着,需要配置Shiro的Web过滤器,通常在Spring Boot中,我们会创建一个 `ShiroConfig` 类来配置Shiro的过滤器链。 CVE-2020-13933是一个关于Shiro认证绕过的安全漏洞,它可能导致攻击者无需正确凭证就能通过认证。...

    shiro-example

    - `webapp` 目录(如果是 Web 应用):包含 JSP 页面、静态资源和 `WEB-INF` 目录,其中 `web.xml` 配置文件可能引用了 Shiro 的过滤器。 `shiro.ini` 配置文件通常包含如下部分: 1. **[main]**:初始化对象,如 ...

    springboot整合shiro视频

    Filter配置,定义Shiro过滤器链,如anon(匿名访问)、authc(身份验证)等。 3. 自定义Realm:实现自定义的UserRealm,重写doGetAuthenticationInfo()和doGetAuthorizationInfo()方法,分别处理用户登录验证和角色...

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

    - 编写过滤器链:定义Shiro的过滤器,例如 anon(匿名访问)、authc(身份验证)等,并配置URL拦截规则。 - 集成Spring Security:虽然Shiro功能强大,但若需要更复杂的安全控制,可以考虑与Spring Security集成。...

    springboot_shiro

    五、项目结构分析 - `src/main/java`:包含项目的主程序类、配置类以及业务逻辑。 - `src/main/resources`:存放配置文件、静态资源和模板文件。 - `pom.xml`:项目依赖管理,包含了SpringBoot、Shiro、Thymeleaf等...

    springboot+mybatis+layui+shiro 权限注解-后台的完整的项目整合

    1. **src/main/java**:包含应用的主类(通常带有@SpringBootApplication注解),业务逻辑层(Service)、数据访问层(Mapper)以及Shiro的配置和过滤器。 2. **src/main/resources**:配置文件如application....

    一个集成了SSM+Shiro的博客系统(期末设计毕业设计).zip

    SSM+Shiro框架组合是Java Web开发中常见的技术栈,尤其在期末设计或毕业设计中,这样的项目能帮助学生全面理解Web应用的开发流程。SSM指的是Spring、Spring MVC和MyBatis三大框架的集成,而Shiro则是一个安全管理...

    【开源,共享,下载免费,原创不易】springBoot快速集成Shiro和jsp

    在`application.yml`或`application.properties`中,我们可以设置Shiro的相关属性,例如过滤器链定义、登录URL、登出URL等。同时,我们还需要创建一个`ShiroConfig`类来配置Shiro的Bean,包括安全Manager、Filter ...

    Shiro整合springMvc+Mybatis+Redis demo源码下载.zip

    - 解压源码后,可以查看`web.xml`中的Shiro Filter配置,了解整体的过滤器结构。 - 分析`shiro.ini`或`shiro.xml`文件,理解Shiro的安全配置,包括角色、权限和用户的定义。 - 查看自定义的Realm类,理解如何与...

    liu_guo_feng-shiro-demo-master_java_源码.zip

    - **Filter**:Shiro提供了Web过滤器,如`FormAuthenticationFilter`用于表单登录,`RememberMeAuthenticationFilter`实现记住我功能。 - `web.xml`配置:将Shiro Filter链加入到Web应用的过滤器配置中,实现对...

    Springmvc+mybatis+shiro 源码分享

    - 配置Shiro,定义Realm处理认证和授权,设置过滤器链,控制用户访问权限。 - 实现RESTful API,定义@RequestMapping,处理HTTP动词,返回JSON格式的数据。 - 创建Web Service,可以选择CXF等库,定义服务接口和服务...

    shiro:shiro基于url做的权限系统

    它定义了一套标准的Filter,如`AuthcFilter`(认证过滤器)和`permsFilter`(权限过滤器),开发者可以通过配置这些Filter来控制访问特定URL的权限。 2. **配置URL与权限**: 在Shiro的配置文件(通常为`shiro.ini`或`...

    shiro:shiro示范项目

    2. **过滤器链配置**:Shiro通过Filter实现URL级别的权限控制,查看`web.xml`或Shiro的Java配置,理解如何配置Filter Chain。 3. **前端与后端交互**:分析JavaScript代码,了解如何通过Ajax请求获取权限信息,以及...

Global site tag (gtag.js) - Google Analytics