SpringBoot整合Shiro實(shí)現(xiàn)權(quán)限控制的代碼實(shí)現(xiàn)
1、SpringBoot整合Shiro
Apache Shiro是一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼和會(huì)話管理。
1.1、shiro簡介
shiro有個(gè)核心組件,分別為Subject、SecurityManager和Realms
- Subject:相當(dāng)于當(dāng)前操作的”用戶“,這個(gè)用戶不一定是一個(gè)具體的人,是一個(gè)抽象的概念,表明的是和當(dāng)前程序進(jìn)行交互的任何東西,例如爬蟲、腳本、等等。所有的Subject都綁定到SecurityManager上,與 Subject 的所有交互都會(huì)委托給 SecurityManager;可以把 Subject 認(rèn)為是一個(gè)門面;SecurityManager 才是實(shí)際的執(zhí)行者。
- SecurityManager:這個(gè)是shiro框架的核心,所有與安全相關(guān)的操作都會(huì)與它進(jìn)行交互,它管理者所有的Subject。
- Realms:充當(dāng)了Shiro與應(yīng)用安全數(shù)據(jù)間的”橋梁“,當(dāng)對(duì)用戶執(zhí)行認(rèn)證(登錄)和授權(quán)(訪問控制)驗(yàn)證時(shí),SecurityManager 需要從 Realm 獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應(yīng)的角色 / 權(quán)限進(jìn)行驗(yàn)證用戶是否能進(jìn)行操作。
如果想要更加深入的了解的shrio可以參考:https://www.w3cschool.cn/shiro/co4m1if2.html
1.2、代碼的具體實(shí)現(xiàn)
1.2.1、Maven的配置
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
<!--shiro整合thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!--shiro緩存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.7.1</version>
</dependency>
shiro默認(rèn)是與jsp進(jìn)行使用的,而這里是shiro整合thymeleaf所有要導(dǎo)入shiro整合thymeleaf的jar包
1.2.2、整合需要實(shí)現(xiàn)的類
- 一般來說整合只需要完成兩個(gè)類的實(shí)現(xiàn)即可
- 一個(gè)是 ShiroConfig 一個(gè)是 CustomerRealm
- 如果需要添加shiro緩存并且不是自帶的緩存而是redis緩存還需要進(jìn)行另外兩個(gè)類的編寫
- 一個(gè)是 RedisCache 一個(gè)是 RedisCacheManager
1.2.3、項(xiàng)目結(jié)構(gòu)

