最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊

教程揭秘 | 動(dòng)力節(jié)點(diǎn)內(nèi)部Java零基礎(chǔ)教學(xué)文檔第十八篇:SpringSecurity

2023-11-27 14:29 作者:動(dòng)力節(jié)點(diǎn)  | 我要投稿

接上期后續(xù)

本期分享第十八章節(jié)

SpringSecurity

文檔馬上就分享完了,你們都跟上了嗎?

每天都在學(xué)習(xí)嘛?

有什么不會(huì)的嘛?

今日教學(xué)文檔分享來了?

今日新篇章

【SpringSecurity】

1.?認(rèn)證授權(quán)的基礎(chǔ)概念

1.1?什么是認(rèn)證(登錄)

進(jìn)入移動(dòng)互聯(lián)網(wǎng)時(shí)代,大家每天都在刷手機(jī),常用的軟件有微信、支付寶、頭條等,下邊拿微信來舉例子說明認(rèn)證相關(guān)的基本概念,在初次使用微信前需要注冊成為微信用戶,然后輸入賬號和密碼即可登錄微信,輸入賬號和密碼登錄微信的過程就是認(rèn)證

系統(tǒng)為什么要認(rèn)證?

http://127.0.0.1:8080/getAllUser

http://127.0.0.1:8080/addUser

http://127.0.0.1:8080/updateUser

http://127.0.0.1:8080/deleteUser

?

認(rèn)證是為了保護(hù)系統(tǒng)的隱私數(shù)據(jù)與資源,用戶的身份合法方可訪問該系統(tǒng)的資源。

認(rèn)證 :用戶認(rèn)證就是判斷一個(gè)用戶的身份是否合法的過程,用戶去訪問系統(tǒng)資源(url接口)時(shí)系統(tǒng)要求驗(yàn)證用戶的身份信息,身份合法方可繼續(xù)訪問,不合法則拒絕訪問。常見的用戶身份認(rèn)證方式有:用戶名密碼登錄,二維碼登錄,手機(jī)短信登錄,指紋認(rèn)證等方式。

1.2?什么是會(huì)話

HttpSession

SqlSession

用戶認(rèn)證通過后,為了避免用戶的每次操作都進(jìn)行認(rèn)證可將用戶的信息保證在會(huì)話中。會(huì)話就是系統(tǒng)為了保持當(dāng)前用戶的登錄狀態(tài)所提供的機(jī)制,常見的有基于session方式、基于token方式等。

1.2.1?基于session的認(rèn)證方式

它的交互流程是,用戶認(rèn)證成功后,在服務(wù)端生成用戶相關(guān)的數(shù)據(jù)保存在session(當(dāng)前會(huì)話)中,發(fā)給客戶端的sesssion_id 存放到 cookie 中,這樣用戶客戶端請求時(shí)帶上 session_id 就可以驗(yàn)證服務(wù)器端是否存在 session 數(shù)據(jù),以此完成用戶的合法校驗(yàn),當(dāng)用戶退出系統(tǒng)或session過期銷毀時(shí),客戶端的session_id也就無效了

1.2.2?基于token方式認(rèn)證方式

它的交互流程是,用戶認(rèn)證成功后,服務(wù)端生成一個(gè)token發(fā)給客戶端,客戶端可以放到 cookie 或 localStorage等存儲中,每次請求時(shí)帶上 token,服務(wù)端收到token通過驗(yàn)證后即可確認(rèn)用戶身份。Redis 存的用戶信息 ?共享session?(分布式中)

基于session的認(rèn)證方式由Servlet規(guī)范定制,服務(wù)端要存儲session信息需要占用內(nèi)存資源,客戶端需要支持cookie;基于token的方式則一般不需要服務(wù)端存儲token,并且不限制客戶端的存儲方式。如今移動(dòng)互聯(lián)網(wǎng)時(shí)代更多類型的客戶端需要接入系統(tǒng),系統(tǒng)多是采用前后端分離的架構(gòu)進(jìn)行實(shí)現(xiàn),所以基于token的方式更適合。

1.3?什么是授權(quán)?(給用戶頒發(fā)權(quán)限)

還拿微信來舉例子,微信登錄成功后用戶即可使用微信的功能,比如,發(fā)紅包、發(fā)朋友圈、添加好友等,沒有綁定銀行卡的用戶是無法發(fā)送紅包的,綁定銀行卡的用戶才可以發(fā)紅包,發(fā)紅包功能、發(fā)朋友圈功能都是微信的資源即功能資源,用戶擁有發(fā)紅包功能的權(quán)限才可以正常使用發(fā)送紅包功能,擁有發(fā)朋友圈功能的權(quán)限才可以使用發(fā)朋友圈功能,這個(gè)根據(jù)用戶的權(quán)限來控制用戶使用資源的過程就是授權(quán)。?鑒權(quán)(判斷用戶是否有這個(gè)權(quán)限)

java應(yīng)用中什么叫資源 url就是資源 ?(API接口就是資源)

http://127.0.0.1:8080/user/getUserById?id=10

1.3.1?為什么要授權(quán)?(控制資源(url)被訪問)

認(rèn)證是為了保證用戶身份的合法性,授權(quán)則是為了更細(xì)粒度的對隱私數(shù)據(jù)進(jìn)行劃分,授權(quán)是在認(rèn)證通過后發(fā)生的,控制不同的用戶能夠訪問不同的資源。

授權(quán):?授權(quán)是用戶認(rèn)證通過根據(jù)用戶的權(quán)限來控制用戶訪問資源的過程,擁有資源的訪問權(quán)限則正常訪問,沒有權(quán)限則拒絕訪問。

200

302

400

403

404

1.4?授權(quán)的數(shù)據(jù)模型(RBAC)

如何進(jìn)行授權(quán)即如何對用戶訪問資源進(jìn)行控制,首先需要學(xué)習(xí)授權(quán)相關(guān)的數(shù)據(jù)模型。

授權(quán)可簡單理解為Who對What(which)進(jìn)行How操作,包括如下:

