日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

一文看懂Shiro权限管理框架!

發(fā)布時(shí)間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一文看懂Shiro权限管理框架! 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

      • 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
Spring Security是一個(gè)能夠基于Spring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI和AOP功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制功能,減少了企業(yè)系統(tǒng)安全控制編寫大量重復(fù)代碼的工作。

(2)什么是Apache Shiro

  • 官網(wǎng):https://github.com/apache/shiro
Apache Shiro是一個(gè)強(qiáng)大且易用的java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼和會(huì)話管理。使用shiro的易于理解的API,可以快速、輕松的執(zhí)行任何應(yīng)用程序。
  • 兩個(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
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <!--mysql starter 注意一定要把runtime去掉--> <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId> </dependency> <!--測(cè)試模塊starter--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope> </dependency> <!--阿里巴巴數(shù)據(jù)源--> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version> </dependency> <!--shiro相關(guān)依賴包--> <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version> </dependency>

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)行處理
authc:org.apache.shiro.web.filter.authc.FromAuthenticationFilter - 需要認(rèn)證登錄才能訪問(wèn) user:org.apache.shiro.web.filter.authc.UserFilter - 用戶攔截器,表示必須存在用戶 anon:org.apache.shiro.web.filter.authc.AnonymousFilter - 匿名攔截器,不需要登錄即可訪問(wèn)的資源,匿名用戶或者游客,一般用于過(guò)濾靜態(tài)資源 roles:org.apache.shiro.web.filter.authz.RolesAuthorizationFilter - 角色授權(quán)攔截器,驗(yàn)證用戶是否擁有角色。 - 參數(shù)可以寫多個(gè),多個(gè)參數(shù)時(shí)寫roles["admin","user"],當(dāng)多個(gè)參數(shù)時(shí)必須每個(gè)參數(shù)都通過(guò)才算通過(guò)。 perms:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter - 權(quán)限授權(quán)攔截器,驗(yàn)證用戶是否擁有權(quán)限 - 參數(shù)可寫多個(gè),和角色多個(gè)是一致的 authcBasic:org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter - httpBasic身份驗(yàn)證攔截器 logout:org.apache.shiro.web.filter.authc.LogoutFilter - 退出攔截器,執(zhí)行后會(huì)執(zhí)行跳轉(zhuǎn)到shiroFilterFactoryBean.setLoginUrl()設(shè)置的url port:org.apache.shiro.web.filter.authz.PortFilter - 端口攔截器,可通過(guò)的端口 ssl:org.apache.shiro.web.filter.authz.SslFilter - ssl攔截器,只有請(qǐng)求協(xié)議是https才能通過(guò)

6.2.Shiro的Filter配置路徑

  • 路徑支持通配符,完整匹配,注意匹配符不包括分隔符"/"
  • 路徑通配符支持?、*、**,注意通配符匹配不包括目錄分隔符"/"
  • * 可以匹配所有。不加 * 可以進(jìn)行前綴匹配,但多個(gè)冒號(hào)就需要多個(gè) * 來(lái)匹配
URL權(quán)限采取第一次匹配優(yōu)先的方式,優(yōu)先匹配靠前的規(guī)則 ?:匹配一個(gè)字符,如/user? 匹配/user3,但不匹配/user/ *:匹配0個(gè)或多個(gè)字符串,如/add*,匹配/addtest,但不匹配/add/1 ** : 匹配路徑中的零個(gè)或多個(gè)路徑,如 /user/** 將匹 配 /user/xxx 或 /user/xxx/yyy例子 /user/**=filter1 /user/add=filter2請(qǐng)求 /user/add 命中的是filter1攔截器
  • 性能問(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的核心組件
  • 核心方法
//開(kāi)啟一個(gè)session Session start(SessionContext context) //指定key獲取session Session getSession(SessionKey key)

(3)SessionDao會(huì)話存儲(chǔ)/持久化

  • SessionDAO

    • AbstractSessionDAO
      • CachingSessionDAO
        • EnterpeiseCacheSessionDAO
      • MemorySessionDAO
  • 核心方法

//創(chuàng)建 Serializable create(Session session) //獲取 Session readSession(Serializable sessionId) throws UnknownSessionException //更新 void update(Session session) //刪除,會(huì)話過(guò)期時(shí)調(diào)用 void delete(Session session) //獲取活躍的session Collection<Session> getActiveSessions() RememberMe 1.Cookie寫到客戶端并保存 2.通過(guò)調(diào)用subject.login()前,設(shè)置 token.setRememberMe(true) - subject.isAuthenticated() 表示用戶進(jìn)行了身份驗(yàn)證登錄的,即Subject.login 進(jìn)行了登錄 - subject.isRemembered() 表示用戶是通過(guò)RememberMe登錄的 - subject.isAuthenticated()==true,則 subject.isRemembered()==false, 兩個(gè)互斥 - 總結(jié):特殊頁(yè)面或者API調(diào)用才需要authc進(jìn)行驗(yàn)證攔截,該攔截器會(huì)判斷用戶是否是通過(guò)

7.SpringBoot2.x整合Shiro

7.1.數(shù)據(jù)庫(kù)設(shè)計(jì)

  • user表
CREATE TABLE `user` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`username` varchar(128) DEFAULT NULL COMMENT '用戶名',`password` varchar(256) DEFAULT NULL COMMENT '密碼',`create_time` datetime DEFAULT NULL,`salt` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
  • role表
CREATE TABLE `role` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(128) DEFAULT NULL COMMENT '名稱',`description` varchar(64) DEFAULT NULL COMMENT '描述',PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
  • user_role表
CREATE TABLE `user_role` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`role_id` int(11) DEFAULT NULL,`user_id` int(11) DEFAULT NULL,`remarks` varchar(64) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
  • permission表
CREATE TABLE `permission` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(128) DEFAULT NULL COMMENT '名稱',`url` varchar(128) DEFAULT NULL COMMENT '接口路徑',PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
  • role_permission表
CREATE TABLE `role_permission` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`role_id` int(11) DEFAULT NULL,`permission_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

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=true

7.3.編寫查詢用戶全部信息接口

(1)實(shí)體類編寫

  • User
/*** 用戶表*/ public class User {private int id;private String username;private Date createTime;private String salt;private List<Role> roleList; }
  • Role