1.2.4、ShiroConfig的實(shí)現(xiàn)
未加shiro的緩存
package com.yuwen.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.yuwen.shiro.cache.RedisCacheManager;
import com.yuwen.shiro.realm.CustomerRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//讓頁面shiro標(biāo)簽生效
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
//1、創(chuàng)建shiroFilter 負(fù)責(zé)攔截所有請求
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//給filter設(shè)置安全管理
factoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系統(tǒng)的受限資源
//配置系統(tǒng)公共資源 全部都能訪問的設(shè)置anon
Map<String,String> map = new HashMap<>();
map.put("/main","authc");//請求這個(gè)資源需要認(rèn)證和授權(quán) authc表示需要認(rèn)證后才能訪問
map.put("/admin","roles[admin]"); //表示admin角色才能訪問 roles[]表示需要什么角色才能訪問
map.put("/manage","perms[user:*:*]"); //表示需要user:*:*權(quán)限才能訪問 perms[]表示需要什么權(quán)限才能訪問
//訪問需要認(rèn)證的頁面如果未登錄會(huì)跳轉(zhuǎn)到/login路由進(jìn)行登陸
factoryBean.setLoginUrl("/login");
//訪問未授權(quán)頁面會(huì)自動(dòng)跳轉(zhuǎn)到/unAuth路由
factoryBean.setUnauthorizedUrl("/unAuth");
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
//2、創(chuàng)建安全管理器
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//給安全管理器設(shè)置
securityManager.setRealm(realm);
return securityManager;
}
//3、創(chuàng)建自定義的realm
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
//修改憑證校驗(yàn)匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//設(shè)置加密算法為md5
credentialsMatcher.setHashAlgorithmName("MD5");
//設(shè)置散列次數(shù)
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
return customerRealm;
}
}
因?yàn)橐话阍跀?shù)據(jù)庫中設(shè)置明文密碼不安全,所有我這里對(duì)密碼進(jìn)行了md5加密,我的加密方式為:密碼 = 密碼+鹽+散列次數(shù) 而后進(jìn)行MD5加密 所以這里創(chuàng)建自定義的realm時(shí)需要進(jìn)行設(shè)置匹配器這樣登錄時(shí)密碼才能匹配成功
1.2.5、CustomerRealm的實(shí)現(xiàn)
package com.yuwen.shiro.realm;
import com.yuwen.pojo.User;
import com.yuwen.pojo.vo.ViewPerms;
import com.yuwen.pojo.vo.ViewRole;
import com.yuwen.service.UserService;
import com.yuwen.shiro.salt.MyByteSource;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.List;
//自定義realm
public class CustomerRealm extends AuthorizingRealm {
@Resource
private UserService userService;
//授權(quán)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//獲取身份信息
String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();
//根據(jù)主身份信息獲取角色 和 權(quán)限信息
List<ViewRole> roles = userService.findRolesByUsername(primaryPrincipal);
if (!CollectionUtils.isEmpty(roles)){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
roles.forEach(viewRole -> {
simpleAuthorizationInfo.addRole(viewRole.getName());
//權(quán)限信息
List<ViewPerms> perms = userService.findPermsByRoleId(viewRole.getName());
if (!CollectionUtils.isEmpty(perms)){
perms.forEach(viewPerms -> {
simpleAuthorizationInfo.addStringPermission(viewPerms.getPName());
});
}
});
return simpleAuthorizationInfo;
}
return null;
}
//認(rèn)證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲取登入的身份信息
String principal = (String) authenticationToken.getPrincipal();
User user = userService.findByUsername(principal);
if (!ObjectUtils.isEmpty(user)){
//ByteSource.Util.bytes(user.getSalt()) 通過這個(gè)工具將鹽傳入
//如果身份認(rèn)證驗(yàn)證成功,返回一個(gè)AuthenticationInfo實(shí)現(xiàn);
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),new MyByteSource(user.getSalt()),this.getName());
}
return null;
}
}
在登錄時(shí)會(huì)自動(dòng)調(diào)用這個(gè)身份驗(yàn)證 在驗(yàn)證時(shí)如果出錯(cuò),會(huì)報(bào)異常,我在controller層接收了異常并處理
controller層中登錄時(shí)的異常處理
@PostMapping("/login")
public String login(String username,String password){
//獲取主體對(duì)象
Subject subject = SecurityUtils.getSubject();
try {
//自動(dòng)調(diào)用CustomerRealm 類中的身份驗(yàn)證方法
subject.login(new UsernamePasswordToken(username,password));
return "index";
}catch (UnknownAccountException e){ //接收異常并處理
e.printStackTrace();
model.addAttribute("msg","用戶名有誤,請重新登錄");
}catch (IncorrectCredentialsException e){//接收異常并處理
e.printStackTrace();
model.addAttribute("msg","密碼有誤,請重新登錄");
}
return "login";
}
1.2.6、shiro緩存配置
定義了shiro緩存,用戶登錄后,其用戶信息、擁有的角色 / 權(quán)限不必每次去查,這樣可以提高效率
默認(rèn)緩存的配置
在 ShiroConfig中 的 getRealm() 方法中開啟緩存管理
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
//開啟緩存管理
customerRealm.setCacheManager(new EhCacheManager());
//開啟全局緩存
customerRealm.setCachingEnabled(true);
//開啟認(rèn)證緩存
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthenticationCacheName("authenticationCache");
//開啟權(quán)限緩存
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCacheName("authorizationCache");
return customerRealm;
}
與reids整合的緩存這里就不說明了,放在源碼里自己查看,源碼在下方
1.2.7、主頁index.html的設(shè)置
在這里用標(biāo)簽來判斷某些區(qū)域需要認(rèn)證或什么角色或者什么權(quán)限才能訪問
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>首頁</title>
<link rel="shortcut icon" href="#">
</head>
<body>
<h1>index</h1>
<a href="/logout">退出</a>
<div>
<a href="/main">main</a> | <a href="/manage">manage</a> | <a href="/admin">admin</a>
</div>
<!--獲取認(rèn)證信息-->
用戶:<span shiro:principal=""></span><hr>
<!--認(rèn)證處理-->
<span shiro:authenticated=""><hr>
顯示認(rèn)證通過內(nèi)容
</span>
<span shiro:notAuthenticated=""><hr>
沒有認(rèn)證時(shí) 顯示
</span>
<!--授權(quán)角色-->
<span shiro:hasRole="admin"><hr>
admin角色 顯示
</span>
<span shiro:hasPermission="user:*:*"><hr>
具有用戶模塊的"user:*:*"權(quán)限 顯示
</span>
</body>
</html>
1.3、簡單測試