Who,即主體(Subject),主體一般是指用戶,也可以是程序,需要訪問系統(tǒng)中的資源。 What,即資源(Resource),如系統(tǒng)菜單、頁面、按鈕、代碼方法、系統(tǒng)商品信息、系統(tǒng)訂單信息等。系統(tǒng)菜單、頁面、按鈕、代碼方法都屬于系統(tǒng)功能資源,對于web系統(tǒng)每個(gè)功能資源通常對應(yīng)一個(gè)URL;系統(tǒng)商品信息、系統(tǒng)訂單信息都屬于實(shí)體資源(數(shù)據(jù)資源),實(shí)體資源由資源類型和資源實(shí)例組成,比如商品信息為資源類型,商品編號 為001的商品為資源實(shí)例。

How,權(quán)限/許可(Permission),規(guī)定了用戶對資源的操作許可,權(quán)限離開資源沒有意義,如用戶查詢權(quán)限、用戶添加權(quán)限、某個(gè)代碼方法的調(diào)用權(quán)限、編號為001的用戶的修改權(quán)限等,通過權(quán)限可知用戶對哪些資源都有哪些操作可。

主體、資源、權(quán)限關(guān)系如下圖:

?


主體、資源、權(quán)限相關(guān)的數(shù)據(jù)模型如下:

主體(用戶id、賬號、密碼、...)

資源(資源id、資源名稱、訪問地址、...)

權(quán)限(權(quán)限id、權(quán)限標(biāo)識、權(quán)限名稱、資源id、...)

角色(角色id、角色名稱、...)

角色和權(quán)限關(guān)系(角色 id、權(quán)限id、...)

主體(用戶)和角色關(guān)系(用戶id、角色id、...)

主體(用戶)、資源、權(quán)限關(guān)系如下圖:

?


通常企業(yè)開發(fā)中將資源和權(quán)限表合并為一張權(quán)限表,如下:

資源(資源id、資源名稱、訪問地址、...)

權(quán)限(權(quán)限id、權(quán)限標(biāo)識、權(quán)限名稱、資源id、...)

合并為:

權(quán)限(權(quán)限id、權(quán)限標(biāo)識、權(quán)限名稱、資源名稱、資源訪問地址、...)

修改后數(shù)據(jù)模型之間的關(guān)系如下圖:

?


1.5?RBAC

用戶,角色,權(quán)限 本質(zhì):就是把權(quán)限打包給角色,分配給用戶

RBAC一般指基于角色的訪問控制???權(quán)限 ?五張表 (最少五張表)

基于角色的訪問控制(RBAC)是實(shí)施面向企業(yè)安全策略的一種有效的訪問控制方式。

1.5.1?基于角色的訪問控制

RBAC基于角色的訪問控制(Role-Based Access Control)是按角色進(jìn)行授權(quán),比如:主體的角色為總經(jīng)理可以查詢企業(yè)運(yùn)營報(bào)表,查詢員工工資信息等

根據(jù)上圖中的判斷邏輯,授權(quán)代碼可表示如下:

if(主體.hasRole("總經(jīng)理角色id")){

查詢工資

}

如果上圖中查詢工資所需要的角色變化為總經(jīng)理和部門經(jīng)理,此時(shí)就需要修改判斷邏輯為“判斷用戶的角色是否是總經(jīng)理或部門經(jīng)理”,修改代碼如下:

if(主體.hasRole("總經(jīng)理角色id") ||? 主體.hasRole("部門經(jīng)理角色id")){

? ? 查詢工資

}

根據(jù)上邊的例子發(fā)現(xiàn),當(dāng)需要修改角色的權(quán)限時(shí)就需要修改授權(quán)的相關(guān)代碼,系統(tǒng)可擴(kuò)展性差。

1.5.2?基于資源的訪問控制

RBAC基于資源的訪問控制(Resource-Based Access Control)是按資源(或權(quán)限)進(jìn)行授權(quán),比如:用戶必須具有查詢工資權(quán)限才可以查詢員工工資信息等,如下的判斷

if(主體.hasPermission("查詢工資") ){

? ? 查詢工資

}

優(yōu)點(diǎn):系統(tǒng)設(shè)計(jì)時(shí)定義好查詢工資的權(quán)限標(biāo)識,即使查詢工資所需要的角色變化為總經(jīng)理和部門經(jīng)理也不需要修改授權(quán)代碼,系統(tǒng)可擴(kuò)展性強(qiáng)。

2.?Spring Security 簡介

官網(wǎng): https://spring.io/projects/spring-security?

中文文檔: https://www.springcloud.cc/spring-security.html?

?


2.1?什么是SpringSecurity?

Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式(注解)的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉(zhuǎn)Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,減少了為企業(yè)系統(tǒng)安全控制編寫大量重復(fù)代碼的工作。

以上解釋來源于百度百科??梢砸痪湓拋砀爬?,SpringSecurity 是一個(gè)安全框架。

3.?Spring Security入門體驗(yàn)

3.1?創(chuàng)建父項(xiàng)目spring-security-main

把下面的項(xiàng)目都放在這個(gè)項(xiàng)目里面,方便展示和學(xué)習(xí)

?


3.2?創(chuàng)建項(xiàng)目security-hello并且選擇依賴

?


3.3?pom.xml

3.4?啟動(dòng)類

@SpringBootApplication
@EnableWebSecurity // 啟用security 在5.X版本之后可以不用加,默認(rèn)就是開啟的
public class SecurityHelloApplication {

????public static void main(String[] args) {
????????SpringApplication.run(SecurityHelloApplication.class, args);
????}

}

3.5?創(chuàng)建HelloController

@RestController
public class HelloController {

????@GetMapping("hello")
????public String hello() {
????????return "hello security";
????}
}

3.6?啟動(dòng)測試訪問

http://127.0.0.1:8080/hello?

發(fā)現(xiàn)我們無法訪問hello這個(gè)請求,這是因?yàn)閟pring Security默認(rèn)攔截了所有請求

?

使用user+啟動(dòng)日志里面的密碼登陸

?

?


登錄后就可以訪問我們的hello了

?


3.7?測試退出

訪問: http://localhost:8080/logout?

?

?


3.8?自定義密碼登錄(yml配置文件方式)

spring:
????security:
????????user:
????????????name: admin ????????#默認(rèn)使用的用戶名
????????????password: 123456 ???#默認(rèn)使用的密碼

3.9?重啟使用admin和123456登錄即可

3.10?總結(jié)

從上面的體驗(yàn)來說,是不是感覺很簡單,但是別急。后面的東西還是有點(diǎn)難度的,如下:

l?如何讀取數(shù)據(jù)庫的用戶名和密碼

l?如何對密碼加密

l?如何使用數(shù)據(jù)的角色和權(quán)限

l?如何配置方法級別的權(quán)限訪問

l?如何自定義登陸頁面

l?如何集成redis把登陸信息放到Redis

l?如何集成驗(yàn)證碼

l?……………………

4.?Spring Security配置多用戶認(rèn)證

4.1?概述

認(rèn)證就是登陸,我們現(xiàn)在沒有連接數(shù)據(jù)庫,那么我們可以模擬下用戶名和密碼

4.2?創(chuàng)建認(rèn)證的配置類WebSecurityConfig

4.3?啟動(dòng)測試

我們只要添加了安全配置類,那么我們在yml里面的配置就失效了

我們使用cxs/123訪問登錄,發(fā)現(xiàn)控制臺報(bào)錯(cuò)了

?


這個(gè)是因?yàn)閟pring Sercurity強(qiáng)制要使用密碼加密,當(dāng)然我們也可以不加密,但是官方要求是不管你是否加密,都必須配置一個(gè)類似Shiro的憑證匹配器

4.4?修改WebSecurityConfig添加加密器

4.5?重啟測試

兩個(gè)用戶都可以登錄成功了

4.6?測試加密和解密

我們對123字符串加密三次,然后匹配三次,看看效果

查看控制臺發(fā)現(xiàn)特點(diǎn)是:相同的字符串加密之后的結(jié)果都不一樣,但是比較的時(shí)候是一樣的

?

4.7?如何獲取當(dāng)前登錄用戶的信息(兩種方式)【重點(diǎn)】

我們添加獲取當(dāng)前用戶信息的Controller

?

測試訪問

http://localhost:8080/userInfo?

http://localhost:8080/userInfo2

?

5.?Spring Security用戶角色,權(quán)限攔截配置

5.1?角色和權(quán)限的配置,修改WebSecurityConfig類

5.2?添加幾個(gè)Controller接口

5.3?啟動(dòng)測試

使用cxs/123登錄后 這幾個(gè)接口都可以訪問

使用test/123登錄后,訪問/update和/del會(huì)報(bào)錯(cuò)跳到403頁面

使用admin/123登錄后,只能訪問/admin/hello,訪問其他接口會(huì)跳到403頁面

5.4?我們添加一個(gè)403.html,讓他報(bào)錯(cuò)后跳到我們自己的頁面

?


5.5?重啟使用test登錄后訪問/del

?


6.?Spring Security方法級別的授權(quán)鑒權(quán)

我們使用方法級別的授權(quán)后,只需要在controller對應(yīng)的方法上添加注解即可了,不需要再webSecurityConfig中配置匹配的url和權(quán)限了,這樣就爽多了

6.1?相關(guān)注解說明

@PreAuthorize? 在方法調(diào)用前進(jìn)行權(quán)限檢查

@PostAuthorize?在方法調(diào)用后進(jìn)行權(quán)限檢查

上面的個(gè)注解如果要使用的話必須加上

@EnableGlobalMethodSecurity(prePostEnabled = true)

如果只使用PreAuthorize?就只用開啟prePostEnabled = true

6.2?在WebSecurityConfig類上添加注解

?


6.3?注釋掉WebSecurityConfig配置url和權(quán)限的代碼

?


6.4?修改controller,給方法添加注解

不加注解的,都可以訪問,加了注解的,要有對應(yīng)權(quán)限才可以訪問哦

6.5?重啟測試即可

7.?Spring Security返回JSON(前后端分離)

在上面的例子中,我們返回的是403頁面,但是在開發(fā)中,如RestAPI風(fēng)格的數(shù)據(jù),是不能返回一個(gè)頁面,而應(yīng)該是給一個(gè)json

7.1?創(chuàng)建Result

?

7.2?創(chuàng)建登陸成功的處理器AppAuthenticationSuccessHandler

?

7.3?創(chuàng)建登陸失敗處理器AppAuthenticationFailureHandler


?

7.4?創(chuàng)建無權(quán)限處理器AppAccessDeniedHandler

?

7.5?創(chuàng)建登出處理器AppLogoutSuccessHandler

?

7.6?修改WebSecurityConfig出現(xiàn)拒接訪問走自己的處理器

?

7.7?重啟使用test/123登錄測試

?


8.?【源碼分析】Spring Security認(rèn)證授權(quán)總攬

目的:怎么自定義登錄

權(quán)限的控制 人家已經(jīng)寫的很好了

8.1?結(jié)構(gòu)總攬

? ??Spring Security所解決的問題就是安全訪問控制,而安全訪問控制功能其實(shí)就是對所有進(jìn)入系統(tǒng)的請求進(jìn)行攔截,校驗(yàn)每個(gè)請求是否能夠訪問它所期望的資源。根據(jù)前邊知識的學(xué)習(xí),可以通過Filter或AOP等技術(shù)來實(shí)現(xiàn),SpringSecurity對Web資源的保護(hù)是靠Filter實(shí)現(xiàn)的,所以從這個(gè)Filter來入手,逐步深入Spring Security原理。當(dāng)初始化Spring Security時(shí),會(huì)創(chuàng)建一個(gè)名為 SpringSecurityFilterChain 的Servlet過濾器,類型為org.springframework.security.web.FilterChainProxy,它實(shí)現(xiàn)了javax.servlet.Filter,因此外部的請求會(huì)經(jīng)過此類,下圖是Spring Security過慮器鏈結(jié)構(gòu)圖:

?


8.2?上圖說明

FilterChainProxy 是一個(gè)代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各個(gè)Filter,同時(shí)這些Filter作為Bean被Spring管理,它們是Spring Security核心,各有各的職責(zé),但他們并不直接處理用戶的認(rèn)證,也不直接處理用戶的授權(quán),而是把它們交給了認(rèn)證管理器(AuthenticationManager)和決策管理器(AccessDecisionManager)進(jìn)行處理

下圖是FilterChainProxy相關(guān)類的UML圖示

?

?


spring Security功能的實(shí)現(xiàn)主要是由一系列過濾器鏈相互配合完成。

?


?

8.3?過濾器鏈中主要的幾個(gè)過濾器及其作用