/*** 角色表*/ public class Role {private int id;private String name;private String description;private List<Permission> permissionList; }
  • UserRole
/*** 用戶角色中間表*/ public class UserRole {private int id;private int userId;private int roleId; }
  • Permission
/*** 權(quán)限表*/ public class Permission {private int id;private String name;private String url; }
  • RolePermission
/*** 權(quán)限角色中間表*/ public class RolePermission {private int id;private int roleId;private int permissionId; }

(2)Mapper編寫

  • UserMapper
public interface UserMapper {@Select("select * from user where username = #{username}")User findByUsername(@Param("username") String username);@Select("select * from user where id = #{id}")User findById(@Param("id") int id);@Select("select * from user where username = #{username} and password = #{pwd}")User findByUsernameAndPwd(@Param("username") String username,@Param("pwd") String pwd); }
  • RoleMapper
public interface RoleMapper {@Select("select * from user_role where user_id = #{userId}")List<UserRole> findRolesByUserId(@Param("userId") int userId);@Select("select * from role where id = #{roleId}")List<Role> findRolesByRoleId(@Param("roleId") int roleId);}
  • PermissionMapper
public interface PermissionMapper {@Select("select * from permission where id = #{roleId}")List<Permission> findPermissionsByRoleId(@Param("roleId") int roleId); }

(3)UserService編寫

  • UserService
public interface UserService {/*** 獲取用戶全部信息,包括角色權(quán)限* @param username* @return*/User findAllUserInfoByUsername(String username);/*** 獲取用戶基本信息* @param userId* @return*/User findSimpleUserInfoById(int userId);/*** 獲取用戶基本信息* @param username* @return*/User findSimpleUserInfoByUsername(String username); }
  • UserServiceImpl
@Service public class UserServiceImpl implements UserService {@Autowiredprivate RoleMapper roleMapper;@Autowiredprivate UserMapper userMapper;@Autowiredprivate PermissionMapper permissionMapper;@Overridepublic User findAllUserInfoByUsername(String username) {User user = userMapper.findByUsername(username);List<UserRole> userRoles = roleMapper.findRolesByUserId(user.getId());List<Role> roles = new ArrayList<>();for (UserRole role : userRoles) {roles = roleMapper.findRolesByRoleId(role.getRoleId());for (Role x : roles) {x.setPermissionList(permissionMapper.findPermissionsByRoleId(x.getId()));}}user.setRoleList(roles);return user;}@Overridepublic User findSimpleUserInfoById(int userId) {return userMapper.findById(userId);}@Overridepublic User findSimpleUserInfoByUsername(String username) {return userMapper.findByUsername(username);} }

