Spring?Boot與JWT和Shiro整合實(shí)踐步驟(非常詳細(xì)!)
1. Spring Boot基礎(chǔ)介紹
在現(xiàn)代企業(yè)級應(yīng)用開發(fā)中,Spring Boot憑借其輕量級、快速開發(fā)的特性,已經(jīng)成為構(gòu)建微服務(wù)架構(gòu)項(xiàng)目的首選框架。本章將為讀者提供Spring Boot的入門級介紹,旨在幫助讀者快速掌握Spring Boot的基礎(chǔ)知識,并了解其在項(xiàng)目中的具體應(yīng)用。
1.1 Spring Boot簡介
Spring Boot是Spring的一個模塊,它提供了快速構(gòu)建項(xiàng)目的能力。通過“約定優(yōu)于配置”的原則,Spring Boot旨在簡化Spring應(yīng)用的初始搭建以及開發(fā)過程。它通過提供默認(rèn)配置,幫助開發(fā)者避免繁瑣的配置工作,從而專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。
1.2 Spring Boot的核心特性
- 自動配置 :Spring Boot能夠自動配置Spring應(yīng)用,只有在特定情況下才需要手動配置。
- 起步依賴 :通過提供特定的“starter”依賴,簡化了構(gòu)建配置。
- 內(nèi)嵌Web服務(wù)器 :如Tomcat、Jetty或Undertow,無需部署WAR文件。
- 監(jiān)控和管理 :提供了一套生產(chǎn)就緒特性,比如指標(biāo)、健康檢查、外部化配置等。
1.3 入門示例
一個典型的Spring Boot應(yīng)用的入口是一個帶有 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>
以上是一個簡單的Spring Boot應(yīng)用的搭建過程,接下來的內(nèi)容將深入探討如何將Spring Boot與其他技術(shù)棧整合,以實(shí)現(xiàn)更為復(fù)雜的應(yī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對象的形式安全傳遞聲明(claims)。JWT通常用于身份驗(yàn)證和信息交換,特別適用于Web API的認(rèn)證場景。
在Web應(yīng)用中,一旦用戶登錄成功,服務(wù)器會生成一個JWT并返回給客戶端,之后客戶端將該JWT存儲在客戶端的瀏覽器或移動設(shè)備中。客戶端每次向服務(wù)器發(fā)送請求時,都需要在HTTP請求的頭信息中附帶該JWT。服務(wù)器通過解析JWT中的信息,就可以驗(yàn)證用戶的身份和訪問權(quán)限。
2.1.2 JWT的結(jié)構(gòu)和特點(diǎn)
JWT由三部分組成:Header(頭部)、Payload(載荷)和Signature(簽名)。這三部分通過點(diǎn)(.)連接在一起,形成一個完整的JWT字符串。
- Header(頭部) :頭部用于描述關(guān)于該JWT的最基本的信息,例如其類型(即JWT),以及所使用的簽名算法(如HMAC SHA256或者RSA)。
{ "alg": "HS256", "typ": "JWT" }
- Payload(載荷) :載荷就是存放有效信息的地方。這些信息包括但不限于發(fā)行者、過期時間、主題等,可以存放一些聲明(claims)。聲明是關(guān)于實(shí)體(通常是用戶)和其他數(shù)據(jù)的聲明,比如用戶ID、用戶名等。
{ "sub": "1234567890", "name": "John Doe", "admin": true }
- Signature(簽名) :為了防止信息篡改,對頭部以及載荷的內(nèi)容進(jìn)行簽名。簽名的方法取決于頭部中指定的算法。使用Header中指定的算法對Header和Payload進(jìn)行加密,生成簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
2.2 JWT的工作原理和優(yōu)勢
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)建一個JWT,將用戶的身份信息、權(quán)限信息和其他聲明打包到Payload中。
- 應(yīng)用系統(tǒng)對JWT的Header和Payload部分使用Base64Url編碼,并使用密鑰對它們進(jìn)行簽名。
- 將生成的JWT返回給用戶,用戶將JWT存儲在本地(例如瀏覽器的localStorage或sessionStorage中)。
- 用戶在之后的請求中,將JWT作為Authorization header附帶在每個HTTP請求中。
- 服務(wù)器收到請求后,提取JWT并驗(yàn)證其簽名,確認(rèn)其有效性和未被篡改。
- 如果JWT驗(yàn)證成功,服務(wù)器根據(jù)其中的聲明信息進(jìn)行用戶身份的識別,并授權(quán)訪問資源。
2.2.2 JWT相較于傳統(tǒng)認(rèn)證的優(yōu)勢
JWT相較于傳統(tǒng)的Session認(rèn)證機(jī)制,具有以下優(yōu)勢:
- 無狀態(tài) :JWT不依賴服務(wù)器存儲認(rèn)證信息,減輕了服務(wù)器壓力。
- 跨域性 :JWT是純文本,因此可以跨不同的服務(wù)器使用,非常適合分布式系統(tǒng)。
- 標(biāo)準(zhǔn)化 :遵循開放標(biāo)準(zhǔn),可以跨不同平臺使用,方便前后端分離的項(xiàng)目。
- 緊湊性 :由于JWT的緊湊性,它可以作為URL的一部分傳輸。
2.3 JWT的安全性分析
2.3.1 JWT的加密和簽名機(jī)制
JWT通過Header和Payload的Base64Url編碼后與簽名組合而成。簽名是為了確保JWT沒有被篡改,其安全性依賴于密鑰的保密性。
- 加密 :通常用于JWT的加密是為了保護(hù)數(shù)據(jù)的隱私性,避免載荷信息泄露。
- 簽名 :使用密鑰對Header和Payload進(jìn)行簽名,可以確保數(shù)據(jù)的完整性和來源的可驗(yàn)證性。
2.3.2 JWT的安全漏洞及防范措施
JWT雖然方便,但也存在一些潛在的安全風(fēng)險:
- 泄露風(fēng)險 :如果JWT存儲在客戶端是暴露的狀態(tài)下,比如localStorage,它可能被惡意JavaScript訪問。可以通過HttpOnly的Cookie來存儲JWT,以減少泄露風(fēng)險。
- 過期時間設(shè)置 :為JWT設(shè)置較短的過期時間可以減少令牌被盜用的風(fēng)險。
- 加密和簽名算法 :使用強(qiáng)加密和簽名算法可以增強(qiáng)JWT的安全性。避免使用已被破解的算法,如HS256如果密鑰泄露的話。使用RSA、ECDSA等非對稱加密算法可以提高安全性。
接下來,我們深入探討JWT的加密和簽名機(jī)制以及如何實(shí)現(xiàn)和優(yōu)化這些機(jī)制,確保系統(tǒng)的安全穩(wěn)定運(yùn)行。
3. Shiro安全框架功能概覽
Shiro是Apache軟件基金會的一個開源安全框架,為應(yīng)用程序提供認(rèn)證、授權(quán)、加密和會話管理功能。本章節(jié)將深入探討Shiro的核心架構(gòu)和組件,以及其在企業(yè)級應(yīng)用中的實(shí)踐。
3.1 Shiro核心架構(gòu)和組件
3.1.1 Shiro的架構(gòu)組成
Shiro的架構(gòu)設(shè)計(jì)簡潔而靈活,由三個主要部分組成:Subject、SecurityManager和Realms。
- Subject : 代表當(dāng)前的用戶,它與當(dāng)前用戶進(jìn)行交互。在應(yīng)用代碼中,Subject通常是指當(dāng)前正在執(zhí)行操作的用戶,或者代表當(dāng)前用戶的線程。
- SecurityManager : 是Shiro框架的心臟,負(fù)責(zé)處理所有Subject的請求。它管理所有的Subject和其它內(nèi)部安全組件。
- Realms : 連接外部數(shù)據(jù)源,比如應(yīng)用的數(shù)據(jù)庫。Shiro通過Realms獲取安全相關(guān)的數(shù)據(jù),如用戶角色、權(quán)限等。
3.1.2 主要組件功能解析
Shiro框架還包含其他組件,如 Authentication
, Authorization
, Session Management
, Cryptography
等。這里我們將對每個組件的功能進(jìn)行分析。
- Authentication (認(rèn)證) : 驗(yàn)證用戶身份的過程。通常通過用戶名和密碼完成。
- Authorization (授權(quán)) : 確定一個用戶是否有操作某個資源的權(quán)限。
- Session Management (會話管理) : Shiro提供了完備的會話管理功能,包括創(chuàng)建會話、銷毀會話、會話存儲、并發(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會對這些信息進(jìn)行驗(yàn)證。下面是Shiro的認(rèn)證流程:
- 用戶提交用戶名和密碼。
- Shiro的
Subject
將認(rèn)證請求提交給SecurityManager
。 SecurityManager
將請求委托給AuthenticationStrategy
(認(rèn)證策略)。AuthenticationStrategy
可以進(jìn)行多個Realm認(rèn)證(即多重認(rèn)證)。- 每個Realm將認(rèn)證請求轉(zhuǎn)換成領(lǐng)域?qū)ο螅鬟f給相應(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)限的訪問控制允許你直接控制用戶對特定操作的權(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è)級應(yīng)用中的實(shí)踐
3.3.1 Shiro與Spring Boot的整合
在企業(yè)級應(yīng)用中,Shiro與Spring Boot的整合是常見的需求。以下為整合步驟的簡介:
添加依賴 : 在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)建一個配置類來配置Shiro的Bean。
java @Configuration public class ShiroConfig { @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 配置Realms等... return securityManager; } // 其他Bean配置... }
創(chuàng)建Realm : 自定義一個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的一個案例:
- 服務(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ù)間通信時,通過Shiro進(jìn)行調(diào)用者身份的認(rèn)證。
- 分布式會話管理 : Shiro支持分布式會話,可以通過Redis等存儲機(jī)制實(shí)現(xiàn)微服務(wù)間的會話共享。
通過這些實(shí)踐,Shiro能夠支持從單體應(yīng)用到分布式微服務(wù)架構(gòu)中的企業(yè)級應(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)的依賴。對于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庫版本。
4.1.2 Shiro的配置文件詳解
在 application.properties
或 application.yml
配置文件中,可以進(jìn)行Shiro的配置。下面是一個簡單的配置示例:
# Shiro 配置 shiro.ini=classpath:shiro.ini
對于更復(fù)雜的配置,我們可能需要定義 shiro.ini
文件或使用Spring配置類來配置Shiro。例如,在 shiro.ini
中定義安全策略和認(rèn)證信息:
[main] # 配置自定義的Realm myRealm=com.example.MyRealm # 設(shè)置SecurityManager的Realm securityManager.realms=$myRealm # 配置會話管理器 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] # 登錄請求不攔截 /login=authc # 其他請求需要通過JWT認(rèn)證 /**=jwtAuthc
對于安全性要求更高的場景,可以使用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(); // 自定義會話ID生成器 sessionManager.setSessionIdGenerator(sessionIdGenerator()); return sessionManager; } @Bean public MyJWTFilter jwtFilter() { return new MyJWTFilter(); }
在配置過程中,要注意權(quán)限管理和認(rèn)證策略的匹配,以及會話管理的合理設(shè)置。對JWT的處理則更多依賴于自定義的過濾器來實(shí)現(xiàn)。
4.2 JWT工具類實(shí)現(xiàn)
4.2.1 JWT的生成和解析
JWT的生成和解析是實(shí)現(xiàn)JWT認(rèn)證機(jī)制的核心步驟。我們需要創(chuàng)建一個工具類來處理這些操作。以下是一個簡單的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)前時間 Date now = new Date(); // 獲取過期時間 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); // 添加過期時間 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(); } }
在這個工具類中, createJWT
方法用于生成JWT, parseJWT
方法用于解析JWT。 SIGNATURE_ALGORITHM
定義了簽名算法,確保了JWT的安全性。在實(shí)際使用中,應(yīng)根據(jù)實(shí)際業(yè)務(wù)需求對JWT的內(nèi)容和過期時間進(jìn)行定制。
4.2.2 JWT工具類的封裝
為了提高工具類的可用性和安全性,我們還需要對其進(jìn)行適當(dāng)?shù)姆庋b,使其易于在應(yīng)用程序中使用。以下是一個封裝示例:
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)允許我們以聲明式的方式配置過濾器,攔截特定請求并進(jìn)行相應(yīng)的處理。通過配置Shiro的 [urls]
部分,可以設(shè)置哪些請求需要進(jìn)行認(rèn)證或授權(quán)。這里是一個配置示例:
@Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "authc"); // 登錄請求,不進(jìn)行JWT認(rèn)證 filterChainDefinitionMap.put("/**", "jwtAuthc"); // 其他請求進(jìn)行JWT認(rèn)證 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
配置完成后,所有進(jìn)入 /**
的請求都需要通過名為 jwtAuthc
的過濾器進(jìn)行處理,此過濾器為自定義的JWT認(rèn)證過濾器。
4.3.2 常見異常處理策略
在使用Shiro進(jìn)行認(rèn)證和授權(quán)時,可能會遇到各種異常情況,比如無效的認(rèn)證信息、未授權(quán)訪問等。Shiro提供了默認(rèn)的異常處理器 DefaultWebSecurityManager
,但通常我們會自定義異常處理策略來提供更好的用戶體驗(yàn)。以下是一個異常處理器的示例:
public class MyExceptionHandler implements ExceptionMapper<Exception> { @Override public String toHTML(Exception e) { // 根據(jù)異常類型返回不同的錯誤信息 if (e instanceof AuthorizationException) { return "<p>您沒有權(quán)限訪問此資源。</p>"; } else if (e instanceof AuthenticationException) { return "<p>認(rèn)證失敗,請重新登錄。</p>"; } else { return "<p>服務(wù)器異常,請稍后再試。</p>"; } } }
通過實(shí)現(xiàn) ExceptionMapper
接口,我們可以在自定義的JWT認(rèn)證過濾器中使用這個異常處理器來返回給用戶更加友好的錯誤提示。
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
來完成的。以下是一個簡單的 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)證邏輯,通常會根據(jù)token信息查詢數(shù)據(jù)庫驗(yàn)證用戶名和密碼 // 如果驗(yàn)證通過,返回一個SimpleAuthenticationInfo對象 // 如果驗(yàn)證失敗,可以拋出相應(yīng)的AuthenticationException異常 } }
在 doGetAuthorizationInfo
方法中,我們根據(jù)用戶的身份信息(通常是用戶名)來查詢用戶所擁有的權(quán)限,并將其封裝到 AuthorizationInfo
對象中。然后,Shiro會根據(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來完成。以下是一個簡單的權(quán)限控制示例:
@Controller public class MyController { @RequestMapping("/admin") public String adminPage(Principal principal) { // 只有擁有admin權(quán)限的用戶才能訪問此頁面 if (SecurityUtils.getSubject().isPermitted("admin")) { return "admin"; } else { return "accessDenied"; } } }
在這個示例中,只有當(dāng)用戶擁有 admin
權(quán)限時,才能訪問 admin
頁面,否則會跳轉(zhuǎn)到 accessDenied
頁面。通過調(diào)用 SecurityUtils.getSubject().isPermitted()
方法,我們可以檢查用戶是否擁有特定的權(quán)限。
以上內(nèi)容為第四章整合步驟的詳述,從配置Shiro和JWT,到權(quán)限控制和用戶界面的結(jié)合,為讀者提供了一個整合這些技術(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)有了一個基于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
中,我們將編寫處理用戶登錄請求的邏輯:
@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 測試和調(diào)試
5.3.1 單元測試的編寫與執(zhí)行
為了確保我們的整合工作正常運(yùn)行,編寫單元測試是至關(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)試過程中遇到的問題及解決方案
在測試過程中,我們可能遇到了 JwtUtil.createToken
方法無法調(diào)用的情況,原因可能是依賴注入沒有正確配置。在 JwtUtil
類上添加 @Component
注解,并確保JWT相關(guān)的配置類已經(jīng)注冊到Spring容器中,可以解決這個問題。
5.4 安全性和性能優(yōu)化
5.4.1 常見安全問題排查與加固
為了加固安全性,我們需要排查和解決以下問題:
- 確保敏感信息如密鑰不會暴露在日志或配置文件中。
- 使用HTTPS來保護(hù)數(shù)據(jù)傳輸過程中的安全。
- 對輸入數(shù)據(jù)進(jìn)行驗(yàn)證,防止SQL注入或跨站腳本攻擊(XSS)。
- 對JWT進(jìn)行過期時間設(shè)置和刷新機(jī)制,以減少被盜用的風(fēng)險。
5.4.2 性能優(yōu)化策略及實(shí)施
對于性能優(yōu)化,我們可以采取以下策略:
- 使用Redis或其他緩存機(jī)制來存儲JWT的黑名單,減少數(shù)據(jù)庫的訪問頻率。
- 對數(shù)據(jù)庫查詢進(jìn)行優(yōu)化,使用合適的索引和查詢條件。
- 通過Shiro的會話管理,合理設(shè)置會話的生命周期,減少會話的存儲開銷。
- 如果系統(tǒng)訪問量較大,考慮引入Shiro的集群支持和會話復(fù)制功能。
通過這些策略,我們能夠確保我們的應(yīng)用在安全性得到保障的同時,也能夠應(yīng)對高并發(fā)場景的需求。
總結(jié)
到此這篇關(guān)于Spring Boot與JWT和Shiro整合實(shí)踐的文章就介紹到這了,更多相關(guān)SpringBoot與JWT和Shiro整合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC獲取HTTP中元素的實(shí)現(xiàn)示例
本文主要介紹了SpringMVC獲取HTTP中的元素,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02Spring?AI借助全局參數(shù)實(shí)現(xiàn)智能數(shù)據(jù)庫操作與個性化待辦管理
這篇文章主要介紹了Spring?AI借助全局參數(shù)實(shí)現(xiàn)智能數(shù)據(jù)庫操作與個性化待辦管理,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11Springboot項(xiàng)目中kaptcha驗(yàn)證碼的使用方式
這篇文章主要介紹了Springboot項(xiàng)目中kaptcha驗(yàn)證碼的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot application.yml和bootstrap.yml的區(qū)別
本文主要介紹了SpringBoot application.yml和bootstrap.yml的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04詳解Springboot如何通過注解實(shí)現(xiàn)接口防刷
本文主要為大家介紹一種極簡潔、靈活通用接口防刷實(shí)現(xiàn)方式、通過在需要防刷的方法加上@Prevent?注解即可實(shí)現(xiàn)短信防刷,感興趣的可以了解一下2022-09-09Mybatis-plus數(shù)據(jù)權(quán)限D(zhuǎn)ataPermissionInterceptor實(shí)現(xiàn)
本文主要介紹了Mybatis-plus數(shù)據(jù)權(quán)限D(zhuǎn)ataPermissionInterceptor實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Struts2返回json格式數(shù)據(jù)代碼實(shí)例
這篇文章主要介紹了Struts2返回json格式數(shù)據(jù)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04java線程池ExecutorService超時處理小結(jié)
使用ExecutorService時,設(shè)置子線程執(zhí)行超時是一個常見需求,本文就來詳細(xì)的介紹一下ExecutorService超時的三種方法,感興趣的可以了解一下2024-09-09