8.3.1?SecurityContextPersistenceFilter?

? ??這個(gè)Filter是整個(gè)攔截過程的入口和出口(也就是第一個(gè)和最后一個(gè)攔截器),會(huì)在請求開始時(shí)從配置好的 SecurityContextRepository 中獲取 SecurityContext,然后把它設(shè)置給SecurityContextHolder。在請求完成后將 SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同時(shí)清除 securityContextHolder 所持有的 SecurityContext;

8.3.2?UsernamePasswordAuthenticationFilter?

? ??用于處理來自表單提交的認(rèn)證。該表單必須提供對應(yīng)的用戶名和密碼,其內(nèi)部還有登錄成功或失敗后進(jìn)行處理的 AuthenticationSuccessHandler 和AuthenticationFailureHandler,這些都可以根據(jù)需求做相關(guān)改變;

8.3.3?FilterSecurityInterceptor?

? ??是用于保護(hù)web資源的,使用AccessDecisionManager對當(dāng)前用戶進(jìn)行授權(quán)訪問;

8.3.4?ExceptionTranslationFilter?

? ??能夠捕獲來自 FilterChain 所有的異常,并進(jìn)行處理。但是它只會(huì)處理兩類異常:AuthenticationException 和 AccessDeniedException,其它的異常它會(huì)繼續(xù)拋出。

9.?【源碼分析】Spring Security認(rèn)證工作流程重點(diǎn)

SecurityContextPersistenceFilter?

UsernamePasswordAuthenticationFilter (attemptAuthentication)

ProviderManager(authenticate)

DaoAuthenticationProvider (retrieveUser)

AbstractUserDetailsAuthenticationProvider(authenticate)

?

9.1?認(rèn)證流程圖

?


9.2?流程圖分析

1.?用戶提交用戶名、密碼被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 過濾器獲取到,封裝為請求Authentication,通常情況下是UsernamePasswordAuthenticationToken這個(gè)實(shí)現(xiàn)類。

2.?然后過濾器將Authentication提交至認(rèn)證管理器(AuthenticationManager)進(jìn)行認(rèn)證

3.?認(rèn)證成功后, AuthenticationManager 身份管理器返回一個(gè)被填充滿了信息的(包括上面提到的權(quán)限信息,身份信息,細(xì)節(jié)信息,但密碼通常會(huì)被移除) Authentication 實(shí)例。

4.?SecurityContextHolder 安全上下文容器將第3步填充了信息的 Authentication ,通過SecurityContextHolder.getContext().setAuthentication(…)方法,設(shè)置到其中??梢钥闯鯝uthenticationManager接口(認(rèn)證管理器)是認(rèn)證相關(guān)的核心接口,也是發(fā)起認(rèn)證的出發(fā)點(diǎn),它的實(shí)現(xiàn)類為ProviderManager。而Spring Security支持多種認(rèn)證方式,因此ProviderManager維護(hù)著一個(gè)List<AuthenticationProvider> 列表,存放多種認(rèn)證方式,最終實(shí)際的認(rèn)證工作是由AuthenticationProvider完成的。咱們知道web表單的對應(yīng)的AuthenticationProvider實(shí)現(xiàn)類為DaoAuthenticationProvider,它的內(nèi)部又維護(hù)著一個(gè)UserDetailsService負(fù)責(zé)UserDetails的獲取。最終AuthenticationProvider將UserDetails填充至Authentication

9.3?斷點(diǎn)調(diào)試及源碼分析

看上圖打斷點(diǎn)調(diào)試

9.4?結(jié)果總結(jié)

9.4.1?AuthenticationProvider

通過前面的Spring Security認(rèn)證流程我們得知,認(rèn)證管理器(AuthenticationManager)委托AuthenticationProvider完成認(rèn)證工作。

AuthenticationProvider是一個(gè)接口,定義如下:

authenticate()方法定義了認(rèn)證的實(shí)現(xiàn)過程,它的參數(shù)是一個(gè)Authentication,里面包含了登錄用戶所提交的用戶、密碼等。而返回值也是一個(gè)Authentication,這個(gè)Authentication則是在認(rèn)證成功后,將用戶的權(quán)限及其他信息重新組裝后生成。

Spring Security中維護(hù)著一個(gè) List<AuthenticationProvider> 列表,存放多種認(rèn)證方式,不同的認(rèn)證方式使用不同的AuthenticationProvider。如使用用戶名密碼登錄時(shí),使用AuthenticationProvider1,短信登錄時(shí)使用AuthenticationProvider2等等這樣的例子很多。

每個(gè)AuthenticationProvider需要實(shí)現(xiàn)supports()方法來表明自己支持的認(rèn)證方式,如我們使用表單方式認(rèn)證,在提交請求時(shí)Spring Security會(huì)生成UsernamePasswordAuthenticationToken,它是一個(gè)Authentication,里面封裝著用戶提交的用戶名、密碼信息。而對應(yīng)的,哪個(gè)AuthenticationProvider來處理它?

我們在DaoAuthenticationProvider的基類AbstractUserDetailsAuthenticationProvider發(fā)現(xiàn)以下代碼:

public boolean supports(Class<?> authentication) {

????return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);

}

也就是說當(dāng)web表單提交用戶名密碼時(shí),Spring Security由DaoAuthenticationProvider處理。

最后,我們來看一下 Authentication(認(rèn)證信息)的結(jié)構(gòu),它是一個(gè)接口,我們之前提到的

UsernamePasswordAuthenticationToken就是它的實(shí)現(xiàn)之一:

?public interface Authentication extends Principal, Serializable { ????????

????Collection<? extends GrantedAuthority> getAuthorities(); ?????????????

????Object getCredentials(); ????????????????????????????????????????????????????

????Object getDetails(); ?????????????????????????????????????????????????

????Object getPrincipal(); ???????????????????????????????????????????????

????boolean isAuthenticated(); ??????????????????????????????????????????

????void setAuthenticated(boolean var1) throws IllegalArgumentException;

}

(1)Authentication是spring security包中的接口,直接繼承自Principal類,而Principal是位于 java.security

包中的。它是表示著一個(gè)抽象主體身份,任何主體都有一個(gè)名稱,因此包含一個(gè)getName()方法。

