国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

SpringBoot+Shiro學習之數據庫動態權限管理和Redis緩存

2019-11-08 18:43:11
字體:
來源:轉載
供稿:網友

發現問題,需找解決思路。

之前我們整合Shiro,完成了登錄認證和權限管理的實現,登錄認證沒什么說的,需要實現AuthorizingRealm中的doGetAuthenticationInfo方法進行認證,但是我們在實現doGetAuthorizationInfo權限控制這個方法的時候發現以下兩個問題:

第一個問題:我們在ShiroConfig中配置鏈接權限的時候,每次只要有一個新的鏈接,或則權限需要改動,都要在ShiroConfig.java中進行權限的修改。而且改動后還需要重新啟動程序新的權限才會生效,很麻煩。解決辦法就是將這些鏈接的權限存入數據庫,在前端可以提供增刪改查的功能,在配置文件中編寫權限的時候從數據庫讀取,當權限發生變更的時候利用ShiroFilterFactoryBean的清空功能,先clear,再set。這樣就可以做到到動態的管理權限了。

第二個問題:每次在訪問設置了權限的頁面時,都會去執行doGetAuthorizationInfo方法來判斷當前用戶是否具備訪問權限,由于在實際情況中,權限是不會經常改變的。解決辦法就是進行緩存處理。

個人博客:http://z77z.oschina.io/

此項目下載地址:https://git.oschina.net/z77z/sPRingboot_mybatisplus

第一個問題解決步驟

建立數據庫

我們從ShiroConfig中的filterChainDefinitionMap.put("/add", "perms[權限添加]"); 配置可以看出,我們需要存儲鏈接,和鏈接需要具備的權限這兩個關鍵字段。還有這個權限的讀取是有順序的,所以還要進行排序控制,所以我新建表為:

-- ------------------------------ Table structure for sys_permission_init-- ----------------------------DROP TABLE IF EXISTS `sys_permission_init`;CREATE TABLE `sys_permission_init` ( `id` varchar(255) NOT NULL, `url` varchar(255) DEFAULT NULL COMMENT '鏈接地址', `permission_init` varchar(255) DEFAULT NULL COMMENT '需要具備的權限', `sort` int(50) DEFAULT NULL COMMENT '排序', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

當然可以按實際情況進行表的設計,這里只做簡單學習。

改造ShiroConfig.java

改造前:

@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必須設置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授權界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 攔截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); // 配置不會被攔截的鏈接 順序判斷 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/AjaxLogin", "anon"); // 配置退出過濾器,其中的具體的退出代碼Shiro已經替我們實現了 filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/add", "perms[權限添加]"); // <!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了; // <!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問--> filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro攔截器工廠類注入成功"); return shiroFilterFactoryBean;}

改造后:

@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必須設置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授權界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 權限控制map. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //從數據庫獲取 List<SysPermissionInit> list = sysPermissionInitService.selectAll(); for (SysPermissionInit sysPermissionInit : list) { filterChainDefinitionMap.put(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit()); } shiroFilterFactoryBean .setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro攔截器工廠類注入成功"); return shiroFilterFactoryBean;}

這里的selectAll()就是從數據庫查詢之前創建的權限管理列表,這里就不貼具體的查詢代碼了。

添加權限

在數據庫中添加權限如下圖:

這里寫圖片描述

現在啟動程序,在控制臺可以發現啟動的時候程序在數據庫查詢了權限的列表信息。做到這步之后還沒有達到動態的目的,比如現在到數據庫手動修改/add鏈接的權限,這時不重啟程序,權限是不會修改的。

動態更改權限實現

ShiroService.java:

/** * * @author 作者: z77z * @date 創建時間:2017年2月15日 下午4:16:07 */@Servicepublic class ShiroService { @Autowired ShiroFilterFactoryBean shiroFilterFactoryBean; @Autowired SysPermissionInitService sysPermissionInitService; /** * 初始化權限 */ public Map<String, String> loadFilterChainDefinitions() { // 權限控制map.從數據庫獲取 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); List<SysPermissionInit> list = sysPermissionInitService.selectAll(); for (SysPermissionInit sysPermissionInit : list) { filterChainDefinitionMap.put(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit()); } return filterChainDefinitionMap; } /** * 重新加載權限 */ public void updatePermission() { synchronized (shiroFilterFactoryBean) { AbstractShiroFilter shiroFilter = null; try { shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean .getObject(); } catch (Exception e) { throw new RuntimeException( "get ShiroFilter from shiroFilterFactoryBean error!"); } PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter .getFilterChainResolver(); DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver .getFilterChainManager(); // 清空老的權限控制 manager.getFilterChains().clear(); shiroFilterFactoryBean.getFilterChainDefinitionMap().clear(); shiroFilterFactoryBean .setFilterChainDefinitionMap(loadFilterChainDefinitions()); // 重新構建生成 Map<String, String> chains = shiroFilterFactoryBean .getFilterChainDefinitionMap(); for (Map.Entry<String, String> entry : chains.entrySet()) { String url = entry.getKey(); String chainDefinition = entry.getValue().trim() .replace(" ", ""); manager.createChain(url, chainDefinition); } System.out.println("更新權限成功?。?); } }}

這樣,可以在修改權限之后,執行updatePermission()這個方法,權限就會先被clear,然后重新查詢權限列表后再set。動態修改就實現了!

注意:在本學習項目里面,我在設置登錄用戶的權限的時候是寫死了的,所以每個登錄用戶權限都是一樣的,實際開發中在MyShiroRealm文件中設置登錄用戶的權限是從數據庫獲取的。還有在實際開發中sys_permission_init權限管理這種表是會在前端提供增刪改查功能的,我學習的時候是直接在數據庫手動修改。說到底,本人很懶!

第二個問題的解決步驟

我們知道Shiro 提供了一系列讓我們自己實現的接口,包括org.apache.shiro.cache.CacheManager 、org.apache.shiro.cache.Cache 等接口。那么我們要對這些做實現,就實現了 Shiro 對 session 和用戶認證信息、用戶緩存信息等的緩存,存儲。我們可以用緩存,如 Redis 、 memcache 、 EHCache 等,甚至我們可以用數據庫,如 OracleMySQL 等,都可以,只有效率的快慢問題,功能都可以達到。

那么我的教程是采用了 Redis ,而且是用了Jedis 。Jedis 可以實現pool 和hash 的集群Redis 。

本來我想是在網上學習學習,自己實現redis的集成。最后發現已經有大神已經做了這個插件,對shiro提供的CacheManager,Cache ,這些接口使用redis都有了很好的實現。我就不需要再費心學習了,我們就直接拿來用。

pom.xml依賴添加

<!-- shiro+redis緩存插件 --><dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>2.4.2.1-RELEASE</version></dependency>

改造ShiroConfig.java文件

/** * @author 作者 z77z * @date 創建時間:2017年2月10日 下午1:16:38 * */@Configurationpublic class ShiroConfig { @Autowired SysPermissionInitService sysPermissionInitService; @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必須設置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授權界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 權限控制map. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); // 從數據庫獲取 List<SysPermissionInit> list = sysPermissionInitService.selectAll(); for (SysPermissionInit sysPermissionInit : list) { filterChainDefinitionMap.put(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit()); } shiroFilterFactoryBean .setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro攔截器工廠類注入成功"); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 設置realm. securityManager.setRealm(myShiroRealm()); // 自定義緩存實現 使用redis securityManager.setCacheManager(cacheManager()); // 自定義session管理 使用redis securityManager.setSessionManager(SessionManager()); return securityManager; } /** * 身份認證realm; (這個需要自己寫,賬號密碼校驗;權限等) * * @return */ @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); return myShiroRealm; } /** * 配置shiro redisManager * * @return */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setExpire(1800);// 配置過期時間 // redisManager.setTimeout(timeout); // redisManager.setPassWord(password); return redisManager; } /** * cacheManager 緩存 redis實現 * * @return */ public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * RedisSessionDAO shiro sessionDao層的實現 通過redis */ public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * shiro session的管理 */ public DefaultWebSessionManager SessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); return sessionManager; }}

這里,因為使用的是redis來做容器緩存,所以要創建redisManager來配置shiro,SessionManager(),cacheManager()這兩個類都是插件給我們寫好了的,里面就是對shiro提供的接口的redis實現方式。

使用插件就是這么簡單,直接啟動程序,多訪問幾次具有權限的頁面,查看控制臺發現,權限認證方法:MyShiroRealm.doGetAuthorizationInfo()會只執行了一次。說明我們的緩存生效了。

總結

到此,我們集成shiro和redis,學習了一下功能的實現:

用戶必須要登陸之后才能訪問定義鏈接,否則跳轉到登錄頁面,被禁用戶不能登錄。并且對一些敏感操作鏈接設置權限,只有滿足權限的才可以訪問。每個鏈接的權限信息保存在數據庫,可以動態進行設置,并且熱加載權限。使用redis對shiro的用戶信息進行緩存,不用每次都去執行MyShiroRealm.doGetAuthorizationInfo()權限認證方法。之前有很多同學下載我的項目時,運行會報錯,那是因為最近都在不斷修改提交,有可能會出現版本問題,現在我在我的碼云上面創建了stable_version分支,都是可以跑起來的。sqltable放在resource目錄下面。下一博,我應該會寫對在線用戶的管理,踢出登錄的功能學習記錄。

香蕉硬幣點贊走一波啦。。。。。。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 松滋市| 二连浩特市| 湛江市| 项城市| 巢湖市| 斗六市| 西青区| 福州市| 京山县| 朝阳县| 中西区| 开平市| 台北县| 栖霞市| 永登县| 社旗县| 承德县| 夏津县| 邛崃市| 滦南县| 凤阳县| 泰和县| 扎赉特旗| 公安县| 介休市| 二手房| 得荣县| 繁昌县| 开远市| 盐亭县| 栖霞市| 安宁市| 商都县| 富民县| 嘉义市| 宜兰市| 绥江县| 诸暨市| 兰西县| 香河县| 镇雄县|