(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
public class CustomRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;/*** 用戶鑒權(quán)的時(shí)候會(huì)調(diào)用* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("鑒權(quán) doGetAuthorizationInfo");String username = (String)principals.getPrimaryPrincipal();User userInfo = userService.findAllUserInfoByUsername(username);//將角色,權(quán)限放到對(duì)應(yīng)的兩個(gè)String類型集合中List<String> stringRoleList = new ArrayList<>();List<String> stringPermissionList = new ArrayList<>();List<Role> roleList = userInfo.getRoleList();for (Role role : roleList) {stringRoleList.add(role.getName());List<Permission> permissionList = role.getPermissionList();for (Permission permission : permissionList) {if(permission!=null){stringPermissionList.add(permission.getName());}}}//拿到對(duì)應(yīng)的角色集合,權(quán)限集合,封裝SimpleAuthorizationInfo對(duì)象添加SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();simpleAuthorizationInfo.addRoles(stringRoleList);simpleAuthorizationInfo.addStringPermissions(stringPermissionList);return simpleAuthorizationInfo;}/*** 用戶認(rèn)證登錄的時(shí)候會(huì)調(diào)用* @param token* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("認(rèn)證 doGetAuthenticationInfo");//從token中拿到usernameString username = (String)token.getPrincipal();User userInfo = userService.findAllUserInfoByUsername(username);//獲取密碼String password = userInfo.getPassword();if(password == null || "".equals(password)){return null;}//密碼不為空,說(shuō)明認(rèn)證成功,封裝SimpleAuthenticationInfo對(duì)象,參數(shù):用戶名,密碼,class.getNamereturn new SimpleAuthenticationInfo(username,userInfo.getUsername(),this.getClass().getName());} }

7.5.ShiroFilterFactoryBean配置

  • shiroFilterFactoryBean-》
    • SecurityManager-》
      • CustomSessionManager
      • CustomRealm-》hashedCredentialsMatcher
  • SessionManager
    • DefaultSessionManager: 默認(rèn)實(shí)現(xiàn),常用于javase
    • ServletContainerSessionManager: web環(huán)境
    • DefaultWebSessionManager:常用于自定義實(shí)現(xiàn)
@Configuration public class ShiroConfig {/*** 設(shè)置shiroFilter* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){System.out.println("執(zhí)行 ShiroFilterFactoryBean.shiroFilter()");ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//設(shè)置securityManagershiroFilterFactoryBean.setSecurityManager(securityManager);//設(shè)置登錄成功后訪問(wèn)的路徑shiroFilterFactoryBean.setLoginUrl("/pub/need_login");//設(shè)置登錄成功后訪問(wèn)的urlshiroFilterFactoryBean.setSuccessUrl("/");//設(shè)置無(wú)權(quán)限訪問(wèn)的接口,未授權(quán)無(wú)法訪問(wèn)接口shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit");//攔截器路徑配置,注意這里一定要用LinkedHashMap,HashMap會(huì)出現(xiàn)偶爾無(wú)法攔截的情況Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();//設(shè)置過(guò)濾器//登出過(guò)濾器,用戶退出時(shí)調(diào)用filterChainDefinitionMap.put("/logout","logout");//匿名過(guò)濾器,游客模式filterChainDefinitionMap.put("/pub/**","anon");//登錄過(guò)濾器,用戶只有登錄才能訪問(wèn)filterChainDefinitionMap.put("/authc/**","authc");//管理員角色才能訪問(wèn)filterChainDefinitionMap.put("/admin/**","roles[admin]");//指定權(quán)限才能訪問(wèn)filterChainDefinitionMap.put("/video/update","perms[video_update]");filterChainDefinitionMap.put("/**","authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}/*** 注入securityManager,設(shè)置Realm和SessionManager* @return*/@Beanpublic SecurityManager securityManager(){DefaultSecurityManager securityManager = new DefaultSecurityManager();securityManager.setRealm(customRealm());//注意如果不是前后端分離的項(xiàng)目就不需要設(shè)置sessionManagersecurityManager.setSessionManager(customSessionManager());return securityManager;}/*** 設(shè)置散列算法* @return*/@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//設(shè)設(shè)置散列算法,md5hashedCredentialsMatcher.setHashAlgorithmName("md5");//設(shè)置散列次數(shù)hashedCredentialsMatcher.setHashIterations(2);return hashedCredentialsMatcher;}/*** 注入自定義的Realm* @return*/@Beanpublic CustomRealm customRealm(){CustomRealm customRealm = new CustomRealm();customRealm.setCredentialsMatcher(hashedCredentialsMatcher());return customRealm;}/*** 注入自定義的SessionManager* @return*/@Beanpublic CustomSessionManager customSessionManager(){CustomSessionManager customSessionManager = new CustomSessionManager();//超時(shí)時(shí)間,默認(rèn)30分鐘不操作就會(huì)過(guò)期,單位豪秒customSessionManager.setGlobalSessionTimeout(20000);return customSessionManager;} }

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 接口的方法
/** * 自定義session持久化 * @return */ public RedisSessionDAO redisSessionDAO(){ RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(getRedisManager()); //設(shè)置sessionid生成器 redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator()); //設(shè)置自定義的sessionIdGenerator return redisSessionDAO; }

總結(jié)

以上是生活随笔為你收集整理的一文看懂Shiro权限管理框架!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。