(2)getAuthorities(),權(quán)限信息列表,默認(rèn)是GrantedAuthority接口的一些實(shí)現(xiàn)類,通常是代表權(quán)限信息的一系

列字符串。

(3)getCredentials(),憑證信息,用戶輸入的密碼字符串,在認(rèn)證過后通常會(huì)被移除,用于保障安全。

(4)getDetails(),細(xì)節(jié)信息,web應(yīng)用中的實(shí)現(xiàn)接口通常為 WebAuthenticationDetails,它記錄了訪問者的ip地

址和sessionId的值。

(5)getPrincipal(),身份信息,大部分情況下返回的是UserDetails接口的實(shí)現(xiàn)類,UserDetails代表用戶的詳細(xì)

信息,那從Authentication中取出來的UserDetails就是當(dāng)前登錄用戶信息,它也是框架中的常用接口之一。

9.4.2?UserDetailsService重點(diǎn)[自定義查詢數(shù)據(jù)庫]

9.4.2.1?認(rèn)識UserDetailsService

現(xiàn)在咱們現(xiàn)在知道DaoAuthenticationProvider處理了web表單的認(rèn)證邏輯,認(rèn)證成功后既得到一個(gè)Authentication(UsernamePasswordAuthenticationToken實(shí)現(xiàn)),里面包含了身份信息(Principal)。這個(gè)身份信息就是一個(gè) Object ,大多數(shù)情況下它可以被強(qiáng)轉(zhuǎn)為UserDetails對象。

DaoAuthenticationProvider中包含了一個(gè)UserDetailsService實(shí)例,它負(fù)責(zé)根據(jù)用戶名提取用戶信息UserDetails(包含密碼),而后DaoAuthenticationProvider會(huì)去對比UserDetailsService提取的用戶密碼與用戶提交的密碼是否匹配作為認(rèn)證成功的關(guān)鍵依據(jù),因此可以通過將自定義的 UserDetailsService 公開為spring bean來定義自定義身份驗(yàn)證。

public interface UserDetailsService { ??

????UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; ???

}

很多人把 DaoAuthenticationProvider和UserDetailsService的職責(zé)搞混淆,其實(shí)UserDetailsService只負(fù)責(zé)從特定的地方(通常是數(shù)據(jù)庫)加載用戶信息,僅此而已。而DaoAuthenticationProvider的職責(zé)更大,它完成完整的認(rèn)證流程,同時(shí)會(huì)把UserDetails填充至Authentication。

上面一直提到UserDetails是用戶信息,咱們看一下它的真面目:

它和Authentication接口很類似,比如它們都擁有username,authorities。Authentication的getCredentials()與UserDetails中的getPassword()需要被區(qū)分對待,前者是用戶提交的密碼憑證,后者是用戶實(shí)際存儲的密碼,認(rèn)證其實(shí)就是對這兩者的比對。Authentication中的getAuthorities()實(shí)際是由UserDetails的getAuthorities()傳遞而形成的。還記得Authentication接口中的getDetails()方法嗎?其中的UserDetails用戶詳細(xì)信息便是經(jīng)過了AuthenticationProvider認(rèn)證之后被填充的。

通過實(shí)現(xiàn)UserDetailsService和UserDetails,我們可以完成對用戶信息獲取方式以及用戶信息字段的擴(kuò)展。

Spring Security提供的InMemoryUserDetailsManager(內(nèi)存認(rèn)證),JdbcUserDetailsManager(jdbc認(rèn)證)就是

UserDetailsService的實(shí)現(xiàn)類,主要區(qū)別無非就是從內(nèi)存還是從數(shù)據(jù)庫加載用戶。

9.4.2.2?測試

自定義UserDetailsService

重啟工程,請求認(rèn)證,SpringDataUserDetailsService的loadUserByUsername方法被調(diào)用 ,查詢用戶信息。

10.?【源碼分析】Spring Security授權(quán)工作流程[了解]

10.1?授權(quán)流程圖

通過快速上手我們知道,Spring Security可以通過 http.authorizeRequests() 對web請求進(jìn)行授權(quán)保護(hù)。SpringSecurity使用標(biāo)準(zhǔn)Filter建立了對web請求的攔截,最終實(shí)現(xiàn)對資源的授權(quán)訪問。

Spring Security的授權(quán)流程如下:

?


10.2?授權(quán)流程分析

10.2.1?攔截請求

已認(rèn)證用戶訪問受保護(hù)的web資源將被SecurityFilterChain中的 FilterSecurityInterceptor 的子類攔截。

10.2.2?獲取資源訪問策略

FilterSecurityInterceptor會(huì)從 SecurityMetadataSource 的子類DefaultFilterInvocationSecurityMetadataSource 獲取要訪問當(dāng)前資源所需要的權(quán)限Collection<ConfigAttribute> 。

SecurityMetadataSource其實(shí)就是讀取訪問策略的抽象,而讀取的內(nèi)容,其實(shí)就是我們配置的訪問規(guī)則, 讀取訪問策略如:

?


10.2.3?最后

FilterSecurityInterceptor會(huì)調(diào)用 AccessDecisionManager 進(jìn)行授權(quán)決策,若決策通過,則允許訪問資源,否則將禁止訪問。

AccessDecisionManager(訪問決策管理器)的核心接口如下:

這里著重說明一下decide的參數(shù):

authentication:要訪問資源的訪問者的身份

object:要訪問的受保護(hù)資源,web請求對應(yīng)FilterInvocation

configAttributes:是受保護(hù)資源的訪問策略,通過SecurityMetadataSource獲取。

decide接口就是用來鑒定當(dāng)前用戶是否有訪問對應(yīng)受保護(hù)資源的權(quán)限。

10.3?授權(quán)決策分析

AccessDecisionManager采用投票的方式來確定是否能夠訪問受保護(hù)資源。

AccessDecisionManager中包含的一系列AccessDecisionVoter將會(huì)被用來對Authentication是否有權(quán)訪問受保護(hù)對象進(jìn)行投票,AccessDecisionManager根據(jù)投票結(jié)果,做出最終決策。

AccessDecisionVoter是一個(gè)接口,其中定義有三個(gè)方法,具體結(jié)構(gòu)如下所示。

