一文看懂Shiro权限管理框架!
文章目錄
- 1.JavaWeb中的權(quán)限控制
- 2.權(quán)限框架核心知識(shí)ACL和RBAC
- 2.1.ACL和RBAC簡(jiǎn)介
- 2.2主流權(quán)限框架介紹
- 3.Shiro架構(gòu)和基本概念
- 3.1.Shiro的4大核心模塊
- 3.2.Shiro權(quán)限控制運(yùn)行流程
- 4.Shiro簡(jiǎn)單API案例
- 4.1.項(xiàng)目搭建所需依賴
- 4.2.Shiro認(rèn)證簡(jiǎn)單實(shí)操
- 4.3.Shiro授權(quán)簡(jiǎn)單實(shí)操
- 5.安全數(shù)據(jù)來(lái)源Realm
- 5.1.Realm簡(jiǎn)介和繼承關(guān)系
- 5.2.Shiro內(nèi)置IniRealm權(quán)限驗(yàn)證
- 5.3.Shiro內(nèi)置JdbcRealm權(quán)限驗(yàn)證
- 5.4.Shiro自定義Realm權(quán)限配置
- 5.5.Shiro源碼認(rèn)證授權(quán)流程
- 6.Shiro權(quán)限認(rèn)證Web案例
- 6.1.Shiro內(nèi)置的過(guò)濾器
- 6.2.Shiro的Filter配置路徑
- 6.3.Shiro數(shù)據(jù)安全之?dāng)?shù)據(jù)加解密
- 6.4.Shiro權(quán)限控制注解
- 6.5.Shiro緩存模塊講解
- 6.6.Shiro Session模塊講解
- 7.SpringBoot2.x整合Shiro
- 7.1.數(shù)據(jù)庫(kù)設(shè)計(jì)
- 7.2.Maven項(xiàng)目搭建
- 7.3.編寫查詢用戶全部信息接口
- 7.4.開(kāi)發(fā)自定義CustomRealm
- 7.5.ShiroFilterFactoryBean配置
- 7.6.自定義SessionManager驗(yàn)證
- 7.7.API攔截驗(yàn)證案例
- 7.8.Shiro密碼加密處理
- 8.權(quán)限控制性能提升
- 8.1.自定義Shiro Filter過(guò)濾器
- 8.2.Redis整合CacheManager
- 8.3.Redis整合SessionManager
- 8.4.ShiroConfig常用的Bean配置
- 9.分布式應(yīng)用鑒權(quán)方式
- 9.1.自定義SessionId
1.JavaWeb中的權(quán)限控制
(1)什么是權(quán)限控制
- 忽略特別細(xì)的概念,比如權(quán)限能細(xì)分很多種,功能權(quán)限,數(shù)據(jù)權(quán)限,管理權(quán)限等
- 理解兩個(gè)概念:用戶和資源,讓指定的用戶,只能操作指定的資源(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.權(quán)限框架核心知識(shí)ACL和RBAC
2.1.ACL和RBAC簡(jiǎn)介
- ACL:Access Control List 訪問(wèn)控制列表
- 以前盛行的一種權(quán)限設(shè)計(jì),它的核心在于用戶直接和權(quán)限掛鉤
- 優(yōu)點(diǎn):簡(jiǎn)單易用、開(kāi)發(fā)便捷
- 缺點(diǎn):用戶和權(quán)限直接掛鉤,導(dǎo)致在授權(quán)時(shí)的復(fù)雜性,比較分散,不便于管理
- 案例:常見(jiàn)的文件系統(tǒng)權(quán)限設(shè)計(jì),直接給用戶加權(quán),類似Linux系統(tǒng)中的chmod
- RBAC:Role Based Access Control
- 基于角色的訪問(wèn)控制系統(tǒng)。權(quán)限與角色相關(guān)聯(lián),用戶通過(guò)適當(dāng)?shù)慕巧某蓡T而獲得角色的權(quán)限
- 優(yōu)點(diǎn):簡(jiǎn)化了用戶與權(quán)限的管理,通過(guò)對(duì)用戶進(jìn)行分類,使得角色與權(quán)限關(guān)聯(lián)起來(lái)
- 缺點(diǎn):開(kāi)發(fā)相比ACL復(fù)雜
- 案例:基于RBAC模型的權(quán)限驗(yàn)證框架有Apache Shiro、Spring Security
- 總結(jié):權(quán)限設(shè)計(jì)不能太過(guò)于復(fù)雜,否則性能會(huì)下降
2.2主流權(quán)限框架介紹
(1)什么是Spring Security
- 官網(wǎng):https://spring.io/projects/spring-security
(2)什么是Apache Shiro
- 官網(wǎng):https://github.com/apache/shiro
- 兩個(gè)的優(yōu)缺點(diǎn)
- Apache Shiro比Spring Security使用更簡(jiǎn)單
- Shiro功能強(qiáng)大、簡(jiǎn)潔、靈活,不跟任何的框架或者容器綁定,可以獨(dú)立運(yùn)行
- SpringSecurity對(duì)Spring體系支持比較友好,脫離Spring體系開(kāi)發(fā)很難
- SpringSecuity支持Oauth鑒權(quán),Shiro需要自己實(shí)現(xiàn)
3.Shiro架構(gòu)和基本概念
3.1.Shiro的4大核心模塊
(1)Shiro的四大核心模塊分為身份認(rèn)證、授權(quán)、會(huì)話管理和加密
- 身份認(rèn)證
- Authentication,身份認(rèn)證,一般就是登錄
- 授權(quán)
- Authorization,給用戶分配角色或者訪問(wèn)某些資源的權(quán)限
- 會(huì)話管理
- Session Management,用戶的會(huì)話管理員,多數(shù)情況下是web Session
- 加密
- Cryptogarphy,數(shù)據(jù)加解密,你如密碼加解密等
(2)Shiro架構(gòu)圖
3.2.Shiro權(quán)限控制運(yùn)行流程
(1)Shiro常見(jiàn)名稱
- Subject
- 我們把用戶或者程序稱為主體,主體去訪問(wèn)資源或者系統(tǒng)
- SecurityManager
- 安全管理器,Subject的認(rèn)證和授權(quán)都在安全管理器下進(jìn)行
- Authenticator
- 認(rèn)證器,主要負(fù)責(zé)Subject的認(rèn)證
- Realm
- 數(shù)據(jù)域,Shiro和安全數(shù)據(jù)的連接器,好比jdbc連接數(shù)據(jù)庫(kù);通過(guò)realm獲取認(rèn)證授權(quán)的相關(guān)信息
- Authorizer
- 授權(quán)器,主要負(fù)責(zé)Subject的授權(quán),控制subject擁有的角色或者權(quán)限
- Cryptography
- 加解密,Shiro包含易于使用和理解的數(shù)據(jù)加密方法,簡(jiǎn)化了很多復(fù)雜的API
- CacheManager
- 緩存管理器,比如認(rèn)證或者授權(quán)信息,通過(guò)緩存進(jìn)行管理,提高性能
- SessionManager
- 會(huì)話管理器,大多數(shù)是web session
- SessionDAO
- SessionDAO即會(huì)話,是對(duì)session會(huì)話的一套接口,比如要將session存儲(chǔ)到數(shù)據(jù)庫(kù)。
4.Shiro簡(jiǎn)單API案例
4.1.項(xiàng)目搭建所需依賴
- 環(huán)境準(zhǔn)備:maven3.5+jdk8+springboot+idea
4.2.Shiro認(rèn)證簡(jiǎn)單實(shí)操
(1)Shiro的認(rèn)證流程
- 創(chuàng)建Security Manager:Security Manager是用來(lái)提供安全服務(wù)的,所以在做shiro認(rèn)證的時(shí)候要先創(chuàng)建此對(duì)象
- 主題Subject提交請(qǐng)求給Security Manager
- Security Manager調(diào)用Authenticator組件做認(rèn)證
- Authenticator通過(guò)Realm來(lái)從數(shù)據(jù)源中獲取認(rèn)證數(shù)據(jù)
(2)編碼測(cè)試
@SpringBootTest public class Test{//聲明SecurityManagerDefaultSecurityManager securityManager = new DefaultSecurityManager();//聲明RealmSimpleAccountRealm accountRealm = new SimpleAccountRealm();@BeforeTestpublic void init(){accountRealm.addAccount("lixiang","123456");accountRealm.addAccount("lisi","123456");//構(gòu)建環(huán)境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("認(rèn)證結(jié)果:"+subject.isAuthenticated());} }(3)測(cè)試結(jié)果
4.3.Shiro授權(quán)簡(jiǎn)單實(shí)操
(1)常用API
//是否有對(duì)應(yīng)角色 subject.hasRole("root")//獲取subject名 subject.getPrincipal()//檢查是否有對(duì)應(yīng)的角色,無(wú)返回值,直接在SecurityManager里面進(jìn)行判斷 subject.checkRole("admin")//檢查是否有對(duì)應(yīng)的角色 subject.hasRole("admin")//退出登錄 subject.logout();(2)編碼實(shí)操
@Testvoid contextLoads() {SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("lixiang","123456");subject.login(usernamePasswordToken);System.out.println("認(rèn)證結(jié)果:"+subject.isAuthenticated());System.out.println("獲取subject主體的唯一標(biāo)識(shí):"+subject.getPrincipal());//檢查是否有對(duì)應(yīng)角色,無(wú)返回值,直接在SecurityManager里面進(jìn)行判斷subject.checkRole("admin");//檢查是否有對(duì)應(yīng)的角色System.out.println("是否有對(duì)應(yīng)角色:"+subject.hasRole("admin"));//退出登錄subject.logout();System.out.println("認(rèn)證結(jié)果:"+subject.isAuthenticated());}5.安全數(shù)據(jù)來(lái)源Realm
5.1.Realm簡(jiǎn)介和繼承關(guān)系
- Realm的作用:Shiro從Realm中獲取安全的數(shù)據(jù)
- Realm中的兩個(gè)概念:
- principal:主體的標(biāo)識(shí),可以有多個(gè),但是必須要有一個(gè)唯一性的,常見(jiàn)的用戶名、手機(jī)號(hào)、郵箱
- credential:訪問(wèn)憑證,一般就是密碼
- 如果要自定義Realm,繼承AuthorizingRealm
- Realm:頂級(jí)接口,所有類的父接口
- CachingRelam:帶有緩存功能的Realm抽象類
- AuthenticatingRealm:帶有認(rèn)證功能的Realm抽象類
- AuthorizingRealm:帶有授權(quán)功能的Realm抽象類
- SimpleAccountRealm:提供一些簡(jiǎn)單的Realm認(rèn)證
- TextConfigurationRealm:提供文本形式的Realm認(rèn)證
- IniRealm和PropertiesRealm:TextConfigurationRealm的子類,細(xì)化文本驗(yàn)證方式
- JdbcRealm:與數(shù)據(jù)庫(kù)交互的Realm認(rèn)證
- DefaultLdapRealm:根據(jù)LDAP進(jìn)行身份驗(yàn)證
5.2.Shiro內(nèi)置IniRealm權(quán)限驗(yàn)證
(1)新建shiro.ini文本文件,編寫規(guī)則
#用戶模塊,對(duì)應(yīng)用戶名、密碼、角色,多個(gè)角色之間用逗號(hào)隔開(kāi) [users] lixiang = 123456,user zhangsan = 123456,admin,root#權(quán)限模塊,對(duì)應(yīng)角色名稱、對(duì)應(yīng)權(quán)限,多個(gè)權(quán)限用,分隔 [roles] user = video:find,video:buy admin = video:* root = *(2)測(cè)試編碼
@Test public void test(){//創(chuàng)建IniSecurityManagerFactory工廠實(shí)例,注意這塊一定要是shiro下的包//IniSecurityManagerFactory這個(gè)類已經(jīng)廢棄了,這里只做驗(yàn)證Factory<SecurityManager> factory = new IniSecurityManagerFactory();//獲取工廠實(shí)例SecurityManager securityManager = factory.getInstance();//將securityManager設(shè)置到當(dāng)前運(yùn)行環(huán)境當(dāng)中SecurityUtils.setSecurityManager(securityManager);//獲取Subject對(duì)象Subject subject = SecurityUtils.getSubject();//創(chuàng)建登錄TokenUsernameAndPasswordToken token = new UsernameAndPasswordToken("lixiang","123456");//驗(yàn)證subject.login(token);//判斷是否有對(duì)應(yīng)角色System.out.print("判斷是否有對(duì)應(yīng)角色:"+subject.hasRole("admin"));//判斷是否有對(duì)應(yīng)的權(quán)限System.out.print("判斷是否有對(duì)應(yīng)權(quán)限:"+subject.isPermitted("video:find"));//判斷是否有對(duì)應(yīng)的權(quán)限,無(wú)返回值,如果檢驗(yàn)不通過(guò)則拋出異常//checkPermission("find:video")}5.3.Shiro內(nèi)置JdbcRealm權(quán)限驗(yàn)證
(1)配置jdbcrealm.ini文件,注意這塊一定要是ANSI格式否則運(yùn)行會(huì)拋錯(cuò)
#注意 文件格式必須為ini,編碼為ANSI#聲明Realm,指定realm類型 jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm#配置數(shù)據(jù)源 #dataSource=com.mchange.v2.c3p0.ComboPooledDataSourcedataSource=com.alibaba.druid.pool.DruidDataSource# mysql-connector-java 5 用的驅(qū)動(dòng)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#指定數(shù)據(jù)源 jdbcRealm.dataSource=$dataSource#開(kāi)啟查找權(quán)限, 默認(rèn)是false,不會(huì)去查找角色對(duì)應(yīng)的權(quán)限,坑!!!!! jdbcRealm.permissionsLookupEnabled=true#指定SecurityManager的Realms實(shí)現(xiàn),設(shè)置realms,可以有多個(gè),用逗號(hào)隔開(kāi) securityManager.realms=$jdbcRealm- 如果編碼不是ANSI格式
(2)驗(yàn)證
配置文件中 jdbcRealm.permissionsLookupEnabled=true 一定要設(shè)置成true,默認(rèn)是false不會(huì)去校驗(yàn)角色
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-sdIHQpbi-1667452035146)(images/5.2(3)].jpg)
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-TjSb8TpI-1667452035146)(images/5.2(4)].jpg)
@Testvoid contextLoads() {//創(chuàng)建SecurityManager工廠Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:jdbcrealm.ini");//拿到工廠SecurityManager securityManager = factory.getInstance();//將securityManager設(shè)置到當(dāng)前運(yùn)行環(huán)境當(dāng)中SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken("jack","123");subject.login(token);System.out.println("認(rèn)證結(jié)果:"+subject.isAuthenticated());System.out.println("是否有對(duì)應(yīng)的角色:"+subject.hasRole("user"));//查詢是否有權(quán)限,無(wú)返回值,沒(méi)有則拋異常//subject.checkPermission("video:delete");//查詢是否有權(quán)限,有返回值System.out.println(subject.isPermitted("video:delete"));}5.4.Shiro自定義Realm權(quán)限配置
(1)自定義Realm步驟
(1)創(chuàng)建一個(gè)類,繼承AuthorizingRealm->AuthenticatingRealm->CachingRealm->Realm (2)重寫授權(quán)方法:doGetAuthorizationInfo(進(jìn)行權(quán)限校驗(yàn)的時(shí)候會(huì)調(diào)用) (3)重寫認(rèn)證方法:doGetAuthenticationInfo(當(dāng)用戶登陸的時(shí)候會(huì)調(diào)用)(2)對(duì)象介紹
- UsernamePasswordToken : 對(duì)應(yīng)就是 shiro的token中有Principal和Credential
- UsernameAndPasswordToken->HostAuthenticationToken->AuthenticationToken
- SimpleAuthorizationInfo:代表用戶角色權(quán)限信息
- SimpleAuthenticationInfo:代表該用戶的認(rèn)證信息
(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);}/*** 進(jìn)行權(quán)限驗(yàn)證的時(shí)候調(diào)用* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("進(jìn)行權(quán)限驗(yàn)證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;}/*** 通過(guò)用戶名查找角色* @param username* @return*/private Set<String> getRolesfromDB(String username) {return roleMap.get(username);}/*** 通過(guò)用戶名查找權(quán)限* @param username* @return*/private Set<String> getPermissionsfromDB(String username) {return permissionMaps.get(username);}/*** 進(jìn)行身份驗(yàn)證的時(shí)候調(diào)用* @param token* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println(" 進(jìn)行身份驗(yàn)證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)測(cè)試
SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("lixiang","123"); //登錄 subject.login(token); //唯一標(biāo)識(shí) System.out.println("用戶名:"+subject.getPrincipal()); System.out.println("是否有對(duì)應(yīng)的角色:"+subject.hasRole("role1")); System.out.println("是否有對(duì)應(yīng)的權(quán)限:"+subject.isPermitted("video:find"));5.5.Shiro源碼認(rèn)證授權(quán)流程
認(rèn)證流程:
- subject.login(token)
- DelegatingSubject.login(token)
- AuthenticatingSecurityManager.authenticate(token)
- AbstractAuthenticator.authenticate(token)
- ModulearRealmAuthenticator.doAuthenticate(token)
- ModulearRealmAuthenticator.doSingleRealmAuthentication(token)
- AuthenticatingRealm.getAuthenticationInfo(token)
鑒權(quán)流程:
- subject.checkRole(“admin”)
- DelegatingSubject.checkRole()
- AuthorizingSecurityManager.checkRole()
- ModulatRealmAuthorizer.checkRole()
- AuthorizingReaim,hasRole()
- AuthorizingRealm.doGetAuthorizationInfo()
6.Shiro權(quán)限認(rèn)證Web案例
6.1.Shiro內(nèi)置的過(guò)濾器
- 核心過(guò)濾器類:DefaultFilter,配置那個(gè)路徑對(duì)應(yīng)那個(gè)攔截器進(jìn)行處理
6.2.Shiro的Filter配置路徑
- 路徑支持通配符,完整匹配,注意匹配符不包括分隔符"/"
- 路徑通配符支持?、*、**,注意通配符匹配不包括目錄分隔符"/"
- * 可以匹配所有。不加 * 可以進(jìn)行前綴匹配,但多個(gè)冒號(hào)就需要多個(gè) * 來(lái)匹配
- 性能問(wèn)題:通配符比字符串匹配會(huì)復(fù)雜點(diǎn),所以性能也會(huì)稍弱,推薦使用字符匹配方式
6.3.Shiro數(shù)據(jù)安全之?dāng)?shù)據(jù)加解密
(1)為啥要加解密
- 明文數(shù)據(jù)容易泄露,比如密碼銘文存儲(chǔ),萬(wàn)一泄露則會(huì)造成嚴(yán)重的后果
(2)什么是散列算法
- 一般叫hash,簡(jiǎn)單的說(shuō)就是一種將任意長(zhǎng)度的消息壓縮到某一固定長(zhǎng)度的消息摘要的函數(shù),適合存儲(chǔ)密碼,比如MD5
(3)什么是salt(鹽)
- 如果直接通過(guò)散列函數(shù)得到的加密數(shù)據(jù),容易被對(duì)應(yīng)解密網(wǎng)站暴力破解,一般會(huì)在應(yīng)用層序里面加特殊的自動(dòng)進(jìn)行處理,比如用戶id等等,唯一標(biāo)識(shí)的東西。
(4)Shiro里面CredentialsMatcher,用來(lái)驗(yàn)證密碼是否正確
源碼:AuthenticatingRealm -> assertCredentialsMatch()(5)自定義驗(yàn)證規(guī)則
一般會(huì)自定義驗(yàn)證規(guī)則@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//散列算法,使用MD5算法;hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列的次數(shù),比如散列兩次,相當(dāng)于 md5(md5("xxx"));hashedCredentialsMatcher.setHashIterations(2);return hashedCredentialsMatcher;}6.4.Shiro權(quán)限控制注解
@RequiresRoles(value={"admin","editor"},logical=Logical.AND) 需要角色admin和editor兩個(gè)角色同時(shí)滿足@RequiresRoles(value={"admin","editor"},logical=Logical.OR) 需要角色admin或editor兩個(gè)角色其中一個(gè)滿足@RequiresAuthentication 已經(jīng)授過(guò)權(quán),調(diào)用Subject.isAuthenticated()返回true@RequiresUser 身份驗(yàn)證或者通過(guò)記 住我登錄的查用API
subject.hasRole("xxx") subject.isPermitted("xxx") subject.isPermittedAll("xxx","xxx") subject.checkRole("xxx")6.5.Shiro緩存模塊講解
- AuthenticatingRealm 及 AuthorizingRealm 分別提供了對(duì)AuthenticationInfo 和 AuthorizationInfo 信息的緩存.
6.6.Shiro Session模塊講解
(1)什么是session會(huì)話
用戶和程序直接的鏈接,程序可以根據(jù)session識(shí)別到哪個(gè)用戶,和javaweb中的session類似
(2)什么是會(huì)話管理器SessionManager
- 會(huì)話管理器管理所有subject的所有操作,是shiro的核心組件
- 核心方法
(3)SessionDao會(huì)話存儲(chǔ)/持久化
-
SessionDAO
- AbstractSessionDAO
- CachingSessionDAO
- EnterpeiseCacheSessionDAO
- MemorySessionDAO
- CachingSessionDAO
- AbstractSessionDAO
-
核心方法
7.SpringBoot2.x整合Shiro
7.1.數(shù)據(jù)庫(kù)設(shè)計(jì)
- user表
- role表
- user_role表
- permission表
- role_permission表
7.2.Maven項(xiàng)目搭建
創(chuàng)建SpringBoot項(xiàng)目,引入依賴,配置數(shù)據(jù)庫(kù)
<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數(shù)據(jù)源--><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> #==============================數(shù)據(jù)庫(kù)相關(guān)配置======================================== 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數(shù)據(jù)源,默認(rèn)使用自帶的 #spring.datasource.type =com.alibaba.druid.pool.DruidDataSource #開(kāi)啟控制臺(tái)打印sql mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl# mybatis 下劃線轉(zhuǎn)駝峰配置,兩者都可以 #mybatis.configuration.mapUnderscoreToCamelCase=true mybatis.configuration.map-underscore-to-camel-case=true7.3.編寫查詢用戶全部信息接口
(1)實(shí)體類編寫
- 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)測(cè)試
7.4.開(kāi)發(fā)自定義CustomRealm
- 繼承 AuthorizingRealm
- 重寫 doGetAuthorizationInfo
- 重寫 doGetAuthenticationInfo
7.5.ShiroFilterFactoryBean配置
- shiroFilterFactoryBean-》
- SecurityManager-》
- CustomSessionManager
- CustomRealm-》hashedCredentialsMatcher
- SecurityManager-》
- SessionManager
- DefaultSessionManager: 默認(rèn)實(shí)現(xiàn),常用于javase
- ServletContainerSessionManager: web環(huán)境
- DefaultWebSessionManager:常用于自定義實(shí)現(xiàn)
7.6.自定義SessionManager驗(yàn)證
public class CustomSessionManager extends DefaultWebSessionManager {private static final String TOKEN="token";//調(diào)用父類構(gòu)造方法,以防后續(xù)有人修改構(gòu)造,空構(gòu)造覆蓋,會(huì)出問(wèn)題public CustomSessionManager() {super();}@Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {String sessionId = WebUtils.toHttp(request).getHeader(TOKEN);//如果sessionId不為空,就調(diào)用自定義的邏輯,如果為空就調(diào)用父類的方法if(sessionId!=null){//調(diào)用shiro內(nèi)部的校驗(yàn),檢測(cè)sessionId是否存在,是否過(guò)期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攔截驗(yàn)證案例
(1)AdminController
@RestController @RequestMapping("/admin") public class AdminController {@RequestMapping("/order")public JsonData findOrder(){Map<String,String> recordMap = new HashMap<>();recordMap.put("SpringBoot入門到高級(jí)實(shí)戰(zhàn)","300元");recordMap.put("Cloud微服務(wù)入門到高級(jí)實(shí)戰(zhàn)","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入門到高級(jí)實(shí)戰(zhàn)","第8章第1集");recordMap.put("Cloud微服務(wù)入門到高級(jí)實(shí)戰(zhàn)","第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("溫馨提示:請(qǐng)使用對(duì)應(yīng)的賬號(hào)登錄",-2);}@RequestMapping("not_permit")public JsonData notPermit(){return JsonData.buildSuccess("溫馨提示:拒絕訪問(wèn),沒(méi)權(quán)限",-3);}@RequestMapping("/index")public JsonData index(){List<String> videoList = new ArrayList<>();videoList.add("Mysql零基礎(chǔ)入門到實(shí)戰(zhàn) 數(shù)據(jù)庫(kù)教程");videoList.add("Redis高并發(fā)高可用集群百萬(wàn)級(jí)秒殺實(shí)戰(zhàn)");videoList.add("Zookeeper+Dubbo視頻教程 微服務(wù)教程分布式教程");videoList.add("2019年新版本RocketMQ4.X教程消息隊(duì)列教程");videoList.add("微服務(wù)SpringCloud+Docker入門到高級(jí)實(shí)戰(zhàn)");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("賬號(hào)或密碼錯(cuò)誤");}} }(5)VideoController
@RestController @RequestMapping("/video") public class VideoController {@RequestMapping("/update")public JsonData updateVideo(){return JsonData.buildSuccess("video 更新成功");}}(6)用戶角色權(quán)限分配圖
(7)測(cè)試
7.8.Shiro密碼加密處理
@SpringBootTest class RbacShiroApplicationTests {@Testvoid contextLoads() {//加密算法String hashName = "md5";//密碼明文String pwd = "123456";//加密函數(shù),使用shiro自帶的SimpleHash hash = new SimpleHash(hashName, pwd, null, 2);System.out.println(hash);}}8.權(quán)限控制性能提升
8.1.自定義Shiro Filter過(guò)濾器
(1)shiro默認(rèn)的roles過(guò)濾器存在的問(wèn)題
(2)自定義過(guò)濾器類,繼承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配置角色默認(rèn)是與的關(guān)系,需要都滿足,這里改成或的關(guān)系,只要有其中一個(gè)即可for (String role : roles) {if (subject.hasRole(role)){return true;}}return false;} }(3)ShiroConfig中配置自定義過(guò)濾器
//設(shè)置自定義過(guò)濾器 Map<String, Filter> filterMap = new HashMap<>(); filterMap.put("customRolesFilter",new CustomRolesAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filterMap);8.2.Redis整合CacheManager
- Redis整合CacheManager為了提高性能,避免每次都去庫(kù)查
(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());//設(shè)置過(guò)期時(shí)間,單位秒redisCacheManager.setExpire(60);return redisCacheManager;}(3)改造現(xiàn)有邏輯自定義的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();//設(shè)置RedisManagersessionDAO.setRedisManager(getRedisManager());return sessionDAO;}(2)自定義的sessionManager中設(shè)置sessionDAO
//設(shè)置Session持久化,RedisSessionManager //注意:如果不設(shè)置過(guò)期時(shí)間,redis中存儲(chǔ)也和shiro中session的默認(rèn)過(guò)期時(shí)間保持一致 customSessionManager.setSessionDAO(sessionDAO());(3)注意傳輸?shù)膶?shí)體類都要實(shí)現(xiàn)Serializable接口,否則會(huì)報(bào)錯(cuò)
8.4.ShiroConfig常用的Bean配置
(1)LifecycleBeanPostProcessor:管理shiro一些bean的生命周期,即bean初始化與銷毀
@Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor(); }(2)AuthorizationAttributeSourceAdvisor:加入注解的使用,不加入這個(gè)AOP注解不生效(@RequiresGuest)
@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());return authorizationAttributeSourceAdvisor; }(3)DefaultAdvisorAutoProxyCreator:用來(lái)掃描上下文尋找的所有Advistor(通知器),將符合條件的Advisor應(yīng)用到切入點(diǎn)的Bean中,需要在LifecycleBeanPostProcessor創(chuàng)建后才可以創(chuàng)建
@Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setUsePrefix(true);return defaultAdvisorAutoProxyCreator; }9.分布式應(yīng)用鑒權(quán)方式
9.1.自定義SessionId
- Shiro 默認(rèn)的sessionid生成 類名 SessionIdGenerator
- 創(chuàng)建CustomSessionIdGenerator類,實(shí)現(xiàn) SessionIdGenerator 接口的方法
總結(jié)
以上是生活随笔為你收集整理的一文看懂Shiro权限管理框架!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 小程序如何引用阿里巴巴图标
- 下一篇: 录屏时计算机休眠,硬盘录像机里硬盘提示休