(springboot)shiro安全框架自定义过滤器出现的几个疑难杂症解决方案

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> (springboot)shiro安全框架自定义过滤器出现的几个疑难杂症解决方案

问题一:多次重复重定向问题(匹配多个过滤器链重复调用其对应过滤器)

问题二:shiro认证时Realm会执行两次

在使用springboot框架整合shiro安全认证框架时踩了很多坑,每次出问题网上都找不到其中的解决方案,这里贴两个我遇到的坑,以及其解决方案给大家,希望大家可以少走弯路。

问题一场景:

123456789101112
        // 自定义拦截器        MapString, Filter customisedFilter = new HashMap();        customisedFilter.put("url", new CustomRolesAuthorizationFilter());         // 配置映射关系        filterChainDefinitionMap.put("/login", "anon");        filterChainDefinitionMap.put("/index", "anon");        filterChainDefinitionMap.put("/unauthorized", "anon");        filterChainDefinitionMap.put("/doLogout", "logout");        filterChainDefinitionMap.put("/**", "url");        shiroFilterFactoryBean.setFilters(customisedFilter);        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    // 自定义拦截器
    MapString, Filter customisedFilter = new HashMap();
    customisedFilter.put("url", new CustomRolesAuthorizationFilter());

    // 配置映射关系
    filterChainDefinitionMap.put("/login", "anon");
    filterChainDefinitionMap.put("/index", "anon");
    filterChainDefinitionMap.put("/unauthorized", "anon");
    filterChainDefinitionMap.put("/doLogout", "logout");
    filterChainDefinitionMap.put("/**", "url");
    shiroFilterFactoryBean.setFilters(customisedFilter);
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

当我访问/login时,会进入anon过滤器 。但是问题出在,他还会额外继续执行url(自定义过滤器),在自定义过滤器中的逻辑是用户没有登录就重定向到/login这个url去,然后又进入anon过滤器,又执行url过滤器,又进行重定向,循环往复造成多次重定向。

问题一解决过程:

翻阅了一些shiro的资料,了解了一下其中过滤器的机制,shiro会对Servlet容器里的FilterChain进行代理,Shiro会通过一个代理类ProxiedFilterChain对Servlet对其进行代理,其中会先进行Shiro中用户自己配置的拦截器配置映射的关系,即先走映射关系中匹配到的url对应的过滤器,然后会去执行Servlet中的FilterChain进行Filter链的执行。

如果有看过底层源码,就会看到一个originalChain这个名词,它就是Servlet保存的FilterChain。也就是说,每次请求都将会先走Shiro的过滤器链,然后再走Servlet的过滤器链。

(springboot)shiro安全框架自定义过滤器出现的几个疑难杂症解决方案