public interface AccessDecisionVoter<S> {

????int ACCESS_GRANTED = 1;

????int ACCESS_ABSTAIN = 0;

????int ACCESS_DENIED = ‐1;

????boolean supports(ConfigAttribute var1);

????boolean supports(Class<?> var1);

????int vote(Authentication var1, S var2, Collection<ConfigAttribute> var3);

}

vote()方法的返回結(jié)果會(huì)是AccessDecisionVoter中定義的三個(gè)常量之一。ACCESS_GRANTED表示同意,ACCESS_DENIED表示拒絕,ACCESS_ABSTAIN表示棄權(quán)。如果一個(gè)AccessDecisionVoter不能判定當(dāng)前Authentication是否擁有訪問對應(yīng)受保護(hù)對象的權(quán)限,則其vote()方法的返回值應(yīng)當(dāng)為棄權(quán)ACCESS_ABSTAIN。

? ? ? Spring Security內(nèi)置了三個(gè)基于投票的AccessDecisionManager實(shí)現(xiàn)類如下,它們分別是AffirmativeBased、ConsensusBased和UnanimousBased,。

10.3.1?AffirmativeBased的邏輯是:一票通過

?(1)只要有AccessDecisionVoter的投票為ACCESS_GRANTED則同意用戶進(jìn)行訪問;

?(2)如果全部棄權(quán)也表示通過;

?(3)如果沒有一個(gè)人投贊成票,但是有人投反對票,則將拋出AccessDeniedException。

Spring security默認(rèn)使用的是AffirmativeBased。

10.3.2?ConsensusBased的邏輯是:多數(shù)派

(1)如果贊成票多于反對票則表示通過。

(2)反過來,如果反對票多于贊成票則將拋出AccessDeniedException。

(3如果贊成票與反對票相同且不等于0,并且屬性allowIfEqualGrantedDeniedDecisions的值為true,則表示通過,否則將拋出異常AccessDeniedException。參數(shù)allowIfEqualGrantedDeniedDecisions的值默認(rèn)為true。

(4)如果所有的AccessDecisionVoter都棄權(quán)了,則將視參數(shù)allowIfAllAbstainDecisions的值而定,如果該值為true則表示通過,否則將拋出異常AccessDeniedException。參數(shù)allowIfAllAbstainDecisions的值默認(rèn)為false。

10.3.3?UnanimousBased的邏輯具體是:

UnanimousBased的邏輯與另外兩種實(shí)現(xiàn)有點(diǎn)不一樣,另外兩種會(huì)一次性把受保護(hù)對象的配置屬性全部傳遞給AccessDecisionVoter進(jìn)行投票,而UnanimousBased會(huì)一次只傳遞一個(gè)ConfigAttribute給AccessDecisionVoter進(jìn)行投票。這也就意味著如果我們的AccessDecisionVoter的邏輯是只要傳遞進(jìn)來的

ConfigAttribute中有一個(gè)能夠匹配則投贊成票,但是放到UnanimousBased中其投票結(jié)果就不一定是贊成了。

UnanimousBased的邏輯具體來說是這樣的:

(1)如果受保護(hù)對象配置的某一個(gè)ConfigAttribute被任意的AccessDecisionVoter反對了,則將拋出AccessDeniedException。

(2)如果沒有反對票,但是有贊成票,則表示通過。

(3)如果全部棄權(quán)了,則將視參數(shù)allowIfAllAbstainDecisions的值而定,true則通過,false則拋出AccessDeniedException。

Spring Security也內(nèi)置一些投票者實(shí)現(xiàn)類如RoleVoter、AuthenticatedVoter和WebExpressionVoter等,可以自行查閱資料進(jìn)行學(xué)習(xí)。

?

總結(jié):認(rèn)證和鑒權(quán)都是過濾器鏈 ,認(rèn)證是重點(diǎn)關(guān)注

UserDetailsService接口 loadUserByUsername() 我們可以實(shí)現(xiàn)這個(gè)接口 集成數(shù)據(jù)庫

11.?Spring Security集成Thymeleaf詳解

Springsecurity+mysql完成認(rèn)證和授權(quán)

1,自定義訪問數(shù)據(jù)庫UserDetailsService接口 loadUserByUsername()

2,自定義登陸頁面

3,能不能自定義登陸的請求地址呢 /默認(rèn)為/login

4,能不能自定義登出的地址呢 ?默認(rèn)為/logout

5,能不能自定義表單的名字 ?默認(rèn)為username password

?

11.1?準(zhǔn)備數(shù)據(jù)庫

?

11.2?創(chuàng)建新項(xiàng)目選擇依賴

?


11.3?pom.xml

11.4?application.yml

11.5?修改啟動(dòng)類

11.6?逆向生成SysUser

?


11.7?修改SysUser【重點(diǎn)】

因?yàn)槲覀円咦远x登錄方法,方法需要返回UserDetails,所以我們就這么來

11.8?創(chuàng)建AppUserDetailsServiceImpl【重點(diǎn)】

11.9?修改SysUserMapper

11.10?修改SysUserMapper.xml文件(sql)


11.11?創(chuàng)建WebSecurityConfig配置類【重點(diǎn)】

11.12?創(chuàng)建RouterController

11.13?創(chuàng)建UserController

11.14?創(chuàng)建頁面

11.14.1?在tempaltes下面創(chuàng)建main.html和login.html

創(chuàng)建main.html

創(chuàng)建login.html

11.14.2?在tempaltes/user下面創(chuàng)建頁面

創(chuàng)建export.html

創(chuàng)建query.html

創(chuàng)建add.html

創(chuàng)建update.html

?

11.14.3?在static/error下面創(chuàng)建頁面

創(chuàng)建403.html

11.15?啟動(dòng)測試即可

11.16?當(dāng)用戶沒有某權(quán)限時(shí),頁面不展示該按鈕

上一講里面我們創(chuàng)建的項(xiàng)目里面是當(dāng)用戶點(diǎn)擊頁面上的鏈接請求到后臺之后沒有權(quán)限會(huì)跳轉(zhuǎn)到403那么如果用戶沒有權(quán)限,對應(yīng)的按鈕就不顯示出來,這樣豈不是更好嗎

我們接著上一個(gè)項(xiàng)目來改造

引入下面的依賴

?

11.17?登錄后查看效果

?


12.?Spring Security集成Thymeleaf圖片驗(yàn)證碼

