springboot集成shiro權(quán)限管理簡(jiǎn)單實(shí)現(xiàn)
前言
為了解決項(xiàng)目當(dāng)中的權(quán)限管理問(wèn)題,我們一般會(huì)選擇引入spring security或者shiro框架來(lái)幫助我們更好地更快地構(gòu)建權(quán)限管理體系。
依賴
首先第一步,我們需要給當(dāng)前項(xiàng)目引入對(duì)應(yīng)的依賴包。與spring boot集成一般首選starter包。
<!-- shiro權(quán)限管理框架 --> <dependency> ? ?<groupId>org.apache.shiro</groupId> ? ?<artifactId>shiro-spring-boot-web-starter</artifactId> ? ?<version>1.9.1</version> </dependency>
配置
無(wú)論是spring security還是shiro,兩者都是基于servlet的Filter過(guò)濾器機(jī)制實(shí)現(xiàn)的權(quán)限管理。所以第一步配置我們就需要把對(duì)應(yīng)的Filter給加入進(jìn)來(lái)。
Filter過(guò)濾器配置
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
? ?// 設(shè)置securityManager,負(fù)責(zé)權(quán)限驗(yàn)證的核心事務(wù)處理。
shiroFilterFactoryBean.setSecurityManager(securityManager());
? ?// 配置過(guò)濾器鏈
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
? ?// anon表示該url不需要經(jīng)過(guò)權(quán)限驗(yàn)證
filterChainDefinitionMap.put("/static/**", "anon");
? ?// logout表示用戶登出功能的過(guò)濾器;調(diào)用指定的url會(huì)讓已經(jīng)登陸的用戶退出
filterChainDefinitionMap.put("/logout", "logout");
? ?// authc過(guò)濾器表示對(duì)應(yīng)的url都需要權(quán)限驗(yàn)證才能訪問(wèn)
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
? ?// 配置用戶登陸的url。調(diào)用該接口需要傳username和password字段。
shiroFilterFactoryBean.setLoginUrl("/login");
? ?// 登陸成功自動(dòng)跳轉(zhuǎn)頁(yè)面
shiroFilterFactoryBean.setSuccessUrl("/index");
return shiroFilterFactoryBean;
}securityManager配置
看上面代碼,我們就可以分析出,需要把這些Filter過(guò)濾器創(chuàng)建出來(lái),除了配置一些需要攔截的url之外,我們還要?jiǎng)?chuàng)建一個(gè)非常核心的securityManager,這個(gè)才是權(quán)限驗(yàn)證過(guò)程處理的核心。
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
return securityManager;
}別看securityManager里面我只設(shè)置了Realm,其實(shí)securityManager就像一個(gè)大領(lǐng)導(dǎo),壓根不干活兒,有啥活都派給底下的小弟去干。
我們來(lái)看看securityManager底下到底有哪些小弟;
后續(xù)的博客中會(huì)逐一解析這些小弟的作用,今天就先把如何簡(jiǎn)單集成shiro講完。
- SessionManager:管理用戶session;
- SubjectDao:負(fù)責(zé)Subject保存和刪除;
- CacheManager:負(fù)責(zé)緩存管理;
- Realm:負(fù)責(zé)用戶登陸和權(quán)限驗(yàn)證;
- RememberMeManager:負(fù)責(zé)實(shí)現(xiàn)remember me功能;
- EventBus:事件總線;
- SubjectFactory:創(chuàng)建Subject;

