shiro基础知识

什么是权限管理?

权限管理是系统的安全范畴,要求必须是合法的用户才可以访问系统(用户认证),且必须具有该 资源的访问权限才可以访问该 资源(授权)。

认证:对用户合法身份的校验,要求必须是合法的用户才可以访问系统。

授权:访问控制,必须具有该 资源的访问权限才可以访问该 资源。

 

权限模型:标准权限数据模型包括 :用户、角色、权限(包括资源和权限)、用户角色关系、角色权限关系。

权限分配:通过UI界面方便给用户分配权限,对上边权限模型进行增、删、改、查操作。

权限控制:

基于角色的权限控制:根据角色判断是否有操作权限,因为角色的变化 性较高,如果角色修改需要修改控制代码,系统可扩展性不强。

基于资源的权限控制:根据资源权限判断是否有操作权限,因为资源较为固定,如果角色修改或角色中权限修改不需要修改控制代码,使用此方法系统可维护性很强。建议使用。

 

权限管理的解决方案:

 对于粗颗粒权限管理,建议在系统架构层面去解决,写系统架构级别统一代码(基础代码)。

粗颗粒权限:比如对系统的url、菜单、jsp页面、页面上按钮、类方法进行权限管理,即对资源类型进行权限管理。

 

对于细颗粒权限管理

粗颗粒权限:比如用户id001的用户信息(资源实例)、类型为t01的商品信息(资源实例),对资源实例进行权限管理,理解对数据级别的权限管理。

细颗粒权限管理是系统的业务逻辑,业务逻辑代码不方便抽取统一代码,建议在系统业务层进行处理。

 

基于url的权限管理(掌握):

企业开发常用的方法,使用web应用中filter来实现,用户请求url,通过filter拦截,判断用户身份是否合法(用户认证),判断请求的地址是否是用户权限范围内的url(授权)

 

shiro

shiro是一个权限管理框架,是apache下的开源项目。相比spring security框架更简单灵活,spring securityspring依赖较强。shiro可以实现web系统、c/s、分布式等系统 权限管理。

 

shiro认证流程:重要

1subject(主体)请求认证,调用subject.login(token)

2SecurityManager (安全管理器)执行认证

3SecurityManager通过ModularRealmAuthenticator进行认证。

4ModularRealmAuthenticatortoken传给realmrealm根据token中用户信息从数据库查询用户信息(包括身份和凭证)

5realm如果查询不到用户给ModularRealmAuthenticator返回nullModularRealmAuthenticator抛出异常(用户不存在)

6realm如果查询到用户给ModularRealmAuthenticator返回AuthenticationInfo(认证信息)

7ModularRealmAuthenticator拿着AuthenticationInfo(认证信息)去进行凭证(密码 )比对。如果一致则认证通过,如果不致抛出异常(凭证错误)。

subject:主体

Authenticator:认证器( shiro提供)

realm一般需要自定义:相当于数据源,认证器需要realm从数据源查询用户身份信息及权限信息。

授权流程

1、对subject进行授权,调用方法isPermitted"permission"

2SecurityManager执行授权,通过ModularRealmAuthorizer执行授权

3ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据

   调用realm的授权方法:doGetAuthorizationInfo

4realm从数据库查询权限数据,返回ModularRealmAuthorizer

5ModularRealmAuthorizer调用PermissionResolver进行权限串比对

6、如果比对后,isPermitted"permission"realm查询到权限数据中,说明用户访问permission串有权限,否则 没有权限,抛出异常。

使用maven整合shiro和ssm 

web.xml中的配置

额外添加

  <!-- shiro过滤器定义 -->