以前因?yàn)槲覀冏约簩懙顷懙姆椒梢栽谧约旱牡顷懛椒ɡ锩嫒ソ邮枕撁鎮(zhèn)鬟^來的code再和session里面正確的code進(jìn)行比較 ??

12.1?概述

上一講里面我們集成了Thymeleaf實(shí)現(xiàn)在頁面鏈接的動(dòng)態(tài)判斷是否顯示,那么在實(shí)際開發(fā)中,我們會(huì)遇到有驗(yàn)證碼的功能,那么如何處理呢?

我們接著上一個(gè)項(xiàng)目來改造

12.2?原理、存在問題、解決思路

我們知道Spring Security是通過過濾器鏈來完成了,所以它的解決方案是創(chuàng)建一個(gè)過濾器放到Security的過濾器鏈中,在自定義的過濾器中比較驗(yàn)證碼

12.3?添加依賴(生成驗(yàn)證碼)

12.4?添加一個(gè)獲取驗(yàn)證碼的接口

12.5?創(chuàng)建驗(yàn)證碼過濾器ValidateCodeFilter【重點(diǎn)】

12.6?修改WebSecurityConfig【重點(diǎn)】

?

12.7?修改login.html

12.8?測試登錄即可

故意填錯(cuò)驗(yàn)證碼

?


13.?Spring Security集成Thymeleaf+ajax的驗(yàn)證碼處理

13.1?創(chuàng)建項(xiàng)目

繼續(xù)上一個(gè)項(xiàng)目的修改

13.2?加入依賴

不用動(dòng)

13.3?創(chuàng)建Result

13.4?創(chuàng)建四個(gè)處理器

13.4.1?創(chuàng)建登陸成功的處理器AppAuthenticationSuccessHandler

?

13.4.2?創(chuàng)建登陸失敗處理器AppAuthenticationFailureHandler

?

?

13.4.3?創(chuàng)建無權(quán)限處理器AppAccessDeniedHandler

?

13.4.4?創(chuàng)建登出處理器AppLogoutSuccessHandler

?

?

13.5?修改ValidateCodeFilter處理驗(yàn)證碼

?

13.6?創(chuàng)建WebSecurityConfig配置

?

13.7?引入vue代碼和axios

?


13.8?修改templates/login.html

13.9?啟動(dòng)測試

?


14.?【掌握】JWT概述

14.1?概述

14.1.1?什么是JWT

Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)((RFC 7519).該token被設(shè)計(jì)為緊湊且安全的,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密【可解析的】

官網(wǎng): https://jwt.io/?

14.1.2?跨域認(rèn)證問題

互聯(lián)網(wǎng)服務(wù)離不開用戶認(rèn)證。一般流程是下面這樣。

l?用戶向服務(wù)器發(fā)送用戶名和密碼。

l?服務(wù)器驗(yàn)證通過后,在當(dāng)前對話(session)里面保存相關(guān)數(shù)據(jù),比如用戶角色、登錄時(shí)間等等。

l?服務(wù)器向用戶返回一個(gè) jsession_id,寫入用戶的 Cookie。

l?用戶隨后的每一次請求,都會(huì)通過 Cookie,將 session_id 傳回服務(wù)器。

l?服務(wù)器收到 session_id,找到前期保存的數(shù)據(jù),由此得知用戶的身份。

這種模式的問題在于,擴(kuò)展性(scaling)不好。單機(jī)當(dāng)然沒有問題,如果是服務(wù)器集群,或者是跨域的服務(wù)導(dǎo)向架構(gòu),就要求 session 數(shù)據(jù)共享,每臺服務(wù)器都能夠讀取 session。

舉例來說,A 網(wǎng)站和 B 網(wǎng)站是同一家公司的關(guān)聯(lián)服務(wù)?,F(xiàn)在要求,用戶只要在其中一個(gè)網(wǎng)站登錄,再訪問另一個(gè)網(wǎng)站就會(huì)自動(dòng)登錄,請問怎么實(shí)現(xiàn)?

一種解決方案是 session 數(shù)據(jù)持久化,寫入數(shù)據(jù)庫或別的持久層。各種服務(wù)收到請求后,都向持久層請求數(shù)據(jù)。這種方案的優(yōu)點(diǎn)是架構(gòu)清晰,缺點(diǎn)是工程量比較大。另外,持久層萬一掛了,就會(huì)單點(diǎn)失敗。

另一種方案是服務(wù)器索性不保存 session 數(shù)據(jù)了,所有數(shù)據(jù)都保存在客戶端,每次請求都發(fā)回服務(wù)器。JWT 就是這種方案的一個(gè)代表。?服務(wù)器不存數(shù)據(jù),客戶端存,服務(wù)器解析就行了

14.2?JWT 的原理

JWT 的原理是,服務(wù)器認(rèn)證成功以后,生成一個(gè) JSON 對象,發(fā)回給用戶,就像下面這樣。

{

??"姓名": "張三",

??"角色": "管理員",

??"到期時(shí)間": "2018年7月1日0點(diǎn)0分"

}

以后,用戶與服務(wù)端通信的時(shí)候,都要發(fā)回這個(gè) JSON 對象。服務(wù)器完全只靠這個(gè)對象認(rèn)定用戶身份。為了防止用戶篡改數(shù)據(jù),服務(wù)器在生成這個(gè)對象的時(shí)候,會(huì)加上簽名(詳見后文)。

服務(wù)器就不保存任何 session 數(shù)據(jù)了,也就是說,服務(wù)器變成無狀態(tài)了,從而比較容易實(shí)現(xiàn)擴(kuò)展。

14.3?JWT 的數(shù)據(jù)結(jié)構(gòu)

實(shí)際的 JWT 大概就像下面這樣。

?


它是一個(gè)很長的字符串,中間用點(diǎn)(.)分隔成三個(gè)部分。注意,JWT 內(nèi)部是沒有換行的,這里只是為了便于展示,將它寫成了幾行。

JWT 的三個(gè)部分依次如下。?

面試問題: jwt知道嗎?談?wù)勀愕睦斫猓ㄓ蓽\入深的聊)

??Header(頭部)

??Payload(負(fù)載)

??Signature(簽名)

寫成一行,就是下面的樣子。

Header.Payload.Signature

?

?


下面依次介紹這三個(gè)部分。

