测试环境

  • shiro 1.4.0
  • springboot

shiro过滤器配置如下:

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    //设置安全管理器
    shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    //设置未认证(登录)时,访问需要认证的资源时跳转的页面
    shiroFilterFactoryBean.setLoginUrl("/index");
    //设置访问无权限的资源时跳转的页面
    shiroFilterFactoryBean.setUnauthorizedUrl("/index");
    shiroFilterFactoryBean.setUnauthorizedUrl("/login");
    //指定路径和过滤器的对应关系
    Map<String, String> filterMap = new HashMap<>();
    //设置/index和/login不需要登录就能访问
    filterMap.put("/login", "anon");
    filterMap.put("/index", "anon");
    //设置/admin/index需要登录用户拥有角色p1n93r时才能访问
    filterMap.put("/admin/*", "roles[p1n93r]");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    return shiroFilterFactoryBean;
}

前置知识

  • shiro对Servlet容器的FilterChain进行了代理,即shiroFilter在Servlet容器的Filter链之前。
  • 如果请求的url和shiro规则匹配,则在FilterChainResolver中使用ProxiedFilterChain对Servlet容器的FilterChain进行代理,即走Shiro的Filter体系,然后才会走Servlet的Filter链。
  • 绕过原理就是:通过绕过匹配规则,使得在FilterChainResolver中不对Servlet容器的FilterChain进行代理,直接走Servlet容器的FilterChain,从而绕过了shiro的验证。

代码分析

分析入口为 org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver ,如下:

shiro分析0

现在问题是,如何构造请求的url,让 pathMatches(pathPattern, requestURI) 返回false,且该请求的url还能让springmvc匹配正确的控制器。通过给 pathMatches(pathPattern, requestURI) 所在行打断点,追踪代码,最终可以达到下面:

shiro分析1

所以我们在请求的最后加一个“/”符号,就能不走shiro的代理,绕过shiro(同时spring下 /index/admin//index/admin 是等效的)。如下:

① 第一次没加“/”,被拦截: shiro分析2

② 第二次末尾加“/”,绕过shiro: shiro分析3