個(gè)人定制網(wǎng)站軟件關(guān)鍵詞排名
第一章 入門(mén)概述
1.概念
shiro是一個(gè)Java安全框架,可以完成:認(rèn)證、授權(quán)、加密、會(huì)話(huà)管理、與web集成、緩存…
2.優(yōu)勢(shì)
● 易于使用,構(gòu)建簡(jiǎn)單
● 功能全面
● 靈活,可以在任何應(yīng)用程序環(huán)境中工作,并且不需要依賴(lài)它們
● 高效支持web,可以基于應(yīng)用程序URL和Web協(xié)議創(chuàng)建靈活的安全策略
● 兼容性強(qiáng),易于和其他框架和應(yīng)用程序集成
● 社區(qū)支持
3.與springsecurity的區(qū)別
● shiro需要和spring整合開(kāi)發(fā),而springsecurity基于spring開(kāi)發(fā),配合起來(lái)更便利
● springsecurity比shiro功能豐富,如安全維護(hù)
● shiro配置和使用簡(jiǎn)單,springsecurity較難
● shiro依賴(lài)性低,不需要任何框架和容器,可以獨(dú)立運(yùn)行,而springsecurity需要依賴(lài)spring容器
● shiro可以工作于任何應(yīng)用環(huán)境,獨(dú)立于容器
4.基本功能
- Authentication:身份認(rèn)證/登錄,驗(yàn)證用戶(hù)是否擁有對(duì)應(yīng)的身份
- authorization:權(quán)限驗(yàn)證,驗(yàn)證用戶(hù)是否擁有某個(gè)權(quán)限,判斷用戶(hù)可以進(jìn)行什么操作
- session management:會(huì)話(huà)管理,即用戶(hù)登錄后的一次會(huì)話(huà),在沒(méi)有登出之前,所有的信息都保存在會(huì)話(huà)中
- cryptography:加密,保證數(shù)據(jù)的安全,比如密碼加密存儲(chǔ)數(shù)據(jù)庫(kù)
- web support:web支持,集成web環(huán)境
- caching:緩存,比如用戶(hù)登錄后,用戶(hù)信息、角色和權(quán)限保存下來(lái)
- concurrency:多線程并發(fā)驗(yàn)證,比如在一個(gè)線程紅開(kāi)啟另一個(gè)線程,可以把權(quán)限自動(dòng)傳播過(guò)去
- Testing:測(cè)試功能
- run as:偽裝用戶(hù)
- remember me:記住我
5.原理
shiro外部架構(gòu)
subject:對(duì)外API核心,與應(yīng)用代碼直接交互的對(duì)象,與subject的所有交互都會(huì)委托為securitymanager
securitymanager:安全管理器,所有與安全相關(guān)的操作都會(huì)與securitymanager交互,管理所有的subject,是shiro的核心,相當(dāng)于spingmvc的dispatcherservlet的角色
realm:從realm中獲取安全數(shù)據(jù)信息(用戶(hù)、角色、權(quán)限)
shiro內(nèi)部架構(gòu)
- Subject:任何可以與應(yīng)用交互的“用戶(hù)”
- SecurityManager :相當(dāng)于 SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟; 所有具體的交互都通過(guò) SecurityManager 進(jìn)行控制;它管理著所有 Subject、且負(fù)責(zé)進(jìn) 行認(rèn)證、授權(quán)、會(huì)話(huà)及緩存的管理
- Authenticator:負(fù)責(zé) Subject 認(rèn)證,是一個(gè)擴(kuò)展點(diǎn),可以自定義實(shí)現(xiàn);可以使用認(rèn)證策略(Authentication Strategy),即什么情況下算用戶(hù)認(rèn)證通過(guò)了
- Authorizer:授權(quán)器、即訪問(wèn)控制器,用來(lái)決定主體是否有權(quán)限進(jìn)行相應(yīng)的操作;即控制著用戶(hù)能訪問(wèn)應(yīng)用中的哪些功能
- Realm:可以有 1 個(gè)或多個(gè) Realm,可以認(rèn)為是安全實(shí)體數(shù)據(jù)源,即用于獲取安全實(shí)體的;可以是 JDBC 實(shí)現(xiàn),也可以是內(nèi)存實(shí)現(xiàn)等等;由用戶(hù)提供;所以一般在應(yīng)用中都需要實(shí)現(xiàn)自己的 Realm
- SessionManager:管理 Session 生命周期的組件
- CacheManager:緩存控制器,來(lái)管理如用戶(hù)、角色、權(quán)限等的緩存的;因?yàn)檫@些數(shù)據(jù) 基本上很少改變,放到緩存中后可以提高訪問(wèn)的性能
- Cryptography:密碼模塊,Shiro 提高了一些常見(jiàn)的加密組件用于如密碼加密/解密。
第二章 基本使用
1.環(huán)境搭建
● 引入依賴(lài)
<dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.9.0</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency>
</dependencies>
● ini文件:src/main/resources/shiro.ini
[users]
zhangsan=z3
lisi=l4
2.登錄認(rèn)證
概念:
① 身份驗(yàn)證:一般需要提供如身份ID等一些標(biāo)識(shí)信息來(lái)表明登錄者的身份,如提供email,用戶(hù)名/密碼來(lái)證明
② 在shiro中,用戶(hù)需要提供principals(身份)和credentials(證明)給shiro,從而應(yīng)用能驗(yàn)證用戶(hù)身份
③ principals:身份,即主體的標(biāo)識(shí)屬性,可以是任何屬性,如用戶(hù)名、郵箱等,唯一即可。一個(gè)主體可以有多個(gè)principals,但只有一個(gè)Primary principals,一般是用戶(hù)名/郵箱/手機(jī)號(hào)
④ credentials:證明/憑證,即只有主體知道的安全值,如密碼/數(shù)字證書(shū)等
最常見(jiàn)的principals和credentials組合就是用戶(hù)名/密碼
流程:
① 收集用戶(hù)身份/憑證,即如用戶(hù)名/密碼
② 調(diào)用 Subject.login 進(jìn)行登錄,如果失敗將得到相應(yīng) 的 AuthenticationException異常,根據(jù)異常提示用戶(hù) 錯(cuò)誤信息;否則登錄成功
③ 創(chuàng)建自定義的 Realm 類(lèi),繼承 org.apache.shiro.realm.AuthenticatingRealm類(lèi),實(shí)現(xiàn) doGetAuthenticationInfo() 方法
實(shí)例:
public class ShiroRun {public static void main(String[] args) {//1.初始化獲取securitymanagerIniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//2.獲取subject對(duì)象Subject subject = SecurityUtils.getSubject();//3.創(chuàng)建token對(duì)象,web應(yīng)用用戶(hù)名密碼從頁(yè)面?zhèn)鬟fAuthenticationToken token = new UsernamePasswordToken("zhangsan", "z3");//4.完成登錄try {subject.login(token);System.out.println("login successful");} catch (UnknownAccountException e) {e.printStackTrace();System.out.println("用戶(hù)不存在");}catch (IncorrectCredentialsException e) {e.printStackTrace();System.out.println("密碼錯(cuò)誤");}catch (AuthenticationException e) {e.printStackTrace();System.out.println("登錄失敗");}}
}
3.授權(quán)
① 授權(quán),也叫訪問(wèn)控制,即在應(yīng)用中控制誰(shuí)訪問(wèn)哪些資源(如訪問(wèn)頁(yè)面/編輯數(shù)據(jù)/頁(yè)面 操作等)。在授權(quán)中需了解的幾個(gè)關(guān)鍵對(duì)象:主體(Subject)、資源(Resource)、權(quán)限 (Permission)、角色(Role)
② 主體(Subject):訪問(wèn)應(yīng)用的用戶(hù),在 Shiro 中使用 Subject 代表該用戶(hù)。用戶(hù)只有授權(quán) 后才允許訪問(wèn)相應(yīng)的資源
③ 資源(Resource):在應(yīng)用中用戶(hù)可以訪問(wèn)的 URL,比如訪問(wèn) JSP 頁(yè)面、查看/編輯 某些 數(shù)據(jù)、訪問(wèn)某個(gè)業(yè)務(wù)方法、打印文本等等都是資源。用戶(hù)只要授權(quán)后才能訪問(wèn)
④ 權(quán)限(Permission):安全策略中的原子授權(quán)單位,通過(guò)權(quán)限我們可以表示在應(yīng)用中用戶(hù) 有沒(méi)有操作某個(gè)資源的權(quán)力。即權(quán)限表示在應(yīng)用中用戶(hù)能不能訪問(wèn)某個(gè)資源,如:訪問(wèn)用 戶(hù)列表頁(yè)面查看/新增/修改/刪除用戶(hù)數(shù)據(jù)(即很多時(shí)候都是CRUD(增查改刪)式權(quán)限控 制)等。權(quán)限代表了用戶(hù)有沒(méi)有操作某個(gè)資源的權(quán)利,即反映在某個(gè)資源上的操作允不允許
⑤ Shiro 支持粗粒度權(quán)限(如用戶(hù)模塊的所有權(quán)限)和細(xì)粒度權(quán)限(操作某個(gè)用戶(hù)的權(quán)限, 即實(shí)例級(jí)別的)
⑥ 角色(Role):權(quán)限的集合,一般情況下會(huì)賦予用戶(hù)角色而不是權(quán)限,即這樣用戶(hù)可以擁有 一組權(quán)限,賦予權(quán)限時(shí)比較方便。典型的如:項(xiàng)目經(jīng)理、技術(shù)總監(jiān)、CTO、開(kāi)發(fā)工程師等都是角色,不同的角色擁有一組不同的權(quán)限
授權(quán)方式:
- 編程式:
subject.hasRole("admin")
- 注解式:
@RequiresRoles("admin")
- jsp/gsp標(biāo)簽:
<shiro:hasRole name="admin"></shiro:hasRole>
授權(quán)流程:
- 首先調(diào)用Subject.isPermitted*/hasRole*接口,其會(huì)委托給SecurityManager,而SecurityManager接著會(huì)委托給 Authorizer
- Authorizer是真正的授權(quán)者,如果調(diào)用如isPermitted(“user:view”),其首先會(huì)通過(guò)PermissionResolver把字符串轉(zhuǎn)換成相應(yīng)的Permission實(shí)例
- 在進(jìn)行授權(quán)之前,其會(huì)調(diào)用相應(yīng)的Realm獲取Subject相應(yīng)的角色/權(quán)限用于匹配傳入的角色/權(quán)限
- Authorizer會(huì)判斷Realm的角色/權(quán)限是否和傳入的匹配,如果有多個(gè)Realm,會(huì)委托給ModularRealmAuthorizer進(jìn)行循環(huán)判斷,如果匹配如isPermitted*/hasRole* 會(huì)返回 true,否則返回false表示授權(quán)失敗
實(shí)例:
1.給shiro.ini增加角色配置
[users]
fate=admin,role1,role2
fuck=123456
2.增加權(quán)限配置
[roles]
role1=user:insert,user:select
3.測(cè)試代碼
public static void main(String[] args) {//1.初始化獲取securitymanagerIniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//2.獲取subject對(duì)象Subject subject = SecurityUtils.getSubject();//3.創(chuàng)建token對(duì)象,web應(yīng)用用戶(hù)名密碼從頁(yè)面?zhèn)鬟fAuthenticationToken token = new UsernamePasswordToken("zhangsan", "z3");//4.完成登錄try {subject.login(token);System.out.println("login successful");//5.判斷角色boolean hasRole = subject.hasRole("role1");System.out.println("是否擁有role1角色:" + hasRole);//6.判斷權(quán)限boolean permitted = subject.isPermitted("user:insert");System.out.println("是否擁有此權(quán)限:" + permitted);//也可以用checkPermission方法,但沒(méi)有返回值,沒(méi)權(quán)限拋AuthenticationExceptionsubject.checkPermission("user:update");} catch (UnknownAccountException e) {e.printStackTrace();System.out.println("用戶(hù)不存在");} catch (IncorrectCredentialsException e) {e.printStackTrace();System.out.println("密碼錯(cuò)誤");} catch (AuthenticationException e) {e.printStackTrace();System.out.println("登錄失敗");}
}
4.加密
Shiro內(nèi)嵌很多常用的加密算法,比如MD5加密
public class ShiroMD5 {public static void main(String[] args) {//密碼明文String password = "admin";//使用MD5加密Md5Hash md5Hash = new Md5Hash(password);System.out.println("md5加密:" + md5Hash);//帶鹽的md5加密,鹽就是在密碼明文后拼接新字符串,然后再進(jìn)行加密Md5Hash md5Hash1 = new Md5Hash(password, "salt");System.out.println("帶鹽的md5加密:" + md5Hash1.toHex());//為了保證安全,避免被破解還可以多次迭代加密,保證數(shù)據(jù)安全Md5Hash md5Hash2 = new Md5Hash(password, "salt", 3);System.out.println("帶鹽的3次迭代加密:" + md5Hash2);//使用父類(lèi)進(jìn)行加密SimpleHash simpleHash = new SimpleHash("MD5", password, "salt", 3);System.out.println("父類(lèi)帶鹽的3次加密:" + simpleHash);}
}
5.自定義登錄認(rèn)證
Shiro 默認(rèn)的登錄認(rèn)證是不帶加密的,如果想要實(shí)現(xiàn)加密認(rèn)證需要自定義登錄認(rèn)證, 自定義Realm
public class MyRealm extends AuthenticatingRealm {//自定義登錄認(rèn)證方法,shiro的login方法的底層會(huì)調(diào)用該類(lèi)的認(rèn)證方法進(jìn)行認(rèn)證//需要配置自定義的realm生效,應(yīng)該在ini文件中配置,如果有springboot框架,也可以在Springboot中進(jìn)行配置//該方法只是獲取進(jìn)行對(duì)比的信息,認(rèn)證邏輯還是按照shiro的底層認(rèn)證邏輯來(lái)完成protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.獲取身份信息String principal = authenticationToken.getPrincipal().toString();//2.獲取憑證信息String password = new String((char[]) authenticationToken.getCredentials());System.out.println("認(rèn)證的用戶(hù)信息:" + principal + "---" + password);//3.獲取數(shù)據(jù)庫(kù)中存儲(chǔ)的用戶(hù)信息if(principal.equals("zhangsan")){//3.1數(shù)據(jù)庫(kù)中存儲(chǔ)的加鹽3次迭代的密碼String pwdInfo = "b073e9301c412431159e7075340c4c66";//4.創(chuàng)建封裝校驗(yàn)邏輯的對(duì)象,封裝數(shù)據(jù)返回AuthenticationInfo info = new SimpleAuthenticationInfo(authenticationToken.getPrincipal(),pwdInfo,ByteSource.Util.bytes("salt"),authenticationToken.getPrincipal().toString());return info;}return null;}
}
添加配置信息,讓shiro知曉你使用的是自定義的Realm
[main]
#在shiro.ini中添加配置信息
md5CredentialsMatcher=org.apache.shiro.authc.credential.Md5CredentialsMatcher
md5CredentialsMatcher.hashIterations=3
myrealm=com.atguigu.shirotest.MyRealm
myrealm.credentialsMatcher=$md5CredentialsMatcher
securityManager.realms=$myrealm[users]
zhangsan=7174f64b13022acd3c56e2781e098a5f,role1,role2
lisi=l4 [roles]
role1=user:insert,user:select
第三章 與springboot整合
框架整合
1.引入依賴(lài):
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version>
</parent>
<dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.9.0</version></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.5</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>
2.配置文件
# mybatis配置
mybatis-plus:configuration:# 日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath:mapper/ *.xmlspring:
# 數(shù)據(jù)庫(kù)配置datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/shirodb?characterEncoding=utf-8&useSSL=falsepassword: rootusername: root
# json格式j(luò)ackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8shiro:
# 登錄接口loginUrl: /myController/login
3.自定義realm
@Component
public class MyRealm extends AuthorizingRealm{@Autowiredprivate UserService userService;//自定義授權(quán)方法@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}//自定義登錄認(rèn)證方法@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.獲取用戶(hù)身份信息String name = authenticationToken.getPrincipal().toString();//2.調(diào)用業(yè)務(wù)層獲取用戶(hù)信息(數(shù)據(jù)庫(kù))User user = userService.getUserInfoByName(name);//3.非空判斷,將數(shù)據(jù)封裝返回if(user != null){AuthenticationInfo info = new SimpleAuthenticationInfo(authenticationToken.getPrincipal(),user.getPwd(),ByteSource.Util.bytes("salt"),authenticationToken.getPrincipal().toString());return info;}return null;}
}
4.配置類(lèi)
@Slf4j
@Configuration
public class ShiroConfig {@Autowiredprivate MyRealm myRealm;//配置SecurityManager@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager(){//1.創(chuàng)建defaultWebSecurityManager 對(duì)象DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();//2.創(chuàng)建加密對(duì)象,設(shè)置相關(guān)屬性HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();//2.1 采用md5加密matcher.setHashAlgorithmName("md5");//2.2 迭代加密次數(shù)matcher.setHashIterations(3);//3.將加密對(duì)象存儲(chǔ)到myRealm中myRealm.setCredentialsMatcher(matcher);//4.將myRealm存入defaultWebSecurityManager 對(duì)象defaultWebSecurityManager.setRealm(myRealm);//5.返回return defaultWebSecurityManager;}//配置Shiro內(nèi)置過(guò)濾器攔截范圍@Beanpublic DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition = new DefaultShiroFilterChainDefinition();
// 設(shè)置不認(rèn)證就可以訪問(wèn)的資源defaultShiroFilterChainDefinition.addPathDefinition("/myController/userLogin","anon");defaultShiroFilterChainDefinition.addPathDefinition("/login","anon");
// 設(shè)置需要進(jìn)行登錄認(rèn)證的攔截范圍defaultShiroFilterChainDefinition.addPathDefinition("/**","authc");return defaultShiroFilterChainDefinition;}
}
5.編寫(xiě)控制類(lèi)
@Controller
@RequestMapping("myController")
public class MyController{@GetMapping("userLogin")@ResponseBobypublic String userLogin(String name,String pwd){//1.獲取subject對(duì)象Subject subject = SecurityUtils.getSubject();//2.封裝請(qǐng)求數(shù)據(jù)到tokenAuthenticationToken token = new UsernamePasswordToken(name,pwd);//3.調(diào)用login方法進(jìn)行登錄認(rèn)證try{ subject.login(token);return "登錄成功";}catch(AuthenticationException e){e.printStackTrace();System.out.println("登錄失敗");return "登錄失敗";}
}