Spring?Boot與JWT和Shiro整合實(shí)踐步驟(非常詳細(xì)!)
1. Spring Boot基礎(chǔ)介紹
在現(xiàn)代企業(yè)級(jí)應(yīng)用開發(fā)中,Spring Boot憑借其輕量級(jí)、快速開發(fā)的特性,已經(jīng)成為構(gòu)建微服務(wù)架構(gòu)項(xiàng)目的首選框架。本章將為讀者提供Spring Boot的入門級(jí)介紹,旨在幫助讀者快速掌握Spring Boot的基礎(chǔ)知識(shí),并了解其在項(xiàng)目中的具體應(yīng)用。
1.1 Spring Boot簡(jiǎn)介
Spring Boot是Spring的一個(gè)模塊,它提供了快速構(gòu)建項(xiàng)目的能力。通過“約定優(yōu)于配置”的原則,Spring Boot旨在簡(jiǎn)化Spring應(yīng)用的初始搭建以及開發(fā)過程。它通過提供默認(rèn)配置,幫助開發(fā)者避免繁瑣的配置工作,從而專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。
1.2 Spring Boot的核心特性
- 自動(dòng)配置 :Spring Boot能夠自動(dòng)配置Spring應(yīng)用,只有在特定情況下才需要手動(dòng)配置。
- 起步依賴 :通過提供特定的“starter”依賴,簡(jiǎn)化了構(gòu)建配置。
- 內(nèi)嵌Web服務(wù)器 :如Tomcat、Jetty或Undertow,無需部署WAR文件。
- 監(jiān)控和管理 :提供了一套生產(chǎn)就緒特性,比如指標(biāo)、健康檢查、外部化配置等。
1.3 入門示例
一個(gè)典型的Spring Boot應(yīng)用的入口是一個(gè)帶有 main 方法的類,通常包含 SpringApplication.run 調(diào)用:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
在 pom.xml 文件中添加Spring Boot的依賴,可以快速開始:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
以上是一個(gè)簡(jiǎn)單的Spring Boot應(yīng)用的搭建過程,接下來的內(nèi)容將深入探討如何將Spring Boot與其他技術(shù)棧整合,以實(shí)現(xiàn)更為復(fù)雜的應(yīng)用場(chǎng)景。
2. JWT認(rèn)證機(jī)制解析
2.1 JWT的基本概念和組成
2.1.1 JWT的定義和作用
JSON Web Token(JWT)是一種開放標(biāo)準(zhǔn)(RFC 7519),用于在互聯(lián)網(wǎng)環(huán)境中安全地傳輸信息。它作為一種緊湊的、自包含的方式,使得雙方可以以JSON對(duì)象的形式安全傳遞聲明(claims)。JWT通常用于身份驗(yàn)證和信息交換,特別適用于Web API的認(rèn)證場(chǎng)景。
在Web應(yīng)用中,一旦用戶登錄成功,服務(wù)器會(huì)生成一個(gè)JWT并返回給客戶端,之后客戶端將該JWT存儲(chǔ)在客戶端的瀏覽器或移動(dòng)設(shè)備中。客戶端每次向服務(wù)器發(fā)送請(qǐng)求時(shí),都需要在HTTP請(qǐng)求的頭信息中附帶該JWT。服務(wù)器通過解析JWT中的信息,就可以驗(yàn)證用戶的身份和訪問權(quán)限。
2.1.2 JWT的結(jié)構(gòu)和特點(diǎn)
JWT由三部分組成:Header(頭部)、Payload(載荷)和Signature(簽名)。這三部分通過點(diǎn)(.)連接在一起,形成一個(gè)完整的JWT字符串。
- Header(頭部) :頭部用于描述關(guān)于該JWT的最基本的信息,例如其類型(即JWT),以及所使用的簽名算法(如HMAC SHA256或者RSA)。
{
"alg": "HS256",
"typ": "JWT"
}
- Payload(載荷) :載荷就是存放有效信息的地方。這些信息包括但不限于發(fā)行者、過期時(shí)間、主題等,可以存放一些聲明(claims)。聲明是關(guān)于實(shí)體(通常是用戶)和其他數(shù)據(jù)的聲明,比如用戶ID、用戶名等。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
- Signature(簽名) :為了防止信息篡改,對(duì)頭部以及載荷的內(nèi)容進(jìn)行簽名。簽名的方法取決于頭部中指定的算法。使用Header中指定的算法對(duì)Header和Payload進(jìn)行加密,生成簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
2.2 JWT的工作原理和優(yōu)勢(shì)
2.2.1 認(rèn)證流程詳解
JWT的認(rèn)證流程一般如下:
- 用戶登錄到應(yīng)用系統(tǒng),并提供用戶名和密碼。
- 應(yīng)用系統(tǒng)驗(yàn)證用戶信息的正確性。
- 驗(yàn)證通過后,應(yīng)用系統(tǒng)創(chuàng)建一個(gè)JWT,將用戶的身份信息、權(quán)限信息和其他聲明打包到Payload中。
- 應(yīng)用系統(tǒng)對(duì)JWT的Header和Payload部分使用Base64Url編碼,并使用密鑰對(duì)它們進(jìn)行簽名。
- 將生成的JWT返回給用戶,用戶將JWT存儲(chǔ)在本地(例如瀏覽器的localStorage或sessionStorage中)。
- 用戶在之后的請(qǐng)求中,將JWT作為Authorization header附帶在每個(gè)HTTP請(qǐng)求中。
- 服務(wù)器收到請(qǐng)求后,提取JWT并驗(yàn)證其簽名,確認(rèn)其有效性和未被篡改。
- 如果JWT驗(yàn)證成功,服務(wù)器根據(jù)其中的聲明信息進(jìn)行用戶身份的識(shí)別,并授權(quán)訪問資源。
2.2.2 JWT相較于傳統(tǒng)認(rèn)證的優(yōu)勢(shì)
JWT相較于傳統(tǒng)的Session認(rèn)證機(jī)制,具有以下優(yōu)勢(shì):
- 無狀態(tài) :JWT不依賴服務(wù)器存儲(chǔ)認(rèn)證信息,減輕了服務(wù)器壓力。
- 跨域性 :JWT是純文本,因此可以跨不同的服務(wù)器使用,非常適合分布式系統(tǒng)。
- 標(biāo)準(zhǔn)化 :遵循開放標(biāo)準(zhǔn),可以跨不同平臺(tái)使用,方便前后端分離的項(xiàng)目。
- 緊湊性 :由于JWT的緊湊性,它可以作為URL的一部分傳輸。
2.3 JWT的安全性分析
2.3.1 JWT的加密和簽名機(jī)制
JWT通過Header和Payload的Base64Url編碼后與簽名組合而成。簽名是為了確保JWT沒有被篡改,其安全性依賴于密鑰的保密性。
- 加密 :通常用于JWT的加密是為了保護(hù)數(shù)據(jù)的隱私性,避免載荷信息泄露。
- 簽名 :使用密鑰對(duì)Header和Payload進(jìn)行簽名,可以確保數(shù)據(jù)的完整性和來源的可驗(yàn)證性。
2.3.2 JWT的安全漏洞及防范措施
JWT雖然方便,但也存在一些潛在的安全風(fēng)險(xiǎn):
- 泄露風(fēng)險(xiǎn) :如果JWT存儲(chǔ)在客戶端是暴露的狀態(tài)下,比如localStorage,它可能被惡意JavaScript訪問。可以通過HttpOnly的Cookie來存儲(chǔ)JWT,以減少泄露風(fēng)險(xiǎn)。
- 過期時(shí)間設(shè)置 :為JWT設(shè)置較短的過期時(shí)間可以減少令牌被盜用的風(fēng)險(xiǎn)。
- 加密和簽名算法 :使用強(qiáng)加密和簽名算法可以增強(qiáng)JWT的安全性。避免使用已被破解的算法,如HS256如果密鑰泄露的話。使用RSA、ECDSA等非對(duì)稱加密算法可以提高安全性。
接下來,我們深入探討JWT的加密和簽名機(jī)制以及如何實(shí)現(xiàn)和優(yōu)化這些機(jī)制,確保系統(tǒng)的安全穩(wěn)定運(yùn)行。
3. Shiro安全框架功能概覽
Shiro是Apache軟件基金會(huì)的一個(gè)開源安全框架,為應(yīng)用程序提供認(rèn)證、授權(quán)、加密和會(huì)話管理功能。本章節(jié)將深入探討Shiro的核心架構(gòu)和組件,以及其在企業(yè)級(jí)應(yīng)用中的實(shí)踐。
3.1 Shiro核心架構(gòu)和組件
3.1.1 Shiro的架構(gòu)組成
Shiro的架構(gòu)設(shè)計(jì)簡(jiǎn)潔而靈活,由三個(gè)主要部分組成:Subject、SecurityManager和Realms。
- Subject : 代表當(dāng)前的用戶,它與當(dāng)前用戶進(jìn)行交互。在應(yīng)用代碼中,Subject通常是指當(dāng)前正在執(zhí)行操作的用戶,或者代表當(dāng)前用戶的線程。
- SecurityManager : 是Shiro框架的心臟,負(fù)責(zé)處理所有Subject的請(qǐng)求。它管理所有的Subject和其它內(nèi)部安全組件。
- Realms : 連接外部數(shù)據(jù)源,比如應(yīng)用的數(shù)據(jù)庫(kù)。Shiro通過Realms獲取安全相關(guān)的數(shù)據(jù),如用戶角色、權(quán)限等。
3.1.2 主要組件功能解析
Shiro框架還包含其他組件,如 Authentication , Authorization , Session Management , Cryptography 等。這里我們將對(duì)每個(gè)組件的功能進(jìn)行分析。
- Authentication (認(rèn)證) : 驗(yàn)證用戶身份的過程。通常通過用戶名和密碼完成。
- Authorization (授權(quán)) : 確定一個(gè)用戶是否有操作某個(gè)資源的權(quán)限。
- Session Management (會(huì)話管理) : Shiro提供了完備的會(huì)話管理功能,包括創(chuàng)建會(huì)話、銷毀會(huì)話、會(huì)話存儲(chǔ)、并發(fā)性控制等。
- Cryptography (加密) : 提供加密、哈希和編碼的功能。Shiro通過它來保護(hù)數(shù)據(jù)的安全性。
3.2 Shiro的認(rèn)證與授權(quán)機(jī)制
3.2.1 用戶認(rèn)證流程和策略
Shiro提供了多種認(rèn)證策略,其中基于表單的認(rèn)證是最常見的。用戶輸入用戶名和密碼,Shiro會(huì)對(duì)這些信息進(jìn)行驗(yàn)證。下面是Shiro的認(rèn)證流程:
- 用戶提交用戶名和密碼。
- Shiro的
Subject將認(rèn)證請(qǐng)求提交給SecurityManager。 SecurityManager將請(qǐng)求委托給AuthenticationStrategy(認(rèn)證策略)。AuthenticationStrategy可以進(jìn)行多個(gè)Realm認(rèn)證(即多重認(rèn)證)。- 每個(gè)Realm將認(rèn)證請(qǐng)求轉(zhuǎn)換成領(lǐng)域?qū)ο?,并傳遞給相應(yīng)的用戶數(shù)據(jù)源。
- 如果所有Realm的認(rèn)證通過,則認(rèn)證成功,否則失敗。
3.2.2 權(quán)限管理原理和實(shí)踐
權(quán)限管理是Shiro的核心功能之一,它負(fù)責(zé)控制用戶訪問資源的能力。Shiro使用 Roles (角色)和 Permissions (權(quán)限)概念來控制用戶訪問。
- Role-Based Access Control (RBAC) : 基于角色的訪問控制是Shiro中最常用的一種授權(quán)機(jī)制。
- Permission-Based Access Control : 基于權(quán)限的訪問控制允許你直接控制用戶對(duì)特定操作的權(quán)限,這比RBAC更靈活。
Shiro中的權(quán)限表示可以非常細(xì)粒度,例如:
String permission = "user:create"; // 創(chuàng)建用戶的權(quán)限
通過在應(yīng)用程序中設(shè)置相應(yīng)的角色和權(quán)限,Shiro的授權(quán)機(jī)制能夠有效地管理用戶訪問。
3.3 Shiro在企業(yè)級(jí)應(yīng)用中的實(shí)踐
3.3.1 Shiro與Spring Boot的整合
在企業(yè)級(jí)應(yīng)用中,Shiro與Spring Boot的整合是常見的需求。以下為整合步驟的簡(jiǎn)介:
添加依賴 : 在pom.xml文件中添加Spring Boot和Shiro的依賴。
xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.3</version> </dependency>
創(chuàng)建配置類 : 創(chuàng)建一個(gè)配置類來配置Shiro的Bean。
java @Configuration public class ShiroConfig { @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 配置Realms等... return securityManager; } // 其他Bean配置... }創(chuàng)建Realm : 自定義一個(gè)Realm來連接你的用戶數(shù)據(jù)源。
java @Service public class MyRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 授權(quán)邏輯... } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 認(rèn)證邏輯... } }
3.3.2 Shiro在微服務(wù)架構(gòu)中的應(yīng)用案例
Shiro可以與Spring Cloud微服務(wù)架構(gòu)很好地協(xié)同工作。以下是在微服務(wù)架構(gòu)中應(yīng)用Shiro的一個(gè)案例:
- 服務(wù)網(wǎng)關(guān)集成 : 在服務(wù)網(wǎng)關(guān)層集成Shiro,統(tǒng)一進(jìn)行安全認(rèn)證和授權(quán),可以使用Zuul或Spring Cloud Gateway。
- 服務(wù)間通信 : 在服務(wù)間通信時(shí),通過Shiro進(jìn)行調(diào)用者身份的認(rèn)證。
- 分布式會(huì)話管理 : Shiro支持分布式會(huì)話,可以通過Redis等存儲(chǔ)機(jī)制實(shí)現(xiàn)微服務(wù)間的會(huì)話共享。
通過這些實(shí)踐,Shiro能夠支持從單體應(yīng)用到分布式微服務(wù)架構(gòu)中的企業(yè)級(jí)應(yīng)用需求。在下一章中,我們將詳細(xì)探討如何配置依賴、如何使用JWT工具類以及如何設(shè)置過濾器鏈和異常處理策略。
4. 整合步驟詳述
4.1 配置依賴和Shiro配置
4.1.1 添加Spring Boot和Shiro的依賴
在Spring Boot項(xiàng)目中整合Shiro和JWT認(rèn)證機(jī)制,首先需要在 pom.xml 文件中添加相關(guān)的依賴。對(duì)于Spring Boot項(xiàng)目,通常還需要添加Spring Boot Starter的依賴,以方便項(xiàng)目的構(gòu)建和管理。以下是添加Shiro和JWT相關(guān)依賴的示例代碼:
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
確保所添加的Shiro版本與Spring Boot版本兼容,并根據(jù)項(xiàng)目需要選擇合適的JWT庫(kù)版本。
4.1.2 Shiro的配置文件詳解
在 application.properties 或 application.yml 配置文件中,可以進(jìn)行Shiro的配置。下面是一個(gè)簡(jiǎn)單的配置示例:
# Shiro 配置 shiro.ini=classpath:shiro.ini
對(duì)于更復(fù)雜的配置,我們可能需要定義 shiro.ini 文件或使用Spring配置類來配置Shiro。例如,在 shiro.ini 中定義安全策略和認(rèn)證信息:
[main] # 配置自定義的Realm myRealm=com.example.MyRealm # 設(shè)置SecurityManager的Realm securityManager.realms=$myRealm # 配置會(huì)話管理器 sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager securityManager.sessionManager=$sessionManager # JWT認(rèn)證過濾器配置 authc=org.apache.shiro.web.filter.authc.FormAuthenticationFilter jwtAuthc=org.example.MyJWTFilter # 將自定義的JWT認(rèn)證過濾器加入Shiro過濾器鏈 [urls] # 登錄請(qǐng)求不攔截 /login=authc # 其他請(qǐng)求需要通過JWT認(rèn)證 /**=jwtAuthc
對(duì)于安全性要求更高的場(chǎng)景,可以使用Java配置類來替代 shiro.ini 文件:
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// 自定義會(huì)話ID生成器
sessionManager.setSessionIdGenerator(sessionIdGenerator());
return sessionManager;
}
@Bean
public MyJWTFilter jwtFilter() {
return new MyJWTFilter();
}
在配置過程中,要注意權(quán)限管理和認(rèn)證策略的匹配,以及會(huì)話管理的合理設(shè)置。對(duì)JWT的處理則更多依賴于自定義的過濾器來實(shí)現(xiàn)。
4.2 JWT工具類實(shí)現(xiàn)
4.2.1 JWT的生成和解析
JWT的生成和解析是實(shí)現(xiàn)JWT認(rèn)證機(jī)制的核心步驟。我們需要?jiǎng)?chuàng)建一個(gè)工具類來處理這些操作。以下是一個(gè)簡(jiǎn)單的JWT工具類實(shí)現(xiàn)示例:
public class JWTUtil {
private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
private static final String ISSUE_ON_KEY = "issueOn";
private static final String EXP_KEY = "exp";
public static String createJWT(String subject, String issuer, String secret, long ttlMillis) {
// 獲取當(dāng)前時(shí)間
Date now = new Date();
// 獲取過期時(shí)間
Date expTime = new Date(now.getTime() + ttlMillis);
// 獲取JWT ID
String jwtId = IdUtils.simpleUUID();
// 創(chuàng)建JWT
JwtBuilder builder = Jwts.builder()
.setId(jwtId)
.setIssuedAt(now)
.setSubject(subject)
.setIssuer(issuer)
.signWith(SIGNATURE_ALGORITHM, secret);
// 添加過期時(shí)間
if (expTime != null) {
builder.setExpiration(expTime);
}
return builder.compact();
}
public static Claims parseJWT(String jwt, String secret) {
return Jwts.parser()
.setSigningKey(secret.getBytes())
.parseClaimsJws(jwt)
.getBody();
}
}
在這個(gè)工具類中, createJWT 方法用于生成JWT, parseJWT 方法用于解析JWT。 SIGNATURE_ALGORITHM 定義了簽名算法,確保了JWT的安全性。在實(shí)際使用中,應(yīng)根據(jù)實(shí)際業(yè)務(wù)需求對(duì)JWT的內(nèi)容和過期時(shí)間進(jìn)行定制。
4.2.2 JWT工具類的封裝
為了提高工具類的可用性和安全性,我們還需要對(duì)其進(jìn)行適當(dāng)?shù)姆庋b,使其易于在應(yīng)用程序中使用。以下是一個(gè)封裝示例:
public class JWTUtil {
// ... JWT的生成和解析方法
// 獲取當(dāng)前用戶的方法
public static String getCurrentUser(String jwt, String secret) {
Claims claims = parseJWT(jwt, secret);
return claims.getSubject();
}
}
在這里,我們添加了 getCurrentUser 方法,該方法通過解析JWT獲取當(dāng)前用戶信息。這樣在業(yè)務(wù)邏輯中,我們就可以通過JWT直接獲取當(dāng)前登錄用戶的信息,而無需每次都解析JWT。
4.3 過濾器鏈設(shè)置和異常處理
4.3.1 Shiro的Filter鏈配置
Shiro的過濾器鏈(Filter Chain)允許我們以聲明式的方式配置過濾器,攔截特定請(qǐng)求并進(jìn)行相應(yīng)的處理。通過配置Shiro的 [urls] 部分,可以設(shè)置哪些請(qǐng)求需要進(jìn)行認(rèn)證或授權(quán)。這里是一個(gè)配置示例:
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "authc"); // 登錄請(qǐng)求,不進(jìn)行JWT認(rèn)證
filterChainDefinitionMap.put("/**", "jwtAuthc"); // 其他請(qǐng)求進(jìn)行JWT認(rèn)證
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
配置完成后,所有進(jìn)入 /** 的請(qǐng)求都需要通過名為 jwtAuthc 的過濾器進(jìn)行處理,此過濾器為自定義的JWT認(rèn)證過濾器。
4.3.2 常見異常處理策略
在使用Shiro進(jìn)行認(rèn)證和授權(quán)時(shí),可能會(huì)遇到各種異常情況,比如無效的認(rèn)證信息、未授權(quán)訪問等。Shiro提供了默認(rèn)的異常處理器 DefaultWebSecurityManager ,但通常我們會(huì)自定義異常處理策略來提供更好的用戶體驗(yàn)。以下是一個(gè)異常處理器的示例:
public class MyExceptionHandler implements ExceptionMapper<Exception> {
@Override
public String toHTML(Exception e) {
// 根據(jù)異常類型返回不同的錯(cuò)誤信息
if (e instanceof AuthorizationException) {
return "<p>您沒有權(quán)限訪問此資源。</p>";
} else if (e instanceof AuthenticationException) {
return "<p>認(rèn)證失敗,請(qǐng)重新登錄。</p>";
} else {
return "<p>服務(wù)器異常,請(qǐng)稍后再試。</p>";
}
}
}
通過實(shí)現(xiàn) ExceptionMapper 接口,我們可以在自定義的JWT認(rèn)證過濾器中使用這個(gè)異常處理器來返回給用戶更加友好的錯(cuò)誤提示。
4.4 權(quán)限控制的實(shí)現(xiàn)細(xì)節(jié)
4.4.1 自定義權(quán)限驗(yàn)證
在實(shí)際項(xiàng)目中,通常需要根據(jù)不同的業(yè)務(wù)需求實(shí)現(xiàn)自定義的權(quán)限驗(yàn)證邏輯。在Shiro中,這通常是通過實(shí)現(xiàn) Authorizer 接口或使用 Realm 來完成的。以下是一個(gè)簡(jiǎn)單的 Realm 實(shí)現(xiàn)示例:
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 從principals中獲取當(dāng)前用戶的principal信息
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 根據(jù)用戶名查詢用戶權(quán)限信息,然后添加到authorizationInfo中
List<String> permissions = getPermissionsByUsername(username);
authorizationInfo.addStringPermissions(permissions);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 這里實(shí)現(xiàn)用戶的登錄驗(yàn)證邏輯,通常會(huì)根據(jù)token信息查詢數(shù)據(jù)庫(kù)驗(yàn)證用戶名和密碼
// 如果驗(yàn)證通過,返回一個(gè)SimpleAuthenticationInfo對(duì)象
// 如果驗(yàn)證失敗,可以拋出相應(yīng)的AuthenticationException異常
}
}
在 doGetAuthorizationInfo 方法中,我們根據(jù)用戶的身份信息(通常是用戶名)來查詢用戶所擁有的權(quán)限,并將其封裝到 AuthorizationInfo 對(duì)象中。然后,Shiro會(huì)根據(jù)這些權(quán)限信息進(jìn)行授權(quán)。
4.4.2 權(quán)限控制與用戶界面的結(jié)合
實(shí)現(xiàn)權(quán)限控制后,還需要在用戶界面中正確地展示或限制用戶權(quán)限。這通常在控制器層或者服務(wù)層通過調(diào)用Shiro的API來完成。以下是一個(gè)簡(jiǎn)單的權(quán)限控制示例:
@Controller
public class MyController {
@RequestMapping("/admin")
public String adminPage(Principal principal) {
// 只有擁有admin權(quán)限的用戶才能訪問此頁(yè)面
if (SecurityUtils.getSubject().isPermitted("admin")) {
return "admin";
} else {
return "accessDenied";
}
}
}
在這個(gè)示例中,只有當(dāng)用戶擁有 admin 權(quán)限時(shí),才能訪問 admin 頁(yè)面,否則會(huì)跳轉(zhuǎn)到 accessDenied 頁(yè)面。通過調(diào)用 SecurityUtils.getSubject().isPermitted() 方法,我們可以檢查用戶是否擁有特定的權(quán)限。
以上內(nèi)容為第四章整合步驟的詳述,從配置Shiro和JWT,到權(quán)限控制和用戶界面的結(jié)合,為讀者提供了一個(gè)整合這些技術(shù)點(diǎn)的細(xì)致指南。在下一章節(jié),我們將通過實(shí)際項(xiàng)目的代碼演示整合過程,使讀者更易理解和應(yīng)用這些技術(shù)。
5. 通過實(shí)際項(xiàng)目代碼演示整合過程
5.1 項(xiàng)目結(jié)構(gòu)和代碼概覽
5.1.1 項(xiàng)目整體結(jié)構(gòu)布局
在進(jìn)行實(shí)際項(xiàng)目整合的演示之前,我們首先需要了解項(xiàng)目的基本結(jié)構(gòu)。假設(shè)我們已經(jīng)有了一個(gè)基于Spring Boot和Maven的項(xiàng)目結(jié)構(gòu),如下所示:
. ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── myproject │ │ │ ├── MyProjectApplication.java │ │ │ ├── config │ │ │ │ ├── SecurityConfig.java │ │ │ │ └── ShiroConfig.java │ │ │ ├── controller │ │ │ │ ├── UserController.java │ │ │ │ └── ResourceController.java │ │ │ ├── service │ │ │ │ ├── UserService.java │ │ │ │ └── ResourceService.java │ │ │ ├── model │ │ │ │ ├── User.java │ │ │ │ └── Resource.java │ │ │ └── repository │ │ │ ├── UserRepository.java │ │ │ └── ResourceRepository.java │ │ └── resources │ │ ├── application.properties │ │ └── static │ └── test │ └── java │ └── com │ └── example │ └── myproject │ └── MyProjectApplicationTests.java └── pom.xml
5.1.2 關(guān)鍵代碼片段解讀
在 ShiroConfig.java 中,我們將配置Shiro的默認(rèn)安全策略,包括設(shè)置自定義的Realm和啟用注解支持:
@Configuration
public class ShiroConfig {
@Bean
public SecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
@Bean
public Realm realm() {
return new CustomRealm();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/login");
shiroFilter.setUnauthorizedUrl("/403");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilter;
}
// Other beans and configurations...
}
5.2 功能模塊的實(shí)現(xiàn)
5.2.1 用戶登錄模塊
在 UserController.java 中,我們將編寫處理用戶登錄請(qǐng)求的邏輯:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
// Authenticate user
User user = userService.authenticateUser(loginRequest.getUsername(), loginRequest.getPassword());
if (user != null) {
// Create JWT token and send it back to the user
String token = JwtUtil.createToken(user);
return ResponseEntity.ok(new JwtResponse(token));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
}
}
5.2.2 資源訪問控制模塊
在 ResourceController.java 中,我們將演示如何使用Shiro注解來控制資源訪問權(quán)限:
@RestController
@RequestMapping("/resources")
public class ResourceController {
@GetMapping("/{id}")
@RequiresPermissions("resource:view")
public ResponseEntity<?> viewResource(@PathVariable Long id) {
// Fetch the resource and return it
Resource resource = resourceService.findById(id);
return ResponseEntity.ok(resource);
}
@PostMapping("/create")
@RequiresRoles("admin")
public ResponseEntity<?> createResource(@RequestBody Resource resource) {
// Create a new resource
Resource createdResource = resourceService.createResource(resource);
return ResponseEntity.ok(createdResource);
}
}
5.3 測(cè)試和調(diào)試
5.3.1 單元測(cè)試的編寫與執(zhí)行
為了確保我們的整合工作正常運(yùn)行,編寫單元測(cè)試是至關(guān)重要的。在 MyProjectApplicationTests.java 中,我們可以使用Mockito來模擬服務(wù)層的行為,確保我們的控制器層邏輯正確:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyProjectApplicationTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testLoginSuccess() throws Exception {
User mockUser = new User(...);
when(userService.authenticateUser(anyString(), anyString())).thenReturn(mockUser);
mockMvc.perform(post("/user/login")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"username\":\"user\", \"password\":\"pass\"}"))
.andExpect(status().isOk());
}
}
5.3.2 調(diào)試過程中遇到的問題及解決方案
在測(cè)試過程中,我們可能遇到了 JwtUtil.createToken 方法無法調(diào)用的情況,原因可能是依賴注入沒有正確配置。在 JwtUtil 類上添加 @Component 注解,并確保JWT相關(guān)的配置類已經(jīng)注冊(cè)到Spring容器中,可以解決這個(gè)問題。
5.4 安全性和性能優(yōu)化
5.4.1 常見安全問題排查與加固
為了加固安全性,我們需要排查和解決以下問題:
- 確保敏感信息如密鑰不會(huì)暴露在日志或配置文件中。
- 使用HTTPS來保護(hù)數(shù)據(jù)傳輸過程中的安全。
- 對(duì)輸入數(shù)據(jù)進(jìn)行驗(yàn)證,防止SQL注入或跨站腳本攻擊(XSS)。
- 對(duì)JWT進(jìn)行過期時(shí)間設(shè)置和刷新機(jī)制,以減少被盜用的風(fēng)險(xiǎn)。
5.4.2 性能優(yōu)化策略及實(shí)施
對(duì)于性能優(yōu)化,我們可以采取以下策略:
- 使用Redis或其他緩存機(jī)制來存儲(chǔ)JWT的黑名單,減少數(shù)據(jù)庫(kù)的訪問頻率。
- 對(duì)數(shù)據(jù)庫(kù)查詢進(jìn)行優(yōu)化,使用合適的索引和查詢條件。
- 通過Shiro的會(huì)話管理,合理設(shè)置會(huì)話的生命周期,減少會(huì)話的存儲(chǔ)開銷。
- 如果系統(tǒng)訪問量較大,考慮引入Shiro的集群支持和會(huì)話復(fù)制功能。
通過這些策略,我們能夠確保我們的應(yīng)用在安全性得到保障的同時(shí),也能夠應(yīng)對(duì)高并發(fā)場(chǎng)景的需求。
總結(jié)
到此這篇關(guān)于Spring Boot與JWT和Shiro整合實(shí)踐的文章就介紹到這了,更多相關(guān)SpringBoot與JWT和Shiro整合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 程序設(shè)計(jì)總復(fù)習(xí)題(java基礎(chǔ)代碼)
這篇文章主要介紹了Java 程序設(shè)計(jì)總復(fù)習(xí)題,主要是java基礎(chǔ)代碼,方便學(xué)習(xí)java的同學(xué)2021-05-05
淺析SpringBoot自動(dòng)化配置原理實(shí)現(xiàn)
這篇文章主要介紹了淺析SpringBoot自動(dòng)化配置原理實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
java驗(yàn)證用戶是否已經(jīng)登錄 java實(shí)現(xiàn)自動(dòng)登錄
這篇文章主要介紹了java驗(yàn)證用戶是否已經(jīng)登錄,java實(shí)現(xiàn)自動(dòng)登錄,感興趣的小伙伴們可以參考一下2016-04-04
在eclipse導(dǎo)入Java的jar包的方法JDBC(圖文說明)
這篇文章主要介紹了在eclipse導(dǎo)入Java 的jar包的方法 JDBC 圖文說明 ,需要的朋友可以參考下2015-09-09
spring項(xiàng)目實(shí)現(xiàn)國(guó)際化流程解析
SpringBoot實(shí)現(xiàn)國(guó)際化(i18n)的步驟包括創(chuàng)建國(guó)際化資源文件、配置application.yml、自定義LocaleResolver和LocaleChangeInterceptor、在代碼中使用MessageSource獲取國(guó)際化消息,本文介紹spring項(xiàng)目實(shí)現(xiàn)國(guó)際化流程,感興趣的朋友一起看看吧2026-01-01
利用Spring Data MongoDB持久化文檔數(shù)據(jù)的方法教程
這篇文章主要給大家介紹了關(guān)于利用Spring Data MongoDB持久化文檔數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考借鑒,下面來一起看看吧。2017-08-08
劍指Offer之Java算法習(xí)題精講數(shù)組與字符和等差數(shù)列
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03
SpringSecurity導(dǎo)致Redis壓力大問題的解決方案
在我們現(xiàn)有的系統(tǒng)中,用于登錄的?Redis?服務(wù)器?CPU?占用率長(zhǎng)期處于?90%?的高位,這一狀況帶來了極大的風(fēng)險(xiǎn)隱患,因此,本文旨在解決這個(gè)問題,下面小編給大家詳細(xì)介紹SpringSecurity導(dǎo)致Redis壓力大問題的解決方案,需要的朋友可以參考下2025-06-06

