SpringBoot整合Shiro實現(xiàn)權限控制的代碼實現(xiàn)
1、SpringBoot整合Shiro
Apache Shiro是一個強大且易用的Java安全框架,執(zhí)行身份驗證、授權、密碼和會話管理。
1.1、shiro簡介
shiro有個核心組件,分別為Subject、SecurityManager和Realms
- Subject:相當于當前操作的”用戶“,這個用戶不一定是一個具體的人,是一個抽象的概念,表明的是和當前程序進行交互的任何東西,例如爬蟲、腳本、等等。所有的Subject都綁定到SecurityManager上,與 Subject 的所有交互都會委托給 SecurityManager;可以把 Subject 認為是一個門面;SecurityManager 才是實際的執(zhí)行者。
- SecurityManager:這個是shiro框架的核心,所有與安全相關的操作都會與它進行交互,它管理者所有的Subject。
- Realms:充當了Shiro與應用安全數(shù)據(jù)間的”橋梁“,當對用戶執(zhí)行認證(登錄)和授權(訪問控制)驗證時,SecurityManager 需要從 Realm 獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應的角色 / 權限進行驗證用戶是否能進行操作。
如果想要更加深入的了解的shrio可以參考:https://www.w3cschool.cn/shiro/co4m1if2.html
1.2、代碼的具體實現(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默認是與jsp進行使用的,而這里是shiro整合thymeleaf所有要導入shiro整合thymeleaf的jar包
1.2.2、整合需要實現(xiàn)的類
- 一般來說整合只需要完成兩個類的實現(xiàn)即可
- 一個是 ShiroConfig 一個是 CustomerRealm
- 如果需要添加shiro緩存并且不是自帶的緩存而是redis緩存還需要進行另外兩個類的編寫
- 一個是 RedisCache 一個是 RedisCacheManager
1.2.3、項目結構
1.2.4、ShiroConfig的實現(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標簽生效 @Bean public ShiroDialect shiroDialect(){ return new ShiroDialect(); } //1、創(chuàng)建shiroFilter 負責攔截所有請求 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); //給filter設置安全管理 factoryBean.setSecurityManager(defaultWebSecurityManager); //配置系統(tǒng)的受限資源 //配置系統(tǒng)公共資源 全部都能訪問的設置anon Map<String,String> map = new HashMap<>(); map.put("/main","authc");//請求這個資源需要認證和授權 authc表示需要認證后才能訪問 map.put("/admin","roles[admin]"); //表示admin角色才能訪問 roles[]表示需要什么角色才能訪問 map.put("/manage","perms[user:*:*]"); //表示需要user:*:*權限才能訪問 perms[]表示需要什么權限才能訪問 //訪問需要認證的頁面如果未登錄會跳轉到/login路由進行登陸 factoryBean.setLoginUrl("/login"); //訪問未授權頁面會自動跳轉到/unAuth路由 factoryBean.setUnauthorizedUrl("/unAuth"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; } //2、創(chuàng)建安全管理器 @Bean public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("getRealm") Realm realm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //給安全管理器設置 securityManager.setRealm(realm); return securityManager; } //3、創(chuàng)建自定義的realm @Bean public Realm getRealm(){ CustomerRealm customerRealm = new CustomerRealm(); //修改憑證校驗匹配器 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //設置加密算法為md5 credentialsMatcher.setHashAlgorithmName("MD5"); //設置散列次數(shù) credentialsMatcher.setHashIterations(1024); customerRealm.setCredentialsMatcher(credentialsMatcher); return customerRealm; } }
因為一般在數(shù)據(jù)庫中設置明文密碼不安全,所有我這里對密碼進行了md5加密,我的加密方式為:密碼 = 密碼+鹽+散列次數(shù) 而后進行MD5加密 所以這里創(chuàng)建自定義的realm時需要進行設置匹配器這樣登錄時密碼才能匹配成功
1.2.5、CustomerRealm的實現(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; //授權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //獲取身份信息 String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal(); //根據(jù)主身份信息獲取角色 和 權限信息 List<ViewRole> roles = userService.findRolesByUsername(primaryPrincipal); if (!CollectionUtils.isEmpty(roles)){ SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); roles.forEach(viewRole -> { simpleAuthorizationInfo.addRole(viewRole.getName()); //權限信息 List<ViewPerms> perms = userService.findPermsByRoleId(viewRole.getName()); if (!CollectionUtils.isEmpty(perms)){ perms.forEach(viewPerms -> { simpleAuthorizationInfo.addStringPermission(viewPerms.getPName()); }); } }); return simpleAuthorizationInfo; } return null; } //認證 @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()) 通過這個工具將鹽傳入 //如果身份認證驗證成功,返回一個AuthenticationInfo實現(xiàn); return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),new MyByteSource(user.getSalt()),this.getName()); } return null; } }
在登錄時會自動調(diào)用這個身份驗證 在驗證時如果出錯,會報異常,我在controller層接收了異常并處理
controller層中登錄時的異常處理
@PostMapping("/login") public String login(String username,String password){ //獲取主體對象 Subject subject = SecurityUtils.getSubject(); try { //自動調(diào)用CustomerRealm 類中的身份驗證方法 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緩存,用戶登錄后,其用戶信息、擁有的角色 / 權限不必每次去查,這樣可以提高效率
默認緩存的配置
在 ShiroConfig中 的 getRealm() 方法中開啟緩存管理
@Bean public Realm getRealm(){ CustomerRealm customerRealm = new CustomerRealm(); //開啟緩存管理 customerRealm.setCacheManager(new EhCacheManager()); //開啟全局緩存 customerRealm.setCachingEnabled(true); //開啟認證緩存 customerRealm.setAuthenticationCachingEnabled(true); customerRealm.setAuthenticationCacheName("authenticationCache"); //開啟權限緩存 customerRealm.setAuthorizationCachingEnabled(true); customerRealm.setAuthorizationCacheName("authorizationCache"); return customerRealm; }
與reids整合的緩存這里就不說明了,放在源碼里自己查看,源碼在下方
1.2.7、主頁index.html的設置
在這里用標簽來判斷某些區(qū)域需要認證或什么角色或者什么權限才能訪問
<!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> <!--獲取認證信息--> 用戶:<span shiro:principal=""></span><hr> <!--認證處理--> <span shiro:authenticated=""><hr> 顯示認證通過內(nèi)容 </span> <span shiro:notAuthenticated=""><hr> 沒有認證時 顯示 </span> <!--授權角色--> <span shiro:hasRole="admin"><hr> admin角色 顯示 </span> <span shiro:hasPermission="user:*:*"><hr> 具有用戶模塊的"user:*:*"權限 顯示 </span> </body> </html>
1.3、簡單測試
1.3.1、admin角色所有權限測試
1.3.2、無角色有權限測試
1.3.3、無角色無權限測試
1.4 項目源碼
需要源碼的在這:https://gitee.com/residual-temperature/shiro-demo
到此這篇關于SpringBoot整合Shiro實現(xiàn)權限控制的方法的文章就介紹到這了,更多相關SpringBoot整合Shiro權限控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot+Spring Security+JWT實現(xiàn)RESTful Api權限控制的方法
- Springboot+Shiro+Jwt實現(xiàn)權限控制的項目實踐
- SpringBoot如何整合Springsecurity實現(xiàn)數(shù)據(jù)庫登錄及權限控制
- Springboot+Spring Security實現(xiàn)前后端分離登錄認證及權限控制的示例代碼
- Java SpringBoot 使用攔截器作為權限控制的實現(xiàn)方法
- Springboot和bootstrap實現(xiàn)shiro權限控制配置過程
- Springboot+Vue+shiro實現(xiàn)前后端分離、權限控制的示例代碼
- SpringBoot詳解shiro過濾器與權限控制
- SpringBoot集成Swagger使用SpringSecurity控制訪問權限問題
- 淺談基于SpringBoot實現(xiàn)一個簡單的權限控制注解
- SpringSecurity實現(xiàn)RBAC權限管理
相關文章
java向mysql插入數(shù)據(jù)亂碼問題的解決方法
這篇文章主要為大家詳細介紹了java向mysql插入數(shù)據(jù)亂碼問題的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09Java中使用@CrossOrigin和Proxy解決跨域問題詳解
這篇文章主要介紹了Java中使用@CrossOrigin和Proxy解決跨域問題詳解,在Web開發(fā)中,如果前端頁面和后端接口不在同一個域名下,就會發(fā)生跨域請求的問題,同源策略是瀏覽器的一種安全策略,它限制了來自不同源的客戶端腳本在瀏覽器中運行時的交互,需要的朋友可以參考下2023-12-12