Realm配置
下面我們應(yīng)該要給出如何判斷用戶登陸和鑒定用戶權(quán)限了:
@Bean
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(credentialsMatcher());
return userRealm;
}因?yàn)镽ealm需要查詢用戶的密碼已經(jīng)改用戶對(duì)應(yīng)的角色和權(quán)限,所以我們自定義了自己的Realm。
通過(guò)繼承AuthorizingRealm:
package com.example.awesomespring.security;
?
import com.example.awesomespring.bo.AccountInfo;
import com.example.awesomespring.service.AccountService;
import org.apache.shiro.authc.*;
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.ByteSource;
?
import javax.annotation.Resource;
?
public class UserRealm extends AuthorizingRealm {
?
?@Resource
?private AccountService accountService;
?
@Override
?protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
? ? ? ?// 從authenticationToken中獲取用戶提交的username
? ?String accountName = (String) authenticationToken.getPrincipal();
? ? ? ?// 通過(guò)username查詢到對(duì)應(yīng)的用戶信息
? ?AccountInfo accountInfo = accountService.findByAccount(accountName);
? ?if (accountInfo == null) {
? ? ?return null;
? }
? ? ? ?// 取出查詢到的用戶密碼
? ?String password = accountInfo.getPassword();
? ? ? ?// 取出用戶密碼加密需要用到的鹽值
? ?String salt = accountInfo.getSalt();
? ? ? ?// 把查詢出來(lái)的用戶信息、密碼、加密鹽值、Realm名稱包裝進(jìn)SimpleAuthenticationInfo返回
? ?return new SimpleAuthenticationInfo(accountInfo, password, ByteSource.Util.bytes(salt), getName());
}
?
? ?// 這個(gè)方法是在用戶登陸成功后,調(diào)用需要權(quán)限才能訪問(wèn)的接口時(shí)才來(lái)鑒定權(quán)限
?@Override
?protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
? ? ? ?// 拿到已經(jīng)登陸的用戶信息
? ?AccountInfo accountInfo = (AccountInfo) principalCollection.getPrimaryPrincipal();
? ?SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
? ?// 下面的角色和權(quán)限需要從數(shù)據(jù)庫(kù)中查詢
? ? ? ?// 設(shè)置角色
? ?authorizationInfo.addRole("User");
? ?// 設(shè)置權(quán)限
? ?authorizationInfo.addStringPermission("User:read");
? ? ? ?// 返回角色和權(quán)限
? ?return authorizationInfo;
}
}到這里,我們可以看出,Realm就是用來(lái)幫助用戶登陸,并且在用戶訪問(wèn)需要權(quán)限的接口時(shí),查詢出用戶的角色和權(quán)限,交給決策器來(lái)決定用戶是否登陸成功,鑒權(quán)是否通過(guò)。
密碼加密
這是一個(gè)不容易被注意的點(diǎn),使用默認(rèn)的配置時(shí)只會(huì)簡(jiǎn)單的比較輸入的密碼和數(shù)據(jù)庫(kù)查出來(lái)的密碼是否一致,這顯然是不符合要求的,因?yàn)槲覀償?shù)據(jù)庫(kù)里面的密碼是已經(jīng)加密好了的。
另一個(gè)就是我們?cè)趧?chuàng)建用戶的時(shí)候也是需要使用到同樣的密碼加密手段,所以我們有必要把密碼加密給拎出來(lái)處理一下,做一個(gè)自定義的加密。
@Bean
public CredentialsMatcher credentialsMatcher() {
return new PasswordHashedCredentialsMatcher("MD5");
}
?
package com.example.awesomespring.security;
?
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.util.ByteSource;
?
/**
* @author zouwei
* @className PasswordHashedCredentialsMatcher
* @date: 2022/7/31 下午3:43
* @description:
*/
public class PasswordHashedCredentialsMatcher extends HashedCredentialsMatcher {
?
?public PasswordHashedCredentialsMatcher(String hashAlgorithmName) {
? ?super(hashAlgorithmName);
? ?setHashIterations(2);
}
?
?public String encryptedPassword(String passwordString, String salt) {
? ?return hashProvidedCredentials(passwordString, ByteSource.Util.bytes(salt), getHashIterations()).toHex();
}
}其實(shí)我們就是繼承了HashedCredentialsMatcher,在它的基礎(chǔ)上提高了直接針對(duì)密碼加密的功能。這樣既能滿足shiro的登陸驗(yàn)證,又能拿出來(lái)用到創(chuàng)建用戶的時(shí)候加密使用。
測(cè)試
為了驗(yàn)證我們的配置是否成功,我們需要寫(xiě)幾個(gè)測(cè)試接口:
package com.example.awesomespring.controller;
?
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
?
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
?
@GetMapping("/{id}")
@RequiresPermissions("User:info")
String getUserInfo(@PathVariable String id) {
log.info("獲取用戶信息開(kāi)始");
log.info("請(qǐng)求參數(shù) id:{}", id);
log.info("獲取用戶信息結(jié)束");
return "getUserInfo";
}
?
@GetMapping("/hello")
String hello() {
return "hello world!";
}
?
@GetMapping("/read")
@RequiresPermissions("User:read")
String read() {
return "read";
}
}上面的幾個(gè)接口,我們?nèi)绻苯釉L問(wèn)的話,在瀏覽器中,會(huì)被重定向到"/login"頁(yè)面,因?yàn)槲覀兡壳皼](méi)有這個(gè)頁(yè)面,所以一旦重定向這個(gè)url,說(shuō)明用戶沒(méi)有登陸。
其次我們通過(guò)調(diào)用post請(qǐng)求"/login",提交username和password成功登陸后,頁(yè)面會(huì)重定向到"/index"頁(yè)面,目前我們也是沒(méi)有這個(gè)頁(yè)面的,不過(guò)從響應(yīng)體中可以看到響應(yīng)碼是302。
當(dāng)我們?cè)谟脩舻顷懗晒?,再訪問(wèn)"/user/read"是能正常訪問(wèn),并返回結(jié)果"read";如果訪問(wèn)"/user/123456"是不行的,頁(yè)面會(huì)直接報(bào)500錯(cuò)誤。
通過(guò)上述測(cè)試,我們已經(jīng)初步完成了shiro的集成
到此這篇關(guān)于springboot集成shiro權(quán)限管理簡(jiǎn)單實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)springboot 集成shiro內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud Alibaba Nacos 整合SpringBoot A
這篇文章主要介紹了SpringCloud Alibaba Nacos 整合SpringBoot Admin實(shí)戰(zhàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法
Java語(yǔ)言的Calendar(日歷),Date(日期),和DateFormat(日期格式)組成了Java標(biāo)準(zhǔn)的一個(gè)基本但是非常重要的部分,下面這篇文章主要給大家介紹了關(guān)于java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法的相關(guān)資料,需要的朋友可以參考下2023-12-12
關(guān)于Spring Boot獲取bean的3種方式
這篇文章主要介紹了關(guān)于Spring Boot獲取bean的3種方式,在spring中ApplicationContext這個(gè)上下文對(duì)象是獲取bean的基礎(chǔ),需要的朋友可以參考下2023-04-04
關(guān)于mybatis-plus插件使用時(shí)的一些問(wèn)題小結(jié)
這篇文章主要給大家介紹了關(guān)于mybatis-plus插件使用時(shí)的一些問(wèn)題的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03
Java編程實(shí)現(xiàn)軌跡壓縮算法開(kāi)放窗口實(shí)例代碼
這篇文章主要介紹了Java編程實(shí)現(xiàn)軌跡壓縮算法開(kāi)放窗口實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-11
詳解Java的TCP/IP編程學(xué)習(xí)--基于定界符的成幀
這篇文章主要介紹了Java的TCP/IP編程學(xué)習(xí)--基于定界符的成幀,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
mybatis的xml中使用@符號(hào)調(diào)用類方法示例
這篇文章主要為大家介紹了mybatis的xml中使用@符號(hào)調(diào)用類方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12

