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

  • Post author:
  • Post category:其他


				版权声明:重在参与,贵在分享					https://blog.csdn.net/wohaqiyi/article/details/79334899				</div>
							            <div id="content_views" class="markdown_views">
						<!-- flowchart 箭头图标 勿删 -->
						<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg>
						<p>接上一篇文章<a href="http://blog.csdn.net/wohaqiyi/article/details/79323589" rel="nofollow" target="_blank">shiro框架—通过系统介绍shiro框架中的实现逻辑</a> <br>

项目已分享到

GitHub

上,如果需要的可以看下,

springboot+shiro项目Git下载地址



shiro在springboot项目中的配置步骤



1、引入依赖

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

 <dependency>
   <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-all</artifactId>
      <version>1.3.2</version>
  </dependency>



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

以下是shiro在springboot项目中的位置:

这里写图片描述

主要的文件有四个

ShiroConfig



RetryLimitHashedCredentialsMatcher



UserRealm



MShiroFilterFactoryBean

。在这里

`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");
        Map<String, Filter>filters = new LinkedHashMap<>();
//      filters.put("perms", urlPermissionsFilter());
        filters.put("anon", new AnonymousFilter());
        bean.setFilters(filters);
        //shiro配置过滤规则少量的话可以用hashMap,数量多了要用LinkedHashMap,保证有序,原因未知
        Map<String, String> 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();
    }
}


下边针对于

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;
    }

既然上边注入的是名字为

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");
        Map<String, Filter>filters = 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<String, String> 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;
    }

以上的

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;
    }

以上又引入了我们的

第三个配置文件

,即

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;
    }

上边的配置,主要是对

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;
    }

以上配置起的名字为

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]

如果出现

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();
    }

上边有

userRealm

类的注入,而在前边

securityManager

的配置的时候,它引入了

userRealm

,就是在这里对

userRealm

类注入的。

另外,上边

userRealm

方法中设置了一个

credentialsMatcher()

,该方法对应的就是

new RetryLimitHashedCredentialsMatcher()

类,这里就引入了我们

第四个配置文件


RetryLimitHashedCredentialsMatcher

类,该类,继承于

HashedCredentialsMatcher

类,而

HashedCredentialsMatcher

你如果往上找,其实就是实现了

CredentialsMatcher

接口,所以这里注入的时候,可以以自定义的

RetryLimitHashedCredentialsMatcher

类注入成

CredentialsMatcher

,该类的功能主要是将用户输入的密码与查询到的密码进行比较,也就是密码比较器。

这个

shiroConfig

类写的有点多,我理解的也有点不足,有些片面,如果有不对的地方,请读者帮我指正,我及时改过来。


另外提到的另外三个配置文件,先不写了,放到下一篇吧,今天写的有点多了。先贴上shiro的这四个配置文件的下载地址


shiro的配置


下一篇文章

shiro框架—shiro配置介绍(二)



版权声明:本文为m0_54866636原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。