这里我是SpringBoot框架,可以看到控制台在项目启动时自动配置characterEncodingFilter、requestContextFilter等等一些默认过滤器,把它加入一个叫FilterRegistrationBean中,作为一个过滤器链。可以看到红框中,这两个是我自己自定义的过滤器,我将其配置到Shiro中作为Shiro过滤器链使用,但没想到SpringBoot自动把这两个过滤器配置到了FilterRegistrationBean中,并且路径为/*,这也就能理解为什么上面会匹配到anon过滤器之后还会往下执行到我们的自定义过滤器了。

(springboot)shiro安全框架自定义过滤器出现的几个疑难杂症解决方案

这是我两个自定义过滤器的配置Bean,其中过滤器实现了PathMatchingFilter接口,我不知道是不是因为这个,还是什么奇怪的原因才会把它加载到FilterRegistrationBean中,如果有知道为什么的可以在底下评论告知我一下,至今还是对此摸不着头脑。

问题一解决方案:

最后我把这两个Bean注释掉了,在配置Shiro中我直接new出来,不作为Bean交给IOC管理了,这样就解决了问题,Shiro就不会调用额外的自定义过滤器了。

问题二解决过程:

当时因为我需要动态配置映射关系,会从数据库中读取需要映射的url与需要拦截的角色与权限,过滤器链有可能同一个URL匹配多个过滤器(例如permission过滤器和roles过滤器,角色与权限双验证效果),所以我就自定义了PathMatchingFilterChainResolver,并重写了getChain方法,这个方法是用来匹配返回即将调用的过滤器的,在里面我的逻辑是匹配所有匹配到的URL,把所有对应能匹配到的过滤器全部执行,但错误在,我在最底下写了一个匹配规则 /** 匹配authc,逻辑是除了需要权限验证、或是设置了anon、logout过滤器以外的url都要进行登录才可以访问,这就造成了在进行登录的时候,访问一次登录方法,又会去匹配那个/** 继续走一次身份验证,而走身份验证 subject.login 的底层是会走Realm,这就造成了走两次Realm。

问题二解决方案:

造成这种问题很大可能是PathMatchingFilterChainResolver中的getChain逻辑没有写好,ufl映射关系配置不科学造成。

下面贴一个我的getChian方法。

123456789101112131415161718192021222324252627282930
    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {        FilterChainManager filterChainManager = getFilterChainManager();        if (!filterChainManager.hasChains()) {            return null;        }         String requestURI = getPathWithinApplication(request);         //要执行的过滤器集合        ListString chainNames = new ArrayListString();         //获取全部的拦截url        SetString chain = filterChainManager.getChainNames();         for (String pathPattern : chain){            // 匹配所有匹配到的url,装入chainNames作为即将要执行的过滤器集合            if (pathMatches(pathPattern, requestURI)) {                chainNames.add(pathPattern);            }        }        //拦截器链的最后一个Url,因为它匹配全部的url,这里不与其他拦截器冲突,剔除/**url的匹配        Object lastUrl = "/**";        chainNames.remove(lastUrl);        //没有匹配到url        if(chainNames.size() == 0) {            //为了不与其他拦截器冲突,在全部url都不匹配的情况下才匹配/**            chainNames.add("/**");        }        return customDefaultFilterChainManager.proxy(originalChain, chainNames);    }

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
    FilterChainManager filterChainManager = getFilterChainManager();
    if (!filterChainManager.hasChains()) {
        return null;
    }

    String requestURI = getPathWithinApplication(request);
    
    //要执行的过滤器集合
    ListString chainNames = new ArrayListString();

    //获取全部的拦截url
    SetString chain = filterChainManager.getChainNames();

    for (String pathPattern : chain){
        // 匹配所有匹配到的url,装入chainNames作为即将要执行的过滤器集合
        if (pathMatches(pathPattern, requestURI)) {
            chainNames.add(pathPattern);
        }
    }
    //拦截器链的最后一个Url,因为它匹配全部的url,这里不与其他拦截器冲突,剔除/**url的匹配
    Object lastUrl = "/**";
    chainNames.remove(lastUrl);
    //没有匹配到url
    if(chainNames.size() == 0) {
        //为了不与其他拦截器冲突,在全部url都不匹配的情况下才匹配/**
        chainNames.add("/**");
    }
    return customDefaultFilterChainManager.proxy(originalChain, chainNames);
}

基本思路就是匹配除了/**的所有过滤器路径,如果能匹配到,则执行匹配到的过滤器集合,如果一个都没匹配到,才走最后的身份验证。

要了解底层Shiro是怎么进行身份验证,这样出了问题就能通过Debug进行排错,如果项目中需要深度整合Shiro,改写很多Shiro的验证逻辑,需要了解Shiro底层原理,如FilterChainResolver(处理匹配规则)、FilterChainManager(处理匹配到的过滤器链)、PathMatchingFilter(自定义过滤器)、AuthorizingRealm(获取认证相关数据源的地方)、CredentialsMatcher(自定义登录认证逻辑)等等的原理,才能灵活运用Shiro框架。有空写一篇动态URL配置,动态双拦截角色权限的Shiro配置,和与SSO单点登录的WebService服务接口整合实现自定义的登录与权限的逻辑。

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> (springboot)shiro安全框架自定义过滤器出现的几个疑难杂症解决方案


 上一篇
(SpringBoot)Shiro安全框架深入解析 (SpringBoot)Shiro安全框架深入解析
最近在学习Shiro安全框架的使用,深入研究其原理,让自己更得心应手的使用这个框架。 内容目录 Shiro的整体架构介绍 框架验证流程与原理分析 Url匹配模式 加密机制 缓存机制 1.Shiro的整体架构介绍1.1从使用者角度看Shir
下一篇 
使用SpringBoot+Dubbo搭建一个简单的分布式服务 使用SpringBoot+Dubbo搭建一个简单的分布式服务
来源:JavaGuide(ID:Java_Guide) 使用 SpringBoot+Dubbo 搭建一个简单分布式服务实战之前,先来看几个重要的概念开始实战之前,我们先来简单的了解一下这样几个概念:Dubbo、RPC、分布式、由于本文的