14.3.1?Header

Header 部分是一個(gè) JSON 對象,描述 JWT 的元數(shù)據(jù),通常是下面的樣子。

{

??"alg": "HS256",

??"typ": "JWT"

}

上面代碼中,alg屬性表示簽名的算法(algorithm),默認(rèn)是 HMAC SHA256(寫成 HS256);typ屬性表示這個(gè)令牌(token)的類型(type),JWT 令牌統(tǒng)一寫為JWT。

最后,將上面的 JSON 對象使用?Base64URL 算法轉(zhuǎn)成字符串。

14.3.2?Payload

Payload 部分也是一個(gè) JSON 對象,用來存放實(shí)際需要傳遞的數(shù)據(jù)。JWT 規(guī)定了7個(gè)官方字段,供選用。

iss (issuer):簽發(fā)人

exp (expiration time):過期時(shí)間

sub (subject):主題

aud (audience):受眾

nbf (Not Before):生效時(shí)間

iat (Issued At):簽發(fā)時(shí)間

jti (JWT ID):編號

除了官方字段,你還可以在這個(gè)部分定義私有字段,下面就是一個(gè)例子。

{

??"sub": "1234567890",

??"name": "John Doe",

??"admin": true

}

注意,JWT 默認(rèn)是不加密的,任何人都可以讀到,所以不要把秘密信息(密碼,手機(jī)號等)放在這個(gè)部分。

這個(gè) JSON 對象也要使用 Base64URL 算法轉(zhuǎn)成字符串。

14.3.3?Signature保證數(shù)據(jù)安全性的

Signature 部分是對前兩部分的簽名,防止數(shù)據(jù)篡改。

首先,需要指定一個(gè)密鑰(secret)。這個(gè)密鑰只有服務(wù)器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認(rèn)是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。

?HMACSHA256(

??base64UrlEncode(header) + "." +

??base64UrlEncode(payload),

??secret)

算出簽名以后,把 Header、Payload、Signature 三個(gè)部分拼成一個(gè)字符串,每個(gè)部分之間用"點(diǎn)"(.)分隔,就可以返回給用戶。

14.3.4?Base64URL轉(zhuǎn)碼

前面提到,Header 和 Payload 串型化的算法是 Base64URL。這個(gè)算法跟 Base64 算法基本類似,但有一些小的不同。

JWT 作為一個(gè)令牌(token),有些場合可能會(huì)放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個(gè)字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。

14.4?JWT 的使用方式重點(diǎn)

客戶端收到服務(wù)器返回的 JWT,可以儲存在 Cookie 里面,也可以儲存在 localStorage。

此后,客戶端每次與服務(wù)器通信,都要帶上這個(gè) JWT。你可以把它放在 Cookie 里面自動(dòng)發(fā)送,但是這樣不能跨域,所以更好的做法是放在 HTTP 請求的頭信息Authorization字段里面。

Authorization: Bearer?jwt

另一種做法是,跨域的時(shí)候,JWT 就放在 POST 請求的數(shù)據(jù)體里面。

14.5?JWT 的幾個(gè)特點(diǎn)

JWT 默認(rèn)是不加密,但也是可以加密的。生成原始 Token 以后,可以用密鑰再加密一次。

JWT 不加密的情況下,不能將秘密數(shù)據(jù)寫入 JWT。

JWT 不僅可以用于認(rèn)證,也可以用于交換信息。有效使用 JWT,可以降低服務(wù)器查詢數(shù)據(jù)庫的次數(shù)。

JWT 的最大缺點(diǎn)是,由于服務(wù)器不保存 session 狀態(tài),因此無法在使用過程中廢止某個(gè) token,或者更改 token 的權(quán)限。也就是說,一旦 JWT 簽發(fā)了,在到期之前就會(huì)始終有效,除非服務(wù)器部署額外的邏輯(JWT的登出問題)。就是因?yàn)榉?wù)端無狀態(tài)了

正常情況下 修改了密碼后就會(huì)跳轉(zhuǎn)到登錄頁面 :修改成功后清空瀏覽器保存的token了

后端怎么玩? 因?yàn)榉?wù)端不保留token 我用之前的token 還是可以繼續(xù)訪問的

從有狀態(tài)(后端也會(huì)存一個(gè))的變成無狀態(tài)的了

我們就要把它從無狀態(tài)再變成有狀態(tài)了

JWT 本身包含了認(rèn)證信息,一旦泄露,任何人都可以獲得該令牌的所有權(quán)限。為了減少盜用,JWT 的有效期應(yīng)該設(shè)置得比較短。對于一些比較重要的權(quán)限,使用時(shí)應(yīng)該再次對用戶進(jìn)行認(rèn)證。

為了減少盜用,JWT 不應(yīng)該使用 HTTP?80?協(xié)議明碼傳輸,要使用 HTTPS?443?協(xié)議傳輸。

我們頒發(fā)一個(gè)令牌 ?用戶名稱 用戶的權(quán)限信息 ??這個(gè)令牌2個(gè)小時(shí)有效

Jwt只要能解析 就認(rèn)為你是可用的 ??做不了 登出 ?后端不存儲用戶信息了 后端無狀態(tài)了


由于字?jǐn)?shù)限制本文只分享了一半哦~

完整版獲取可私信小動(dòng)~

更多干貨我們下期再說!

下期會(huì)分享

第十九章節(jié)

RocketMQ

相關(guān)知識~

下期見!



教程揭秘 | 動(dòng)力節(jié)點(diǎn)內(nèi)部Java零基礎(chǔ)教學(xué)文檔第十八篇:SpringSecurity的評論 (共 條)

使用qq登录你需要登录后才可以评论。
湟中县| 中牟县| 衡阳市| 托克逊县| 井陉县| 湟中县| 临泉县| 肇州县| 财经| 林周县| 博白县| 五家渠市| 钟山县| 应用必备| 兖州市| 庄浪县| 游戏| 剑河县| 大荔县| 行唐县| 鲁甸县| 屯门区| 达孜县| 济阳县| 定兴县| 衡南县| 三江| 虹口区| 平安县| 龙江县| 卢氏县| 壶关县| 衡南县| 沁源县| 翁源县| 阳城县| 泽州县| 凤庆县| 雅江县| 阿拉善右旗| 高台县|