1.3.1、admin角色所有權(quán)限測試

1.3.2、無角色有權(quán)限測試

1.3.3、無角色無權(quán)限測試


1.4 項(xiàng)目源碼
需要源碼的在這:https://gitee.com/residual-temperature/shiro-demo
到此這篇關(guān)于SpringBoot整合Shiro實(shí)現(xiàn)權(quán)限控制的方法的文章就介紹到這了,更多相關(guān)SpringBoot整合Shiro權(quán)限控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot+Spring Security+JWT實(shí)現(xiàn)RESTful Api權(quán)限控制的方法
- Springboot+Shiro+Jwt實(shí)現(xiàn)權(quán)限控制的項(xiàng)目實(shí)踐
- SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制
- Springboot+Spring Security實(shí)現(xiàn)前后端分離登錄認(rèn)證及權(quán)限控制的示例代碼
- Java SpringBoot 使用攔截器作為權(quán)限控制的實(shí)現(xiàn)方法
- Springboot和bootstrap實(shí)現(xiàn)shiro權(quán)限控制配置過程
- Springboot+Vue+shiro實(shí)現(xiàn)前后端分離、權(quán)限控制的示例代碼
- SpringBoot詳解shiro過濾器與權(quán)限控制
- SpringBoot集成Swagger使用SpringSecurity控制訪問權(quán)限問題
- 淺談基于SpringBoot實(shí)現(xiàn)一個(gè)簡單的權(quán)限控制注解
- SpringSecurity實(shí)現(xiàn)RBAC權(quán)限管理
相關(guān)文章
JavaWeb實(shí)現(xiàn)文件上傳與下載實(shí)例詳解
在Web應(yīng)用程序開發(fā)中,文件上傳與下載功能是非常常用的功能,下面通過本文給大家介紹JavaWeb實(shí)現(xiàn)文件上傳與下載實(shí)例詳解,對(duì)javaweb文件上傳下載相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-02-02
Java中為什么this可以調(diào)用當(dāng)前實(shí)例
本文主要介紹了為什么可以通過this關(guān)鍵字訪問到當(dāng)前對(duì)象呢,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Java數(shù)據(jù)結(jié)構(gòu)之線性表
線性表是其組成元素間具有線性關(guān)系的一種數(shù)據(jù)結(jié)構(gòu),對(duì)線性表的基本操作主要有,獲取元素,設(shè)置元素值,遍歷,插入,刪除,查找,替換,排序等。而線性表可以采用順序儲(chǔ)存結(jié)構(gòu)和鏈?zhǔn)絻?chǔ)存結(jié)構(gòu),本節(jié)主要講解順序表、單鏈表以及雙鏈表的各種基本操作。2017-03-03
java向mysql插入數(shù)據(jù)亂碼問題的解決方法
這篇文章主要為大家詳細(xì)介紹了java向mysql插入數(shù)據(jù)亂碼問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Java中使用@CrossOrigin和Proxy解決跨域問題詳解
這篇文章主要介紹了Java中使用@CrossOrigin和Proxy解決跨域問題詳解,在Web開發(fā)中,如果前端頁面和后端接口不在同一個(gè)域名下,就會(huì)發(fā)生跨域請求的問題,同源策略是瀏覽器的一種安全策略,它限制了來自不同源的客戶端腳本在瀏覽器中運(yùn)行時(shí)的交互,需要的朋友可以參考下2023-12-12