<filter>  
   <filter-name>shiroFilter</filter-name>  
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
    <init-param>  
    <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->  
    <param-name>targetFilterLifecycle</param-name>  
    <param-value>true</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
       <filter-name>shiroFilter</filter-name>  
       <url-pattern>/*</url-pattern>  
</filter-mapping>

可以查看官方文档

pom.xml中需要添加的配置

<!--使用shiro需要的包  -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-core</artifactId>
   <version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-web</artifactId>
   <version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-ehcache</artifactId>
   <version>1.2.3</version>
</dependency>

自定义spring-shiro.xml即网上说的application-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/mvc
                http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 
               
   
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
   <!--注入自定义的realm  -->
   <property name="realm" ref="myRealm"/>  
 <!-- 注入session管理器 -->
 <property name="sessionManager" ref="sessionManager" />
 <!-- 注入缓存管理器 -->
 <property name="cacheManager" ref="cacheManager"/>
 <!-- 记住我 -->
 <property name="rememberMeManager" ref="rememberMeManager"/>
</bean> 
<!-- 自定义Realm -->
<bean id="myRealm" class="com.csl.realm.MyRealm">
   <!--将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
   <property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean> 
 <!--添加凭证匹配器  -->
   <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
          <property name="hashAlgorithmName" value="md5"/>
          <property name="hashIterations" value="2"/>
    </bean>
<!-- ================================  --> 
<!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="rememberMeCookie" />
</bean>
<!-- 记住我cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- rememberMe是cookie的名字 -->
<constructor-arg value="rememberMe" />
<!-- 记住我cookie生效时间30天 -->
<property name="maxAge" value="2592000" />
</bean>
    <!-- ================================  -->
<!--缓存管理器  -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">  
        <property name="cacheManagerConfigFile" value="classpath:resources/shiro-ehcache.xml"/>  
    </bean>   
    <!-- ================================  -->
<!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- session的失效时长,单位毫秒 -->
        <property name="globalSessionTimeout" value="600000"/>
        <!-- 删除失效的session -->
        <property name="deleteInvalidSessions" value="true"/>
    </bean>


<!-- Shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
   <!-- Shiro的核心安全接口,这个属性是必须的 -->  
   <property name="securityManager" ref="securityManager"/>
   <!-- 身份认证失败,则跳转到登录页面的配置 -->  
   <property name="loginUrl" value="/login.jsp"/>
   <!-- 权限认证失败,则跳转到指定页面 -->  
   <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
   <!-- 自定义filter配置 比如验证码,要在认证之前-->
   <!-- 
<property name="filters">
<map>
将自定义 的FormAuthenticationFilter注入shiroFilter中
<entry key="authc" value-ref="formAuthenticationFilter"/>
</map>
</property>
-->
<!-- Shiro连接约束配置,即过滤链的定义 -->  
   <property name="filterChainDefinitions">  
       <value>  
            /index.jsp=anon
            /login.jsp=anon
/admin*=authc
/role*=anon
                 /login.do= roles[admin],perms["user:view"]
       </value>  
   </property>
</bean>  

<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  

<!-- 开启Shiro注解 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
  <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
   <property name="securityManager" ref="securityManager"/>  
    </bean>  
    <!--开启aop对类代理  -->
    <aop:config proxy-target-class="true"></aop:config>    
</beans>

缓存管理器还得需要导入shiro-ehcache.xml

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <ehcache name="es">  
  3.     <diskStore path="java.io.tmpdir"/>  
  4.          <!--  
  5.        name:缓存名称。  
  6.        maxElementsInMemory:缓存最大数目  
  7.        maxElementsOnDisk:硬盘最大缓存个数。   
  8.        eternal:对象是否永久有效,一但设置了,timeout将不起作用。   
  9.        overflowToDisk:是否保存到磁盘,当系统当机时  
  10.        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。  
  11.        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。  
  12.        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.   
  13.        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。   
  14.        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。  
  15.        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。   
  16.        clearOnFlush:内存数量最大时是否清除。  
  17.        memoryStoreEvictionPolicy:  
  18.             Ehcache的三种清空策略;  
  19.             FIFO,first in first out,这个是大家最熟的,先进先出。  
  20.             LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。  
  21.             LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。  
  22.     -->  
  23.     <defaultCache  
  24.             maxElementsInMemory="10000"  
  25.             eternal="false"  
  26.             timeToIdleSeconds="120"  
  27.             timeToLiveSeconds="120"  
  28.             overflowToDisk="false"  
  29.             diskPersistent="false"  
  30.             diskExpiryThreadIntervalSeconds="120"  
  31.             />  
  32.     <!-- 登录记录缓存锁定10分钟 -->  
  33.     <cache name="passwordRetryCache"  
  34.            maxEntriesLocalHeap="2000"  
  35.            eternal="false"  
  36.            timeToIdleSeconds="3600"  
  37.            timeToLiveSeconds="0"  
  38.            overflowToDisk="false"  
  39.            statistics="true">  
  40.     </cache>  
  41. </ehcache>  

     登录验证原理

 登陆原理

使用FormAuthenticationFilter过虑器实现 ,原理如下:

 

将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl

FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)

FormAuthenticationFilter调用realm传入一个token(username和password)

realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。

如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)


由于FormAuthenticationFilter的用户身份和密码的input的默认值(username和password),修改页面的账号和密码 的input的名称为username和password(可以更改)

 使用PermissionsAuthorizationFilter

spring-shiro.xml中配置url所对应的权限。

 

测试流程:

1、在spring-shiro.xml中配置filter规则

<!--商品查询需要商品查询权限  -->

/items/queryItems.action = perms[item:query]

2、用户在认证通过后,请求/items/queryItems.action

3、被PermissionsAuthorizationFilter拦截,发现需要“item:query”权限

4PermissionsAuthorizationFilter调用realm中的doGetAuthorizationInfo获取数据库中正确的权限

5PermissionsAuthorizationFilteritem:query 和从realm中获取权限进行对比,如果“item:query”在realm返回的权限列表中,授权通过。

==============================================

shiro的过虑器

过滤器简称

对应的java类

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authc.UserFilter

logout

org.apache.shiro.web.filter.authc.LogoutFilter

 

anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数

perms例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"]当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

注解的方式  

 @RequiresPermissions("user:view")


jsp标签

 

Jsp页面添加:

<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>

 

标签名称

标签条件(均是显示标签内容)

<shiro:authenticated>

登录之后

<shiro:notAuthenticated>

不在登录状态时

<shiro:guest>

用户在没有RememberMe时

<shiro:user>

用户在RememberMe时

<shiro:hasAnyRoles name="abc,123" >

在有abc或者123角色时

<shiro:hasRole name="abc">

拥有角色abc

<shiro:lacksRole name="abc">

没有角色abc

<shiro:hasPermission name="abc">

拥有权限资源abc

<shiro:lacksPermission name="abc">

没有abc权限资源

<shiro:principal>

显示用户身份名称

 <shiro:principal property="username"/>     显示用户身份中的属性值

 

 <shiro:hasPermission name="user:view">
        <input type="button" value="有权限"/>
 </shiro:hasPermission>

授权测试

当调用controller的一个方法,由于该 方法加了@RequiresPermissions("item:query") shiro调用realm获取数据库中的权限信息,看"item:query"是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过。

 

当展示一个jsp页面时,页面中如果遇到<shiro:hasPermission name="item:update">shiro调用realm获取数据库中的权限信息,看item:update是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过。

 

问题:只要遇到注解或jsp标签的授权,都会调用realm方法查询数据库,需要使用缓存解决此问题。


shiro缓存

为了避免频繁的授权查询数据库,使用shiro缓存

缓存流程

shiro中提供了对认证信息和授权信息的缓存。shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的。主要研究授权信息缓存,因为授权的数据量大。

 

用户认证通过。

用户第一次授权:调用realm查询数据库

用户第二次授权:不调用realm查询数据库,直接从缓存中取出授权信息(权限标识符)。

使用maven座标<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.2.3</version>
</dependency>

导入jar包 共有两个 有依赖关系


缓存清空

如果用户正常退出,缓存自动清空。

如果用户非正常退出,缓存自动清空。

如果修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。

需要手动进行编程实现:

在权限修改后调用realmclearCache方法清除缓存。

service中,权限修改后调用realm的方法。

realm中定义clearCached方法:

//清除缓存

public void clearCached() {

PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();

super.clearCache(principals);

}

在service层首先要注入realm 调用realm中的方法清除缓存

实现记住我功能

 首要将身份实现serializable接口

 配置rememberMeManager

 登录页面的 input标签的(type="chheckbox")name属性也要为rememberMe








































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章