zoukankan      html  css  js  c++  java
  • SSM整合Shiro实现RBAC(八)

    戒色诗: 二八佳人体似酥,腰间仗剑斩凡夫。虽然不见人头落,暗里教君骨髓枯。

    在学习这一章节之前,一定要先学习一下老蝴蝶写的这篇文章: SSM实现权限管理(三)

    一. 前期准备

    一.一 数据库准备

    数据库仍然使用的是 rbac数据库, 将 privilage 表数据改变一下, 为 菜单网址也添加 percode 属性。

    有图片

    一.二 前端页面准备

    有图片

    与前面基本一致 。

    一.三 SSM框架准备

    有图片

    web.xml 和依赖,都需要添加。

    二. SSM 整合Shiro 实现RBAC

    通过 SSM 整合Shiro, 就是将 以前 shiro.ini 里面的配置内容,放置到 spring.xml 配置文件里面,

    同样,要注入自定义的Realm, 设置securityManager,密码, 过滤器等内容。

    但 spring整合 Shiro, 远比利用 shiro.ini 配置文件功能强大得多。

    为了比较区别, 将上一章节的 shiro.ini 配置文件拿过来

    [main]
    #加密类
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #加密方式
    credentialsMatcher.hashAlgorithmName=md5
    #加密次数
    credentialsMatcher.hashIterations=10
    #存储散列后的密码是否为16进制 
    credentialsMatcher.storedCredentialsHexEncoded=false
    #配置自定义realm
    myRealm=com.yjl.shiro.MyRealm
    #配置加密
    myRealm.credentialsMatcher=$credentialsMatcher
    #注入自定义的realm
    securityManager.realm=$myRealm
    
    #配置权限
    authc.loginUrl=/User/toLogin
    #跳转到权限不足的路径
    roles.unauthorizedUrl=/NoPermission/NoPermission
    perms.unauthorizedUrl=/NoPermission/NoPermission
    
    [urls]
    #静态页面可以访问
    /static/**=anon
    #跳转到登录页面和登录方法可以访问
    /User/toLogin=anon
    /User/login=anon
    #跳转到主页,需要认证
    /Main/toMain=authc
    /Privilege/getPrivilegeByUId=authc
    #执行方法,不仅需要认证,还需要有相应的方法
    /Dept/add=authc,perms["dept:add"]
    /Dept/update=authc,perms["dept:update"]
    /Dept/list=authc,perms["dept:list"]
    /Dept/delete=authc,perms["dept:delete"]
    /User/toList=authc,perms["user:toList"]
    #退出登录
    /User/logout=logout
    #其他的一切资源,都需要认证
    /**=authc
    

    二.一 添加 pom.xml 依赖

    相比较以前,只多了一个 shiro-all 的依赖信息。

    <!-- shiro依赖 -->
    <dependency>
    	<groupId>org.apache.shiro</groupId>
    	<artifactId>shiro-all</artifactId>
    	<version>1.2.2</version>
    </dependency>
    
    

    二.二 web.xml 配置过滤器

    <!-- 配置shiro 的过滤器 ,注意 filter-name的名称-->
    <filter>
    	<filter-name>shiroFilter</filter-name>
    	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    	<!-- 配置生命周期,由web控制 -->
    	<init-param>
    		<param-name>targetFilterLifecycle</param-name>
    		<param-value>true</param-value>
    	</init-param>
    	<!--一般不配置 targetBeanName参数,默认与filter-name 相同即可 -->
    </filter>
    
    <filter-mapping>
    	<filter-name>shiroFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>
    

    二.三 编写后端代码

    目录结构如下:

    有图片

    与前面的基本一致,没有太大的改动。

    二.三.一 PrivilgeMapper 信息

    注意, PrivilegeMapper.java 和 对应的 .xml 文件里面 仍然有一个根据员工编号和类型查询员工的权限方法

    /*
         * 根据员工编号,查询员工的权限信息
         * */
    	List<Privilege> findByUid(@Param(value="uId") Integer uId,@Param(value="type") Integer type);
    

    对应 xml配置文件:

    <select id="findByUid" resultMap="BaseResultMap">
        select 
        <include refid="Base_Column_List" />
        from privilege a where a.id in (
    		select rp.pid from user_role ur left join role_privilege rp
    		on ur.rid=rp.rid
    		where ur.uid=#{uId,jdbcType=INTEGER}
    	)
    	<if test="type != null and type !=''" >
          and a.type=#{type,jdbcType=INTEGER}
        </if>
      </select>
    

    这也是与前面一样的, 比较重要,故单独列一下。

    二.三.二 登录和注销功能

    @RequestMapping("/login")
    	@ResponseBody
    	public Map<String,Object> login(User userInfo){
    		Map<String,Object> dataMap=new HashMap<String,Object>();
    		
    		//1. 获取 Subject
    		Subject subject=SecurityUtils.getSubject();
    		//2. 配置Token
    		//配置用户名和密码
    		UsernamePasswordToken token=new UsernamePasswordToken(userInfo.getCode(), userInfo.getPassword());
    		try{
    			//进行登录
    			subject.login(token);
    			
    			Session session=subject.getSession();
    			User user=(User)subject.getPrincipal();
    			session.setAttribute("loginUser", user);
    			dataMap.put("response_status",true);
    		}catch(Exception e){
    			e.printStackTrace();
    			dataMap.put("response_status",false);
    			dataMap.put("error_msg","001");
    		}
    		
    		return dataMap;
    		
    	} 
    	@RequestMapping("/logout")
    	//退出登录
    	public String logout(HttpSession session){
    		//注销
    		session.invalidate();
    		return "login";
    	}
    

    二.四 编写自定义 Realm

    这个跟前面也一样, 路径为: com.yjl.shiro.MyRealm

    package com.yjl.shiro;
    
    import java.util.List;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.yjl.mapper.PrivilegeMapper;
    import com.yjl.mapper.UserMapper;
    import com.yjl.pojo.Privilege;
    import com.yjl.pojo.User;
    
    public class MyRealm extends AuthorizingRealm{
    	@Autowired
    	private UserMapper userMapper;
    	@Autowired
    	private PrivilegeMapper privilegeMapper;
    	@Override
    	public String getName() {
    		return "myRealm";
    	}
    	//授权
    	@Override
    	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
    		//获取当前的登录用户
    		System.out.println("获取权限");
    		User user=(User)paramPrincipalCollection.getPrimaryPrincipal();
    	
    		if(user==null){
    			return null;
    		}
    		//根据 id,获取相应的权限信息
    		
    		List<Privilege> privilegeList=privilegeMapper.findByUid(user.getId(),null);
    		
    		SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
    		
    		for(Privilege priInfo:privilegeList){
    			//仍然放置的是权限的标识 , 注意,这儿获取的是所有的非一级 权限,包括菜单和按钮。
    			if(priInfo!=null&&priInfo.getPercode()!=null){
    				info.addStringPermission(priInfo.getPercode());
    			}
    		}
    		return info;
    		
    	}
    	//认证
    	@Override
    	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken paramAuthenticationToken)
    			throws AuthenticationException {
    		System.out.println("进入认证");
    		String code=(String)paramAuthenticationToken.getPrincipal();
    		//根据用户名,去查询相应的数据
    		User user=userMapper.selectInfoByCode(code);	
    		if(user==null){
    			//没有查询出来
    			return null;
    		}
    		SimpleAuthenticationInfo simpleAuthenticationInfo=
    				new SimpleAuthenticationInfo(user,user.getPassword(),
    						//传入转换后的盐
    						ByteSource.Util.bytes(user.getSalt()),getName());
    		return simpleAuthenticationInfo;
    		
    	}
    }
    

    二.五 配置shiro 文件

    文件的名称是 applicationContext-shiro.xml ,在读取 spring配置文件时,也读取进来了。 不用单独配置。

    web.xml 文件中:

    <!-- 启动spring -->
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext-*.xml</param-value>
      </context-param>
    

    二.五.一 配置密码加密验证

    <!-- 凭证匹配器 -->  
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">  
        <property name="hashAlgorithmName" value="md5"/>  
        <property name="hashIterations" value="10"/>  
        <property name="storedCredentialsHexEncoded" value="false"/>  
    </bean> 
    

    与 以前的 shiro.ini 文件

    #加密类
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #加密方式
    credentialsMatcher.hashAlgorithmName=md5
    #加密次数
    credentialsMatcher.hashIterations=10
    #存储散列后的密码是否为16进制 
    credentialsMatcher.storedCredentialsHexEncoded=false
    

    是匹配的。

    二.五.二 配置自定义realm, 注入密码匹配器

    <!-- 配置自定义的ream -->
    <bean id="myRealm" class="com.yjl.shiro.MyRealm">
    	<!-- 配置密码管理 -->
    	<property name="credentialsMatcher" ref="credentialsMatcher"></property>
    </bean>
    

    与以前的

    #配置自定义realm
    myRealm=com.yjl.shiro.MyRealm
    #配置加密
    myRealm.credentialsMatcher=$credentialsMatcher
    

    是匹配的。

    二.五.三 配置 securityManager 对象,注入自定义Realm

    <!-- 配置securityManager, 将一些信息注入到里面 -->
    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    	<property name="realm" ref="myRealm"></property>
    </bean>
    

    与以前的:

    #配置自定义realm
    myRealm=com.yjl.shiro.MyRealm
    #配置加密
    myRealm.credentialsMatcher=$credentialsMatcher
    #注入自定义的realm
    securityManager.realm=$myRealm
    

    是匹配的。

    securityManager 不仅可以注入自定义Realm,还可以注入缓存,session管理,记住我等功能。

    二.五.四 配置 生命周期

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

    与 web.xml 中 shiroFilter 部分的 targetFilterLifecycle 属性为 true 相对应。

    二.五.五 配置 shiroFilter 过滤器

    <!-- 配置shiroFilter 规则 ,与web.xml的过滤器中的 targetBeanName 名称保持一致-->
    	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<property name="securityManager" ref="securityManager"></property>	
    		
    		<!-- 配置登录,登录成功和没有权限的三个路径 ,以前的main 部分-->
    		<property name="loginUrl" value="/User/toLogin"></property>
    		<!-- <property name="successUrl" value="/Main/toMain"></property>  -->
    		<property name="unauthorizedUrl" value="/Privilege/noPrivilege"></property>
    		
    		<!-- 配置规则 ,即urls部分-->
    		<property name="filterChainDefinitions">
    			<value>
    				<!-- 配置拦截器 -->
    				/static/**=anon
    		 		/User/toLogin=anon
    		 		/User/login=anon
    		 		/User/logout=logout
    		 		/Main/toMain=authc
    		 		<!-- 没有配置的路径走认证 -->
    		 		/**=authc
    			</value>
    		</property>
    	</bean>
    

    常用的,主要分为五个部分,下面依次讲解。

    注意点: 该工厂 ShiroFilterFactoryBean 实例化bean 的 必须是 shiroFilter ,这个名称必须要与 web.xml中关于shiro配置的 filter-name 的名称相同。

    二.五.五.一 注入 securityManager 对象

    	<property name="securityManager" ref="securityManager"></property>	
    

    二.五.五.二 配置 loginUrl 路径

    	<property name="loginUrl" value="/User/toLogin"></property>
    

    loginUrl 指的是 ,如果用户没有认证,或者没有通过认证,跳转到哪?

    一般跳转到去登录页面的那个方法。

    与以前 [main] 里面的

    #配置权限
    authc.loginUrl=/User/toLogin
    

    对应。

    二.五.五.三 配置 successUrl 路径

    一般不配置这一个路径。

    这个路径指的是,如果员工认证通过,跳转到哪? 认证通过,会跳转到主页, 但跳转到主页,一般都是通过程序代码固化下来,并不在这儿进行配置。 这个路径的实际意义是, 如果认证通过,就跳转到上一次要访问的页面。

    即,如果用户 admin 想访问 用户的界面, /项目名/User/toList, 当用户手动输入这个网址时,发现没有认证,就会跳转到登录的界面,当用户登录成功后, 这个时候,会跳转到哪呢? 并不会跳转到主页那,而是跳转到上一次想访问的那个页面, 即 /项目名/User/toList 页面, 展示用户的相应信息。 这个功能还是非常好的,所以一般不配置这个属性值。

    二.五.五.四 配置 unauthorizedUrl 路径

    <property name="unauthorizedUrl" value="/Privilege/noPrivilege"></property>
    

    unauthorizedUrl 指的是权限不足时,跳转到哪?

    与以前 [main] 部分的

    #跳转到权限不足的路径
    roles.unauthorizedUrl=/NoPermission/NoPermission
    perms.unauthorizedUrl=/NoPermission/NoPermission
    

    相对应。

    二.五.五.五 filterChainDefinitions 配置拦截器

    <!-- 配置规则 ,即urls部分-->
    <property name="filterChainDefinitions">
    	<value>
    		<!-- 配置拦截器 -->
    		/static/**=anon
     		/User/toLogin=anon
     		/User/login=anon
     		/User/logout=logout
     		/Main/toMain=authc
     		<!-- 没有配置的路径走认证 -->
     		/**=authc
    	</value>
    </property>
    

    这个与以前的 [urls] 部分

    [urls]
    #静态页面可以访问
    /static/**=anon
    #跳转到登录页面和登录方法可以访问
    /User/toLogin=anon
    /User/login=anon
    #跳转到主页,需要认证
    /Main/toMain=authc
    /Privilege/getPrivilegeByUId=authc
    #执行方法,不仅需要认证,还需要有相应的方法
    /Dept/add=authc,perms["dept:add"]
    /Dept/update=authc,perms["dept:update"]
    /Dept/list=authc,perms["dept:list"]
    /Dept/delete=authc,perms["dept:delete"]
    /User/toList=authc,perms["user:toList"]
    #退出登录
    /User/logout=logout
    #其他的一切资源,都需要认证
    /**=authc
    

    相对应。

    发现,并没有 /Dept/add,/Dept/list ... 等权限的配置。

    一个系统中,会有很多的权限,不能添加一个就在 filterChainDefinitions 部分配置一个,那么就太烦繁了, 一般都是通过 注解的方式进行配置权限。

    关于注解方式配置权限,老蝴蝶下面会讲解。

    二.五.六 applicationContext-shiro.xml 配置文件合并

    将上面的这些部分,进行合并,得到一个总的配置信息。

    <!-- 凭证匹配器 -->  
    	<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">  
    	    <property name="hashAlgorithmName" value="md5"/>  
    	    <property name="hashIterations" value="10"/>  
    	    <property name="storedCredentialsHexEncoded" value="false"/>  
    	</bean>  
    	
    	
    	<!-- 配置自定义的ream -->
    	<bean id="myRealm" class="com.yjl.shiro.MyRealm">
    		<!-- 配置密码管理 -->
    		<property name="credentialsMatcher" ref="credentialsMatcher"></property>
    	</bean>
    	
    	
    	<!-- 配置securityManager, 将一些信息注入到里面 -->
    	
    	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    		<property name="realm" ref="myRealm"></property>
    	</bean>
    	
    	<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
    	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 
    
    	<!-- 配置shiroFilter 规则 ,与web.xml的过滤器中的 targetBeanName 名称保持一致-->
    	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<property name="securityManager" ref="securityManager"></property>	
    		
    		<!-- 配置登录,登录成功和没有权限的三个路径 ,以前的main 部分-->
    		<property name="loginUrl" value="/User/toLogin"></property>
    		<!-- <property name="successUrl" value="/Main/toMain"></property>  -->
    		<property name="unauthorizedUrl" value="/Privilege/noPrivilege"></property>
    		
    		<!-- 配置规则 ,即urls部分-->
    		<property name="filterChainDefinitions">
    			<value>
    				<!-- 配置拦截器 -->
    				/static/**=anon
    		 		/User/toLogin=anon
    		 		/User/login=anon
    		 		/User/logout=logout
    		 		/Main/toMain=authc
    		 		<!-- 没有配置的路径走认证 -->
    		 		/**=authc
    			</value>
    		</property>
    	</bean>
    

    二.六 前端页面

    前端页面的配置信息,与以前基本一致,故不重复写了。

    重点提一下, dept.jsp 页面的按钮标识控制部分。

    dept.jsp 页面设置标识:

    <shiro:hasPermission name="dept:add">
    		<script>
    			 sessionStorage.setItem("dept_update",true);
    		</script>
    	</shiro:hasPermission>
    	<shiro:hasPermission name="dept:update">
    		<script>
    			 sessionStorage.setItem("dept_update",true);
    		</script>
    	</shiro:hasPermission>
    	<shiro:hasPermission name="dept:delete">
    		<script>
    			 sessionStorage.setItem("dept_delete","true")
    		</script>
    	</shiro:hasPermission>
    <script type="text/javascript" src="${pageContext.request.contextPath}/static/js/dept.js"></script>
    

    dept.js 脚本里面获取标识:

    //看是否有添加的权限
    var add=sessionStorage.getItem("dept_add");
    if(add){
    	$("#add").show();
    }else{
    	$("#add").hide();
    }
    //看修改和删除是否显示和隐藏
    function operateFormatter(value, row, index) {
    	var update=sessionStorage.getItem("dept_update");
    	var del=sessionStorage.getItem("dept_delete");
    	//console.log("update:"+update+",del:"+del);
    	var udpateIcon="";
    	if(update){
    		udpateIcon='<a class="update text-primary" href="javascript:void(0)" data-toggle="tooltip" title="修改">'
    			+'<i class="fa fa-pencil"></i>&nbsp修改&nbsp;&nbsp;&nbsp;&nbsp;</a>';
    	}
    	var delIcon="";
    	if(del){
    		 delIcon='<shiro:hasPermission name="dept:delete"><a class="delete text-danger" href="javascript:void(0)" data-toggle="tooltip" title="撤销">'
    			+'<i class="fa fa-minus"></i>&nbsp;删除&nbsp;</a></shiro:hasPermission">';
    	}
    	return udpateIcon+delIcon;
    }
    
    

    二.七 测试验证

    输入网址: http://localhost:8080/SSM_Shiro_RBAC/User/toLogin

    输入admin,1234 进行测试 (错误的密码情况就不测试了)

    有图片

    有图片

    看起来,是正常的,

    然后,当手动输入 部门的添加功能呢, http://localhost:8080/SSM_Shiro_RBAC/Dept/add

    有图片

    响应是正常的, 这是因为我们没有配置具体的权限验证呢。

    三. 注解式配置具体权限

    在 applicationContext-shiro.xml 中的 filterChainDefinitions 属性中,虽然可以像 shiro.ini 那样,配置具体的权限,但是当权限过多时,再那样配置,就不太好了。

    所以 shiro 提供了注解式配置。

    有图片

    注解名称 意义
    @RequiresPermissions 必须具有权限
    @RequiresAuthentication 必须通过认证
    @RequiresGuest 匿名访客
    @RequiresRoles 必须具有角色
    @RequiresUser 是否被记忆,两个含义,一个成功认证的,二是记住我登录的

    只需要在某个具体的方法上面加入相应的注解即可。

    如: 在 部门的 add() 方法上面 ,添加 @RequiresPermissions("Dept:add") 注解

    @RequiresPermissions("dept:add")
    @RequestMapping("/add")
    @ResponseBody
    public Map<String,Object> add(){
    	System.out.println("实现部门添加的方法");
    	Map<String,Object> map=new HashMap<String,Object>();
    	map.put("response_status",true);
    	return map;
    }
    

    shiro 会先看一下,当前会话登录的用户,是否具有 权限 Dept:add ,如果有才执行 add 方法,如果没有,不执行,抛出异常。

    会从 MyRealm 里面的 doGetAuthorizationInfo() 返回对象里面取数据进行比较。

    一般常用的,就是 权限验证。

    @Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequiresPermissions
    {
      String[] value();
      
      Logical logical() default Logical.AND;
    }
    

    默认是 AND

    配置单个权限:

    @RequiresPermissions("dept:add")
    

    表示具有权限 dept:add 才执行

    配置多个权限与:

    @RequiresPermissions({"dept:add","dept:update"})
    

    表示具有权限 dept:add和 dept:update, 两个权限同时具有才执行

    配置多个权限或:

    @RequiresPermissions(value={"dept:add","dept:update"},logical=Logical.OR)
    

    表示具有权限 dept:add 或者 dept:update 任意一个均可。

    四. 注解式声明 Shiro 权限

    直接在方法上 添加 @RequiresPermissions("dept:add") 注解是不生效的,必须要动态代理一下。

    由于 权限注解被放置在 Action类的方法上,所以配置动态代理,必须要配置在 springmvc.xml 文件里面。

    applicationContext-shiro.xml 文件不用动。

    四.一 配置注解

    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    		<property name="securityManager" ref="securityManager"></property>
    </bean>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 
    	depends-on="lifecycleBeanPostProcessor">
    	<property name="proxyTargetClass" value="true"></property>
    </bean> 
    

    四.二 验证

    admin 没有 部门的 添加权限, 当手动输入添加的地址时,会报错。

    有图片

    org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [dept:add]

    当输入 delete 网址时:

    有图片

    说明,注解权限是可以的。

    需要处理一下异常,如果没有权限的话,就跳转到 无权限那个提示页面。

    四.三 springmvc.xml 捕获异常

    关于 SpringMVC 的异常处理,可以看老蝴蝶以前写的 SpringMVC系列文章。

    <!-- 配置异常 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    	<property name="exceptionMappings">
    		 <props>
    		 	<!-- 每个异常类,对应一个jsp -->
    		 	 <!-- 没有权限,到哪个具体的页面 -->
            	<prop key="org.apache.shiro.authz.AuthorizationException">noPrivilege</prop>
            	<!-- 没有认证通过 -->
            	<prop key="org.apache.shiro.authc.AuthenticationException">login</prop>
    			<!-- 可继续添加多个 ,如StudentException -->
    		</props>
    	</property>
    </bean>
    

    配置好之后,重新测试

    当登录成功后,再输入 部门添加的网址后,

    有图片

    注意观察控制台的打印信息:

    有图片

    发现,自定义Realm 中的授权方法,被执行了很多次, 每一个注解方法,每一个前端的 shiro:hasPermission 判断,都需要执行一下这个方法,频繁地取数据, 影响数据库的效率, 所以我们需要做缓存的处理。 老蝴蝶下一章节再讲缓存。


    本章节代码链接为:

    链接:https://pan.baidu.com/s/1qjZcQfKQa4f_SdCjXvTtxQ 
    提取码:ko9l 
    

  • 相关阅读:
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(28)-系统小结
    用谷歌浏览器来当手机模拟器
    解决PHP使用CVS导出Excel乱码问题
    Linux系统中关于Sqlite3中文乱码问题及解决办法
    解决excel日期变成数字的问题
    基于IE的多标签的浏览器-世界之窗2.4
    Thinkphp 获取所有子分类或父分类ID
    PhpExcel 删除默认的Sheet
    Mysql 创建表时错误:Tablespace for table `tablexx` exists. Please DISCARD the tablespace before IMPORT.
    关于ThinkPhp中getField方法存在的问题
  • 原文地址:https://www.cnblogs.com/yjltx/p/14343185.html
Copyright © 2011-2022 走看看