一文看懂Shiro权限管理框架!
文章目錄
- 1.JavaWeb中的權限控制
- 2.權限框架核心知識ACL和RBAC
- 2.1.ACL和RBAC簡介
- 2.2主流權限框架介紹
- 3.Shiro架構和基本概念
- 3.1.Shiro的4大核心模塊
- 3.2.Shiro權限控制運行流程
- 4.Shiro簡單API案例
- 4.1.項目搭建所需依賴
- 4.2.Shiro認證簡單實操
- 4.3.Shiro授權簡單實操
- 5.安全數據來源Realm
- 5.1.Realm簡介和繼承關系
- 5.2.Shiro內置IniRealm權限驗證
- 5.3.Shiro內置JdbcRealm權限驗證
- 5.4.Shiro自定義Realm權限配置
- 5.5.Shiro源碼認證授權流程
- 6.Shiro權限認證Web案例
- 6.1.Shiro內置的過濾器
- 6.2.Shiro的Filter配置路徑
- 6.3.Shiro數據安全之數據加解密
- 6.4.Shiro權限控制注解
- 6.5.Shiro緩存模塊講解
- 6.6.Shiro Session模塊講解
- 7.SpringBoot2.x整合Shiro
- 7.1.數據庫設計
- 7.2.Maven項目搭建
- 7.3.編寫查詢用戶全部信息接口
- 7.4.開發自定義CustomRealm
- 7.5.ShiroFilterFactoryBean配置
- 7.6.自定義SessionManager驗證
- 7.7.API攔截驗證案例
- 7.8.Shiro密碼加密處理
- 8.權限控制性能提升
- 8.1.自定義Shiro Filter過濾器
- 8.2.Redis整合CacheManager
- 8.3.Redis整合SessionManager
- 8.4.ShiroConfig常用的Bean配置
- 9.分布式應用鑒權方式
- 9.1.自定義SessionId
1.JavaWeb中的權限控制
(1)什么是權限控制
- 忽略特別細的概念,比如權限能細分很多種,功能權限,數據權限,管理權限等
- 理解兩個概念:用戶和資源,讓指定的用戶,只能操作指定的資源(CRUD)
(2)javaweb中怎么處理
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws Exception {HttpServletRequest httpRequest=(HttpServletRequest)request;HttpServletResponse httpResponse=(HttpServletResponse)response;HttpSession session=httpRequest.getSession();if(session.getAttribute("username")!=null){chain.doFilter(request, response);} else {httpResponse.sendRedirect(httpRequest.getContextPath()+"/login.jsp");}}2.權限框架核心知識ACL和RBAC
2.1.ACL和RBAC簡介
- ACL:Access Control List 訪問控制列表
- 以前盛行的一種權限設計,它的核心在于用戶直接和權限掛鉤
- 優點:簡單易用、開發便捷
- 缺點:用戶和權限直接掛鉤,導致在授權時的復雜性,比較分散,不便于管理
- 案例:常見的文件系統權限設計,直接給用戶加權,類似Linux系統中的chmod
- RBAC:Role Based Access Control
- 基于角色的訪問控制系統。權限與角色相關聯,用戶通過適當的角色的成員而獲得角色的權限
- 優點:簡化了用戶與權限的管理,通過對用戶進行分類,使得角色與權限關聯起來
- 缺點:開發相比ACL復雜
- 案例:基于RBAC模型的權限驗證框架有Apache Shiro、Spring Security
- 總結:權限設計不能太過于復雜,否則性能會下降
2.2主流權限框架介紹
(1)什么是Spring Security
- 官網:https://spring.io/projects/spring-security
(2)什么是Apache Shiro
- 官網:https://github.com/apache/shiro
- 兩個的優缺點
- Apache Shiro比Spring Security使用更簡單
- Shiro功能強大、簡潔、靈活,不跟任何的框架或者容器綁定,可以獨立運行
- SpringSecurity對Spring體系支持比較友好,脫離Spring體系開發很難
- SpringSecuity支持Oauth鑒權,Shiro需要自己實現
3.Shiro架構和基本概念
3.1.Shiro的4大核心模塊
(1)Shiro的四大核心模塊分為身份認證、授權、會話管理和加密
- 身份認證
- Authentication,身份認證,一般就是登錄
- 授權
- Authorization,給用戶分配角色或者訪問某些資源的權限
- 會話管理
- Session Management,用戶的會話管理員,多數情況下是web Session
- 加密
- Cryptogarphy,數據加解密,你如密碼加解密等
(2)Shiro架構圖
3.2.Shiro權限控制運行流程
(1)Shiro常見名稱
- Subject
- 我們把用戶或者程序稱為主體,主體去訪問資源或者系統
- SecurityManager
- 安全管理器,Subject的認證和授權都在安全管理器下進行
- Authenticator
- 認證器,主要負責Subject的認證
- Realm
- 數據域,Shiro和安全數據的連接器,好比jdbc連接數據庫;通過realm獲取認證授權的相關信息
- Authorizer
- 授權器,主要負責Subject的授權,控制subject擁有的角色或者權限
- Cryptography
- 加解密,Shiro包含易于使用和理解的數據加密方法,簡化了很多復雜的API
- CacheManager
- 緩存管理器,比如認證或者授權信息,通過緩存進行管理,提高性能
- SessionManager
- 會話管理器,大多數是web session
- SessionDAO
- SessionDAO即會話,是對session會話的一套接口,比如要將session存儲到數據庫。
4.Shiro簡單API案例
4.1.項目搭建所需依賴
- 環境準備:maven3.5+jdk8+springboot+idea
4.2.Shiro認證簡單實操
(1)Shiro的認證流程
- 創建Security Manager:Security Manager是用來提供安全服務的,所以在做shiro認證的時候要先創建此對象
- 主題Subject提交請求給Security Manager
- Security Manager調用Authenticator組件做認證
- Authenticator通過Realm來從數據源中獲取認證數據
(2)編碼測試
@SpringBootTest public class Test{//聲明SecurityManagerDefaultSecurityManager securityManager = new DefaultSecurityManager();//聲明RealmSimpleAccountRealm accountRealm = new SimpleAccountRealm();@BeforeTestpublic void init(){accountRealm.addAccount("lixiang","123456");accountRealm.addAccount("lisi","123456");//構建環境securityManager.setRealm(accountRealm);}@Testpublic void test(){SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernameAndPasswordToken token = new UsernameAndPasswordToken("lixiang","123456");subject.login(token);System.out.println("認證結果:"+subject.isAuthenticated());} }(3)測試結果
4.3.Shiro授權簡單實操
(1)常用API
//是否有對應角色 subject.hasRole("root")//獲取subject名 subject.getPrincipal()//檢查是否有對應的角色,無返回值,直接在SecurityManager里面進行判斷 subject.checkRole("admin")//檢查是否有對應的角色 subject.hasRole("admin")//退出登錄 subject.logout();(2)編碼實操
@Testvoid contextLoads() {SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("lixiang","123456");subject.login(usernamePasswordToken);System.out.println("認證結果:"+subject.isAuthenticated());System.out.println("獲取subject主體的唯一標識:"+subject.getPrincipal());//檢查是否有對應角色,無返回值,直接在SecurityManager里面進行判斷subject.checkRole("admin");//檢查是否有對應的角色System.out.println("是否有對應角色:"+subject.hasRole("admin"));//退出登錄subject.logout();System.out.println("認證結果:"+subject.isAuthenticated());}5.安全數據來源Realm
5.1.Realm簡介和繼承關系
- Realm的作用:Shiro從Realm中獲取安全的數據
- Realm中的兩個概念:
- principal:主體的標識,可以有多個,但是必須要有一個唯一性的,常見的用戶名、手機號、郵箱
- credential:訪問憑證,一般就是密碼
- 如果要自定義Realm,繼承AuthorizingRealm
- Realm:頂級接口,所有類的父接口
- CachingRelam:帶有緩存功能的Realm抽象類
- AuthenticatingRealm:帶有認證功能的Realm抽象類
- AuthorizingRealm:帶有授權功能的Realm抽象類
- SimpleAccountRealm:提供一些簡單的Realm認證
- TextConfigurationRealm:提供文本形式的Realm認證
- IniRealm和PropertiesRealm:TextConfigurationRealm的子類,細化文本驗證方式
- JdbcRealm:與數據庫交互的Realm認證
- DefaultLdapRealm:根據LDAP進行身份驗證
5.2.Shiro內置IniRealm權限驗證
(1)新建shiro.ini文本文件,編寫規則
#用戶模塊,對應用戶名、密碼、角色,多個角色之間用逗號隔開 [users] lixiang = 123456,user zhangsan = 123456,admin,root#權限模塊,對應角色名稱、對應權限,多個權限用,分隔 [roles] user = video:find,video:buy admin = video:* root = *(2)測試編碼
@Test public void test(){//創建IniSecurityManagerFactory工廠實例,注意這塊一定要是shiro下的包//IniSecurityManagerFactory這個類已經廢棄了,這里只做驗證Factory<SecurityManager> factory = new IniSecurityManagerFactory();//獲取工廠實例SecurityManager securityManager = factory.getInstance();//將securityManager設置到當前運行環境當中SecurityUtils.setSecurityManager(securityManager);//獲取Subject對象Subject subject = SecurityUtils.getSubject();//創建登錄TokenUsernameAndPasswordToken token = new UsernameAndPasswordToken("lixiang","123456");//驗證subject.login(token);//判斷是否有對應角色System.out.print("判斷是否有對應角色:"+subject.hasRole("admin"));//判斷是否有對應的權限System.out.print("判斷是否有對應權限:"+subject.isPermitted("video:find"));//判斷是否有對應的權限,無返回值,如果檢驗不通過則拋出異常//checkPermission("find:video")}5.3.Shiro內置JdbcRealm權限驗證
(1)配置jdbcrealm.ini文件,注意這塊一定要是ANSI格式否則運行會拋錯
#注意 文件格式必須為ini,編碼為ANSI#聲明Realm,指定realm類型 jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm#配置數據源 #dataSource=com.mchange.v2.c3p0.ComboPooledDataSourcedataSource=com.alibaba.druid.pool.DruidDataSource# mysql-connector-java 5 用的驅動url是com.mysql.jdbc.Driver,mysql-connector-java6以后用的是com.mysql.cj.jdbc.Driver dataSource.driverClassName=com.mysql.cj.jdbc.Driver#避免安全警告 dataSource.url=jdbc:mysql://120.76.62.13:3606/xdclass_shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=falsedataSource.username=testdataSource.password=Xdclasstest#指定數據源 jdbcRealm.dataSource=$dataSource#開啟查找權限, 默認是false,不會去查找角色對應的權限,坑!!!!! jdbcRealm.permissionsLookupEnabled=true#指定SecurityManager的Realms實現,設置realms,可以有多個,用逗號隔開 securityManager.realms=$jdbcRealm- 如果編碼不是ANSI格式
(2)驗證
配置文件中 jdbcRealm.permissionsLookupEnabled=true 一定要設置成true,默認是false不會去校驗角色
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sdIHQpbi-1667452035146)(images/5.2(3)].jpg)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TjSb8TpI-1667452035146)(images/5.2(4)].jpg)
@Testvoid contextLoads() {//創建SecurityManager工廠Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:jdbcrealm.ini");//拿到工廠SecurityManager securityManager = factory.getInstance();//將securityManager設置到當前運行環境當中SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("jack","123");subject.login(token);System.out.println("認證結果:"+subject.isAuthenticated());System.out.println("是否有對應的角色:"+subject.hasRole("user"));//查詢是否有權限,無返回值,沒有則拋異常//subject.checkPermission("video:delete");//查詢是否有權限,有返回值System.out.println(subject.isPermitted("video:delete"));}5.4.Shiro自定義Realm權限配置
(1)自定義Realm步驟
(1)創建一個類,繼承AuthorizingRealm->AuthenticatingRealm->CachingRealm->Realm (2)重寫授權方法:doGetAuthorizationInfo(進行權限校驗的時候會調用) (3)重寫認證方法:doGetAuthenticationInfo(當用戶登陸的時候會調用)(2)對象介紹
- UsernamePasswordToken : 對應就是 shiro的token中有Principal和Credential
- UsernameAndPasswordToken->HostAuthenticationToken->AuthenticationToken
- SimpleAuthorizationInfo:代表用戶角色權限信息
- SimpleAuthenticationInfo:代表該用戶的認證信息
(3)編寫自定義的Realm類
public class CustomRealm extends AuthorizingRealm {//userprivate final static Map<String,String> userMaps = new HashMap<>();{userMaps.put("lixiang","123");userMaps.put("lisi","123");}//roles - > permissionprivate final static Map<String,Set<String>> permissionMaps = new HashMap<>();{Set<String> set1 = new HashSet<>();Set<String> set2 = new HashSet<>();set1.add("video:find");set1.add("video:buy");set2.add("video:add");set2.add("video:delete");permissionMaps.put("lixiang",set1);permissionMaps.put("lisi",set2);}//user -> roleprivate final Map<String,Set<String>> roleMap = new HashMap<>();{Set<String> set1 = new HashSet<>();Set<String> set2 = new HashSet<>();set1.add("role1");set1.add("role2");set2.add("root");roleMap.put("jack",set1);roleMap.put("xdclass",set2);}/*** 進行權限驗證的時候調用* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("進行權限驗證doGetAuthorizationInfo");String username = principals.getPrimaryPrincipal().toString();Set<String> permissions = getPermissionsfromDB(username);Set<String> roles = getRolesfromDB(username);SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();simpleAuthorizationInfo.setRoles(roles);simpleAuthorizationInfo.setStringPermissions(permissions);return simpleAuthorizationInfo;}/*** 通過用戶名查找角色* @param username* @return*/private Set<String> getRolesfromDB(String username) {return roleMap.get(username);}/*** 通過用戶名查找權限* @param username* @return*/private Set<String> getPermissionsfromDB(String username) {return permissionMaps.get(username);}/*** 進行身份驗證的時候調用* @param token* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println(" 進行身份驗證doGetAuthenticationInfo");String username = token.getPrincipal().toString();String pwd = getPwdfromDB(username);if("".equals(pwd) || pwd == null){return null;}SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,pwd,this.getName());return simpleAuthenticationInfo;}private String getPwdfromDB(String username) {return userMaps.get(username).toString();} }(4)測試
SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("lixiang","123"); //登錄 subject.login(token); //唯一標識 System.out.println("用戶名:"+subject.getPrincipal()); System.out.println("是否有對應的角色:"+subject.hasRole("role1")); System.out.println("是否有對應的權限:"+subject.isPermitted("video:find"));5.5.Shiro源碼認證授權流程
認證流程:
- subject.login(token)
- DelegatingSubject.login(token)
- AuthenticatingSecurityManager.authenticate(token)
- AbstractAuthenticator.authenticate(token)
- ModulearRealmAuthenticator.doAuthenticate(token)
- ModulearRealmAuthenticator.doSingleRealmAuthentication(token)
- AuthenticatingRealm.getAuthenticationInfo(token)
鑒權流程:
- subject.checkRole(“admin”)
- DelegatingSubject.checkRole()
- AuthorizingSecurityManager.checkRole()
- ModulatRealmAuthorizer.checkRole()
- AuthorizingReaim,hasRole()
- AuthorizingRealm.doGetAuthorizationInfo()
6.Shiro權限認證Web案例
6.1.Shiro內置的過濾器
- 核心過濾器類:DefaultFilter,配置那個路徑對應那個攔截器進行處理
6.2.Shiro的Filter配置路徑
- 路徑支持通配符,完整匹配,注意匹配符不包括分隔符"/"
- 路徑通配符支持?、*、**,注意通配符匹配不包括目錄分隔符"/"
- * 可以匹配所有。不加 * 可以進行前綴匹配,但多個冒號就需要多個 * 來匹配
- 性能問題:通配符比字符串匹配會復雜點,所以性能也會稍弱,推薦使用字符匹配方式
6.3.Shiro數據安全之數據加解密
(1)為啥要加解密
- 明文數據容易泄露,比如密碼銘文存儲,萬一泄露則會造成嚴重的后果
(2)什么是散列算法
- 一般叫hash,簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數,適合存儲密碼,比如MD5
(3)什么是salt(鹽)
- 如果直接通過散列函數得到的加密數據,容易被對應解密網站暴力破解,一般會在應用層序里面加特殊的自動進行處理,比如用戶id等等,唯一標識的東西。
(4)Shiro里面CredentialsMatcher,用來驗證密碼是否正確
源碼:AuthenticatingRealm -> assertCredentialsMatch()(5)自定義驗證規則
一般會自定義驗證規則@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//散列算法,使用MD5算法;hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列的次數,比如散列兩次,相當于 md5(md5("xxx"));hashedCredentialsMatcher.setHashIterations(2);return hashedCredentialsMatcher;}6.4.Shiro權限控制注解
@RequiresRoles(value={"admin","editor"},logical=Logical.AND) 需要角色admin和editor兩個角色同時滿足@RequiresRoles(value={"admin","editor"},logical=Logical.OR) 需要角色admin或editor兩個角色其中一個滿足@RequiresAuthentication 已經授過權,調用Subject.isAuthenticated()返回true@RequiresUser 身份驗證或者通過記 住我登錄的查用API
subject.hasRole("xxx") subject.isPermitted("xxx") subject.isPermittedAll("xxx","xxx") subject.checkRole("xxx")6.5.Shiro緩存模塊講解
- AuthenticatingRealm 及 AuthorizingRealm 分別提供了對AuthenticationInfo 和 AuthorizationInfo 信息的緩存.
6.6.Shiro Session模塊講解
(1)什么是session會話
用戶和程序直接的鏈接,程序可以根據session識別到哪個用戶,和javaweb中的session類似
(2)什么是會話管理器SessionManager
- 會話管理器管理所有subject的所有操作,是shiro的核心組件
- 核心方法
(3)SessionDao會話存儲/持久化
-
SessionDAO
- AbstractSessionDAO
- CachingSessionDAO
- EnterpeiseCacheSessionDAO
- MemorySessionDAO
- CachingSessionDAO
- AbstractSessionDAO
-
核心方法
7.SpringBoot2.x整合Shiro
7.1.數據庫設計
- user表
- role表
- user_role表
- permission表
- role_permission表
7.2.Maven項目搭建
創建SpringBoot項目,引入依賴,配置數據庫
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--阿里巴巴druid數據源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version></dependency><!--spring整合shiro--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><repositories><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><snapshots><enabled>true</enabled></snapshots></repository><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url></repository></repositories><pluginRepositories><pluginRepository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><snapshots><enabled>true</enabled></snapshots></pluginRepository><pluginRepository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url></pluginRepository></pluginRepositories> #==============================數據庫相關配置======================================== spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.10.88:3306/rbac_shiro?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username =root spring.datasource.password =123456 #使用阿里巴巴druid數據源,默認使用自帶的 #spring.datasource.type =com.alibaba.druid.pool.DruidDataSource #開啟控制臺打印sql mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl# mybatis 下劃線轉駝峰配置,兩者都可以 #mybatis.configuration.mapUnderscoreToCamelCase=true mybatis.configuration.map-underscore-to-camel-case=true7.3.編寫查詢用戶全部信息接口
(1)實體類編寫
- User
- Role
- UserRole
- Permission
- RolePermission
(2)Mapper編寫
- UserMapper
- RoleMapper
- PermissionMapper
(3)UserService編寫
- UserService
- UserServiceImpl
(5)controller編寫
@RestController @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;@GetMapping("/find_user")public Object findUserInfo(@RequestParam("username") String username){return userService.findAllUserInfoByUsername(username);} }(6)測試
7.4.開發自定義CustomRealm
- 繼承 AuthorizingRealm
- 重寫 doGetAuthorizationInfo
- 重寫 doGetAuthenticationInfo
7.5.ShiroFilterFactoryBean配置
- shiroFilterFactoryBean-》
- SecurityManager-》
- CustomSessionManager
- CustomRealm-》hashedCredentialsMatcher
- SecurityManager-》
- SessionManager
- DefaultSessionManager: 默認實現,常用于javase
- ServletContainerSessionManager: web環境
- DefaultWebSessionManager:常用于自定義實現
7.6.自定義SessionManager驗證
public class CustomSessionManager extends DefaultWebSessionManager {private static final String TOKEN="token";//調用父類構造方法,以防后續有人修改構造,空構造覆蓋,會出問題public CustomSessionManager() {super();}@Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {String sessionId = WebUtils.toHttp(request).getHeader(TOKEN);//如果sessionId不為空,就調用自定義的邏輯,如果為空就調用父類的方法if(sessionId!=null){//調用shiro內部的校驗,檢測sessionId是否存在,是否過期request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return sessionId;}else{return super.getSessionId(request,response);}} }7.7.API攔截驗證案例
(1)AdminController
@RestController @RequestMapping("/admin") public class AdminController {@RequestMapping("/order")public JsonData findOrder(){Map<String,String> recordMap = new HashMap<>();recordMap.put("SpringBoot入門到高級實戰","300元");recordMap.put("Cloud微服務入門到高級實戰","877元");recordMap.put("分布式緩存Redis","990元");return JsonData.buildSuccess(recordMap);}}(2)LogoutController
@RestController public class LogoutController {@RequestMapping("/logout")public JsonData logout(){String username = (String)SecurityUtils.getSubject().getPrincipal();if(username!=null){SecurityUtils.getSubject().logout();return JsonData.buildSuccess("logout成功");}return JsonData.buildError("logout失敗");}}(3)OrderController
@RestController @RequestMapping("/authc") public class OrderController {@RequestMapping("/save")public JsonData findMyPlayRecord(){Map<String ,String> recordMap = new HashMap<>();recordMap.put("SpringBoot入門到高級實戰","第8章第1集");recordMap.put("Cloud微服務入門到高級實戰","第4章第10集");recordMap.put("分布式緩存Redis","第10章第3集");return JsonData.buildSuccess(recordMap);}}(4)pubController
@RestController @RequestMapping("/pub") public class PubController {@RequestMapping("/need_login")public JsonData needLogin(){return JsonData.buildSuccess("溫馨提示:請使用對應的賬號登錄",-2);}@RequestMapping("not_permit")public JsonData notPermit(){return JsonData.buildSuccess("溫馨提示:拒絕訪問,沒權限",-3);}@RequestMapping("/index")public JsonData index(){List<String> videoList = new ArrayList<>();videoList.add("Mysql零基礎入門到實戰 數據庫教程");videoList.add("Redis高并發高可用集群百萬級秒殺實戰");videoList.add("Zookeeper+Dubbo視頻教程 微服務教程分布式教程");videoList.add("2019年新版本RocketMQ4.X教程消息隊列教程");videoList.add("微服務SpringCloud+Docker入門到高級實戰");return JsonData.buildSuccess(videoList);}@PostMapping("/login")public JsonData login(@RequestBody UserQuery userQuery, HttpServletRequest request, HttpServletResponse response){Subject subject = SecurityUtils.getSubject();System.out.println("userQuery:"+userQuery);Map<String,Object> info = new HashMap<>();try {UsernamePasswordToken token = new UsernamePasswordToken(userQuery.getUsername(),userQuery.getPwd());subject.login(token);info.put("session_id",subject.getSession().getId());return JsonData.buildSuccess(info,"登錄成功");}catch (Exception e){info.put("session_id",subject.getSession().getId());return JsonData.buildError("賬號或密碼錯誤");}} }(5)VideoController
@RestController @RequestMapping("/video") public class VideoController {@RequestMapping("/update")public JsonData updateVideo(){return JsonData.buildSuccess("video 更新成功");}}(6)用戶角色權限分配圖
(7)測試
7.8.Shiro密碼加密處理
@SpringBootTest class RbacShiroApplicationTests {@Testvoid contextLoads() {//加密算法String hashName = "md5";//密碼明文String pwd = "123456";//加密函數,使用shiro自帶的SimpleHash hash = new SimpleHash(hashName, pwd, null, 2);System.out.println(hash);}}8.權限控制性能提升
8.1.自定義Shiro Filter過濾器
(1)shiro默認的roles過濾器存在的問題
(2)自定義過濾器類,繼承AuthorizationFilter
public class CustomRolesAuthorizationFilter extends AuthorizationFilter {@Overridepublic boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {Subject subject = getSubject(request, response);String[] rolesArray = (String[]) mappedValue;if (rolesArray == null || rolesArray.length == 0) {//no roles specified, so nothing to check - allow access.return true;}Set<String> roles = CollectionUtils.asSet(rolesArray);//filterChainDefinitionMap.put("/admin/**","roles[admin,root]")// shiro配置角色默認是與的關系,需要都滿足,這里改成或的關系,只要有其中一個即可for (String role : roles) {if (subject.hasRole(role)){return true;}}return false;} }(3)ShiroConfig中配置自定義過濾器
//設置自定義過濾器 Map<String, Filter> filterMap = new HashMap<>(); filterMap.put("customRolesFilter",new CustomRolesAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filterMap);8.2.Redis整合CacheManager
- Redis整合CacheManager為了提高性能,避免每次都去庫查
(1)加入shiro-redis依賴(shiro和redis整合的jar包)
<!--shiro整合redis--> <dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>3.1.0</version> </dependency>(2)ShiroConfig中配置,RedisManager,RedisCacheManager,SecruityManager
/*** 加入RedisManager*/public RedisManager getRedisManager(){RedisManager redisManager = new RedisManager();redisManager.setHost("192.168.10.88");redisManager.setPort(6379);return redisManager;}/*** 配置RedisCacheManager*/public RedisCacheManager cacheManager(){RedisCacheManager redisCacheManager = new RedisCacheManager();redisCacheManager.setRedisManager(getRedisManager());//設置過期時間,單位秒redisCacheManager.setExpire(60);return redisCacheManager;}(3)改造現有邏輯自定義的Realm
doGetAuthorizationInfo 方法 原有:String username = (String)principals.getPrimaryPrincipal();User user = userService.findAllUserInfoByUsername(username);改為User newUser = (User)principals.getPrimaryPrincipal();User user = userService.findAllUserInfoByUsername(newUser.getUsername());doGetAuthenticationInfo方法 原有: return new SimpleAuthenticationInfo(username, user.getPassword(), this.getClass().getName());改為 return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());8.3.Redis整合SessionManager
(1)加入SessionDAO的配置
/*** 配置SessionDAO*/public RedisSessionDAO sessionDAO(){RedisSessionDAO sessionDAO = new RedisSessionDAO();//設置RedisManagersessionDAO.setRedisManager(getRedisManager());return sessionDAO;}(2)自定義的sessionManager中設置sessionDAO
//設置Session持久化,RedisSessionManager //注意:如果不設置過期時間,redis中存儲也和shiro中session的默認過期時間保持一致 customSessionManager.setSessionDAO(sessionDAO());(3)注意傳輸的實體類都要實現Serializable接口,否則會報錯
8.4.ShiroConfig常用的Bean配置
(1)LifecycleBeanPostProcessor:管理shiro一些bean的生命周期,即bean初始化與銷毀
@Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor(); }(2)AuthorizationAttributeSourceAdvisor:加入注解的使用,不加入這個AOP注解不生效(@RequiresGuest)
@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());return authorizationAttributeSourceAdvisor; }(3)DefaultAdvisorAutoProxyCreator:用來掃描上下文尋找的所有Advistor(通知器),將符合條件的Advisor應用到切入點的Bean中,需要在LifecycleBeanPostProcessor創建后才可以創建
@Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setUsePrefix(true);return defaultAdvisorAutoProxyCreator; }9.分布式應用鑒權方式
9.1.自定義SessionId
- Shiro 默認的sessionid生成 類名 SessionIdGenerator
- 創建CustomSessionIdGenerator類,實現 SessionIdGenerator 接口的方法
總結
以上是生活随笔為你收集整理的一文看懂Shiro权限管理框架!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小程序如何引用阿里巴巴图标
- 下一篇: 录屏时计算机休眠,硬盘录像机里硬盘提示休