背景介紹
網(wǎng)上搜索jwt有很多實(shí)現(xiàn)芥挣,但是大部分都是基于jjwt依賴自己實(shí)現(xiàn)jwt的生成邏輯渐裂,在Spring Security中結(jié)合Oauth2可以以少量代碼即可實(shí)現(xiàn)Jwt功能剂碴,將邏輯轉(zhuǎn)移到上層的框架中,可以有效的避免自寫代碼的安全漏洞,本項(xiàng)目
基于Springboot3的Spring Security搭建一套微服務(wù)的認(rèn)證授權(quán)框架,和大家分享朱灿,附上GitHub代碼续扔。
Spring-security-jwt代碼地址
Goals
- 實(shí)現(xiàn)jwt認(rèn)證(Authentication)
- 實(shí)現(xiàn)基于jwt的方法級(jí)授權(quán)(Authorization)
- 實(shí)現(xiàn)在安全模式下的swagger文檔(附帶)
Assumption
- 本項(xiàng)目使用spring-boot 3.0.4攻臀,jdk版本為17
- 使用了pring-security-oauth2-authorization-server來實(shí)現(xiàn)Authorization server
- Jwt本身的認(rèn)證功能可以通過Oauth2的spring-boot-starter-oauth2-resource-server包實(shí)現(xiàn),所以本次實(shí)現(xiàn)去除了jjwt的依賴纱昧,借助spring-boot-starter-oauth2-resource-server和spring-boot-starter-security實(shí)現(xiàn)
- 為了方便調(diào)試刨啸,添加了swagger-ui支持,文檔地址為/swagger-ui.html,基于springdoc而不是springfox识脆,注解有點(diǎn)區(qū)別设联,只是簡(jiǎn)單地進(jìn)行了實(shí)現(xiàn)
Out of scope
- Oauth2包含好幾種認(rèn)證機(jī)制,本次實(shí)現(xiàn)只用于微服務(wù)的jwt Token
- MethodSecurity有三種類型灼捂,在
EnableMethodSecurity
注解中聲明离例,分別是jsr250Enabled,prePostEnabled悉稠,securedEnabled宫蛆,項(xiàng)目實(shí)現(xiàn)了prePostEnabled中的@PreAuthorize和簡(jiǎn)單的@RolesAllowed。@RolesAllowed是通過Jsr250AuthorizationManage
r處理的的猛,不進(jìn)行展開 - 只添加了幾個(gè)jwt Claim耀盗,claim的最佳實(shí)踐需要自行研究,如果要更改還涉及到j(luò)wt轉(zhuǎn)化卦尊。
- web-service在解析jwt的時(shí)候未做更多的自定義叛拷,可在
/preAuthorize/id
端點(diǎn)看到,直接使用了authentication.name來映射jwt中的sub, 要走Principal的需要自己轉(zhuǎn)化
Approach
整體流程
本次實(shí)現(xiàn)包含三個(gè)部分猫牡,附上三個(gè)部分的流程圖
-
Jwt認(rèn)證流程(圖片不清晰胡诗,請(qǐng)參考github etc目錄下plantuml文件)
JwtAuthentication-jwt_Authentication_Diagram.png 基于Jwt的prePostEnabled授權(quán)流程
- Spring Security對(duì)于url的授權(quán)流程
Components
示例代碼有三個(gè)項(xiàng)目組成邓线,具體參考github的README.
- Auth-Service
- Web-service
- Simple-jwt-service
Jwt認(rèn)證流程解析
jwt的認(rèn)證是基于Filter來做的,請(qǐng)求進(jìn)來時(shí)FilterChain中的BearerTokenAuthenticationFilter
被調(diào)用煌恢,
由代碼可知骇陈,這里實(shí)現(xiàn)了認(rèn)證過程和SecurityContext的創(chuàng)建,我們繼續(xù)authenticate瑰抵,這里面持續(xù)地調(diào)用下層你雌,到JwtAuthenticationProvider
這個(gè)authenticate基本是實(shí)際認(rèn)證的地方了,有兩個(gè)點(diǎn)二汛,一個(gè)是jwtDecoder
婿崭,一個(gè)是jwtAuthenticationConverter
。
- 我們先拋出一個(gè)問題:JWT到底是如何認(rèn)證的肴颊?
查看auth-service的代碼氓栈,我們可以發(fā)現(xiàn)jwt token是基于密鑰對(duì)生成的,密鑰對(duì)舉兩個(gè)常用場(chǎng)景:- 加密婿着。像https,客戶端使用公鑰加密對(duì)稱密鑰授瘦,服務(wù)端使用私鑰解密,在接下來的通信中使用對(duì)稱密鑰加密數(shù)據(jù)竟宋。
- 簽名提完。像jwt,jwt三部分的最后一部分Sigature即為Auth-service使用私鑰進(jìn)行的簽名丘侠,web-service在啟動(dòng)的時(shí)候會(huì)自動(dòng)裝配
OAuth2ResourceServerJwtConfiguration
類
Screenshot 2023-04-10 at 00.26.29.png
里面有幾個(gè)conditional的bean注入徒欣,我們看這個(gè),當(dāng)issuerUri存在時(shí)蜗字,會(huì)向auth-service拉取jwks打肝,依據(jù)公鑰生成jwtDecoder,用decoder去解析jwt(上面圖中有)秽澳,這個(gè)過程也附帶了認(rèn)證的過程闯睹,同時(shí)這里會(huì)注冊(cè)兩個(gè)validator,一個(gè)是issuerUri(jwt中的iss和applicaiton.yml配置的)是否一致担神,一個(gè)是驗(yàn)證jwt有效時(shí)間,在JwtValidators.createDefaultWithIssuer()
中有聲明.
附 jwks example
http://127.0.0.1:8081/oauth2/jwks
{
"keys":[
{
"kty":"RSA",
"e":"AQAB",
"kid":"e902868c-50ec-419c-a85a-1e181794577e",
"n":"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw"
}
]
}
- jwtAuthenticationConverter轉(zhuǎn)換
在web-service的WebSecureConfig
中始花,自定義了一個(gè)jwtAuthenticationConverter
Screenshot 2023-04-10 at 00.36.12.png
這個(gè)轉(zhuǎn)換器在JwtAuthenticationProvider
中調(diào)用妄讯,把jwt轉(zhuǎn)換成AbstractAuthenticationToken
(最開始的圖),
converter過程我們簡(jiǎn)單分為兩部分酷宵,一部分是sub轉(zhuǎn)換亥贸,一部分是權(quán)限轉(zhuǎn)換。
默認(rèn)權(quán)限轉(zhuǎn)換時(shí)會(huì)使用jwt中的scp域浇垦,并加上SCOPE_前綴
配置中jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
即為去除SCOPE_前綴
Jwt PreAuth授權(quán)解析
prePostEnabled檢查是基于aop實(shí)現(xiàn)的炕置,配置類為PrePostMethodSecurityConfiguration
, 入口類為AuthorizationManagerBeforeMethodInterceptor
,執(zhí)行attemptAuthorization方法
最后Spring EL表達(dá)式驗(yàn)證,可參考流程圖,不再展開。
SpringSecurity Authorization 解析
SpringSecurity Authorization 是指對(duì)允許訪問的URL的授權(quán)認(rèn)證
即我們?cè)?code>WebSecureConfig中配置的url端點(diǎn)授權(quán)朴摊,基于filterchain,入口為AuthorizationFilter
,具體參考流程圖默垄,不再展開。
如有錯(cuò)誤甚纲,請(qǐng)指正口锭,也歡迎更好的實(shí)現(xiàn)。