• Shiro的学习之Shiro的配置(一)


    				版权声明:重在参与,贵在分享					https://blog.csdn.net/wohaqiyi/article/details/79334899				

    接上一篇文章shiro框架—通过系统介绍shiro框架中的实现逻辑

    • 1
    • 2
    • 3
    • 4
    • 5

    项目已分享到GitHub上,如果需要的可以看下,springboot+shiro项目Git下载地址

    shiro在springboot项目中的配置步骤

    1、引入依赖

    首先shiro的应用,引入的依赖仅仅只有一个,即下边这个。

     
       org.apache.shiro
          shiro-all
          1.3.2
      
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、shiro在springboot项目中的位置:

    以下是shiro在springboot项目中的位置:
    这里写图片描述
    主要的文件有四个ShiroConfigRetryLimitHashedCredentialsMatcherUserRealmMShiroFilterFactoryBean 。在这里 `UserOAuthMatcher 没有用到,其实它跟RetryLimitHashedCredentialsMatcher 是一样的意思,都是实现的同一个shiro的接口,用哪一个都可以,我在下边的链接里就不放进UserOAuthMatcher 了。

    3、以上配置文件的主要功能:

    (1) shiroConfig
    关于shiroConfig 的配置主要内容,其实就是下图的这些:
    这里写图片描述

    以下是shiroConfig 文件的全部配置。其他的配置文件都是围绕这个文件展开工作的。
    package microservice.fpzj.shiro;
    import org.apache.shiro.authc.credential.CredentialsMatcher;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.filter.authc.AnonymousFilter;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.web.filter.DelegatingFilterProxy;
    import javax.servlet.DispatcherType;
    import javax.servlet.Filter;
    import java.util.LinkedHashMap;
    import java.util.Map;
    @Configuration
    public class ShiroConfig {
        @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
            filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter")); 
            filterRegistration.setEnabled(true);
            filterRegistration.addUrlPatterns("/*"); //过滤规则,即所有的请求
            filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
            return filterRegistration;
        }
    
        /**
         * 这个即是上边调用的shiroFilter过滤器,也就是shiro配置的过滤器
         * @return
         */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(){
            /**
            *MShiroFilterFactoryBean指向自定义过滤器,自定义过滤器对js/css等忽
            *略
            **/
            ShiroFilterFactoryBean bean = new MShiroFilterFactoryBean(); 
            bean.setSecurityManager(securityManager());
            bean.setLoginUrl("/login");
            bean.setUnauthorizedUrl("/unauthor");
            Mapfilters = new LinkedHashMap<>();
    //      filters.put("perms", urlPermissionsFilter());
            filters.put("anon", new AnonymousFilter());
            bean.setFilters(filters);
            //shiro配置过滤规则少量的话可以用hashMap,数量多了要用LinkedHashMap,保证有序,原因未知
            Map chains = new LinkedHashMap<>();
            chains.put("/login", "anon");
            chains.put("/unauthor", "anon");
            chains.put("/logout", "anon");
            chains.put("/weblogin", "anon");
            chains.put("/**", "authc");
            bean.setFilterChainDefinitionMap(chains);
            return bean;
        }
    
        /**
         * @see org.apache.shiro.mgt.SecurityManager
         * @return
         */
        @Bean(name="securityManager")
        public DefaultWebSecurityManager securityManager() {
            DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
            manager.setRealm(userRealm());
            //manager.setCacheManager(cacheManager());
            manager.setSessionManager(defaultWebSessionManager());
            return manager;
        }
    
        /**
         * @see DefaultWebSessionManager
         * @return
         */
        @Bean(name="sessionManager")
        public DefaultWebSessionManager defaultWebSessionManager() {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            //sessionManager.setCacheManager(cacheManager());
            sessionManager.setGlobalSessionTimeout(1800000);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionValidationSchedulerEnabled(true);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionIdCookie(getSessionIdCookie());
            return sessionManager;
        }
        /**
         * 给shiro的sessionId默认的JSSESSIONID名字改掉
         * @return
         */
        @Bean(name="sessionIdCookie")
        public SimpleCookie getSessionIdCookie(){
            SimpleCookie simpleCookie = new SimpleCookie("webcookie");
            /**
             * HttpOnly标志的引入是为了防止设置了该标志的cookie被JavaScript读取,
             * 但事实证明设置了这种cookie在某些浏览器中却能被JavaScript覆盖,
             * 可被攻击者利用来发动session fixation攻击
             */
            simpleCookie.setHttpOnly(true);
            /**
             * 设置浏览器cookie过期时间,如果不设置默认为-1,表示关闭浏览器即过期
             * cookie的单位为秒 比如60*60为1小时
             */
            simpleCookie.setMaxAge(-1);
            return simpleCookie;
        }
    
        /**
         * @see UserRealm--->AuthorizingRealm
         * @return
         */
        @Bean
        @DependsOn(value="lifecycleBeanPostProcessor")
        public UserRealm userRealm() {
            UserRealm userRealm = new UserRealm();
            userRealm.setCredentialsMatcher(credentialsMatcher());
            //userRealm.setCacheManager(cacheManager());
            return userRealm;
        }
        @Bean(name="credentialsMatcher")
        public CredentialsMatcher credentialsMatcher() {
            return new RetryLimitHashedCredentialsMatcher();
        }
    
    
        /*@Bean
        public EhCacheManager cacheManager() {
            EhCacheManager cacheManager = new EhCacheManager();
            cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
            return cacheManager;
        }*/
    
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137

    下边针对于shiroConfig 文件中的配置,一一说明。
    首先该文件的第一个配置为FilterRegistrationBean 。 springboot注入过滤器有多种方式,一种是最简单的@WebFilter注解,一种就是下边的这种,写一个FilterRegistrationBean,然后将自定义过滤器set进去,下边是通过DelegatingFilterProxy 代理的方式,注入容器中名字为shiroFilter 的过滤器,最后设置过滤器的规则。

    @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
            /**
            *DelegatingFilterProxy做的事情是代理Filter的方法,从application
            *context里获得bean,从下边可以理解到,它是将容器中名字为shiroFilter
            *的过滤器加入到过滤器注册bean中
            **/
            filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter")); 
            filterRegistration.setEnabled(true);
            filterRegistration.addUrlPatterns("/*"); //过滤规则,即所有的请求
            filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
            return filterRegistration;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    既然上边注入的是名字为shiroFilter 的过滤器,那下边就是shiroFilter 的配置

    /**
         * 这个即是上边调用的shiroFilter过滤器,也就是shiro配置的过滤器
         * @return
         */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(){
            /**
            *MShiroFilterFactoryBean指向自定义过滤器,自定义过滤器对js/css等忽
            *略
            **/
            ShiroFilterFactoryBean bean = new MShiroFilterFactoryBean(); 
            bean.setSecurityManager(securityManager());
            bean.setLoginUrl("/login");
            bean.setUnauthorizedUrl("/unauthor");
            Mapfilters = new LinkedHashMap<>();
    //      filters.put("perms", urlPermissionsFilter());
            /**
            * shiro自己的过滤器,anon,表示不拦截的路径,authc,表示拦截的路径
            **/
            filters.put("anon", new AnonymousFilter());
            bean.setFilters(filters);
            /*
            *shiro配置过滤规则少量的话可以用hashMap,数量多了要用
            *LinkedHashMap,保证有序,原因未知。
            *,anon,表示不拦截的路径,authc,表示拦截的路径。匹配时,首先匹配
            *anon的,然后最后匹配authc
            **/
            Map chains = new LinkedHashMap<>();
            chains.put("/login", "anon");
            chains.put("/unauthor", "anon");
            chains.put("/logout", "anon");
            chains.put("/weblogin", "anon");
            chains.put("/**", "authc"); 
            bean.setFilterChainDefinitionMap(chains);
            return bean;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    以上的shiroFilter 配置中,又引入了我们的第二个配置文件,名字为MShiroFilterFactoryBean ,该类继承了ShiroFilterFactoryBean 类,通过名字,应该大体能知道,它是shiro的过滤器工厂类,而我们的MShiroFilterFactoryBean 类就是一个自定义的shiro过滤器,为什么要自己写一个过滤器呢?
    在当前的shiro框架中,无法拦截那种.js.css.html.jsp 等等带有. 的请求路径,对于前三种这种静态资源,我们可以不纳入shiro拦截,即可以在不登录的情况下访问成功,但是我们现在有这样的需求,即对.jsp 也要拦截起来,那就需要自定义shiro过滤器了,即现在MShiroFilterFactoryBean 类存在的意义。关于该类的解释,在后边再说,我们继续shiroConfig 文件的介绍。
    在上边的配置中,其实就是自定义了一个shiro过滤器,然后对其进行了一些操作,其中bean.setLoginUrl("/login") 是在项目启动后,如果没有登录的情况下,会被shiro强制请求的路径,即为/login
    另外,bean.setSecurityManager(securityManager()); 这句的配置,即引入设置shiro的控制中心,即securityManager ,安全管理器;即所有与安全有关的操作都会与securityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;关于subject ,你就理解为是每一个访问系统的用户对象即可,所有的访问用户的情况都是一种subject 的体现,它们又统一被securityManager 管理,这个在第一篇里已经说过。
    通过上边的shiroFilter 的配置之后,然后再看securityManager

    /**
         * @see org.apache.shiro.mgt.SecurityManager
         * @return
         */
        @Bean(name="securityManager")
        public DefaultWebSecurityManager securityManager() {
            DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
            manager.setRealm(userRealm());
            //manager.setCacheManager(cacheManager());
            manager.setSessionManager(defaultWebSessionManager());
            return manager;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    以上又引入了我们的第三个配置文件,即UserRealm 文件,改文件又引出了我们的一个概念,Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
    在这里的UserRealm 类继承于AuthorizingRealm ,该类的作用其实有用户密码验证、权限授权等。这个也会在后边贴出来,先继续讲shiroConfig 文件。
    通过上边的manager.setSessionManager(defaultWebSessionManager()); 然后引入下边的配置

    /**
         * @see DefaultWebSessionManager
         * @return
         */
        @Bean(name="sessionManager")
        public DefaultWebSessionManager defaultWebSessionManager() {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            //sessionManager.setCacheManager(cacheManager());
            sessionManager.setGlobalSessionTimeout(1800000);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionValidationSchedulerEnabled(true);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionIdCookie(getSessionIdCookie());
            return sessionManager;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    上边的配置,主要是对session的配置,比如,超时时间的设置等,基本上都是跟session相关的配置。另外,上边还有getSessionIdCookie() 方法的引用,众所周知,浏览器与后台系统交互的方式,是以后台存储session,然后将该session对应key,以字符串的形式返给浏览器,并在浏览器中以cookie 的形式记录起来,方便后续的访问,如果浏览器丢失了这个cookie,那就会失去与后台系统的联系,必须重新登录,才能重新再生成这个cookie。而getSessionIdCookie() 方法,即是对cookie在浏览器那里的名字的定义,如下:

    /**
         * 给shiro的sessionId默认的JSSESSIONID名字改掉
         * @return
         */
        @Bean(name="sessionIdCookie")
        public SimpleCookie getSessionIdCookie(){
            SimpleCookie simpleCookie = new SimpleCookie("webcookie");
            /**
             * HttpOnly标志的引入是为了防止设置了该标志的cookie被JavaScript读取,
             * 但事实证明设置了这种cookie在某些浏览器中却能被JavaScript覆盖,
             * 可被攻击者利用来发动session fixation攻击
             */
            simpleCookie.setHttpOnly(true);
            /**
             * 设置浏览器cookie过期时间,如果不设置默认为-1,表示关闭浏览器即过期
             * cookie的单位为秒 比如60*60为1小时
             */
            simpleCookie.setMaxAge(-1);
            return simpleCookie;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    以上配置起的名字为webcookie ,这个webcookie 就是浏览器那边存储后台传入过来的cookie 的key。整个的大体流程,我理解的如下:
    这里写图片描述
    以后再请求,只要浏览器没有清除cookie ,上边关于session 的超时时间没有超时,就可以正常访问系统。之所以,这里要给sessionid 起一个名字webcookie 这是防止浏览器访问多个系统的时候,恰巧碰上两个系统在浏览器那边存储sessionid 对应的key正好相同,即session污染 ,造成访问系统出现问题。如果不设置,shiro默认的sessionid 在前端浏览器的名字为cookie
    关于session污染 的异常,我遇到的是下边这个:

     Found 'sid' cookie value [1a22b751-0542-4e74-a8e7-59942692f6ae]
    22:13:37 DEBUG net.sf.ehcache.Cache - mx-master-SessionCache cache - Miss
    22:13:37 DEBUG o.a.shiro.mgt.DefaultSecurityManager - Resolved SubjectContext context session is invalid.  Ignoring and creating an anonymous (session-less) Subject instance.
    org.apache.shiro.session.UnknownSessionException: There is no session with id [1a22b751-0542-4e74-a8e7-59942692f6ae]
    
    • 1
    • 2
    • 3
    • 4

    如果出现There is no session with id 的异常,不出意外的话,就是上边的配置有问题,需要有cookie 的配置,相关的文章,你可以看这一篇一个项目两个web模块会导致shiro的session污染 ,可以得到解释。
    继续shiroconfig 文件的配置,然后再后边就是如下配置:

        @Bean
        @DependsOn(value="lifecycleBeanPostProcessor")
        public UserRealm userRealm() {
            UserRealm userRealm = new UserRealm();
            userRealm.setCredentialsMatcher(credentialsMatcher());
            //userRealm.setCacheManager(cacheManager());
            return userRealm;
        }
        @Bean(name="credentialsMatcher")
        public CredentialsMatcher credentialsMatcher() {
            return new RetryLimitHashedCredentialsMatcher();
        }
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    上边有userRealm 类的注入,而在前边securityManager 的配置的时候,它引入了userRealm ,就是在这里对userRealm 类注入的。
    另外,上边userRealm 方法中设置了一个credentialsMatcher() ,该方法对应的就是new RetryLimitHashedCredentialsMatcher() 类,这里就引入了我们第四个配置文件RetryLimitHashedCredentialsMatcher 类,该类,继承于HashedCredentialsMatcher 类,而HashedCredentialsMatcher 你如果往上找,其实就是实现了CredentialsMatcher 接口,所以这里注入的时候,可以以自定义的RetryLimitHashedCredentialsMatcher 类注入成CredentialsMatcher ,该类的功能主要是将用户输入的密码与查询到的密码进行比较,也就是密码比较器。
    这个shiroConfig 类写的有点多,我理解的也有点不足,有些片面,如果有不对的地方,请读者帮我指正,我及时改过来。
    另外提到的另外三个配置文件,先不写了,放到下一篇吧,今天写的有点多了。先贴上shiro的这四个配置文件的下载地址shiro的配置
    下一篇文章shiro框架—shiro配置介绍(二)

  • 相关阅读:
    Go之map详解
    基于Matlab模拟用于海况海洋学研究的 X 波段雷达系统(附源码)
    【T】03
    UGUI交互组件ScrollBar
    3.5 Android gpu_mem ebpf程序设计原理(一)
    网络安全——黑客自学(笔记)
    基于springboot+vue的旅游系统(前后端分离)
    并发编程的三大特性
    新版本Android Studio logcat日志过滤提示
    1038. 从二叉搜索树到更大和树
  • 原文地址:https://blog.csdn.net/m0_54866636/article/details/126496465