自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

決定放棄 JWT 了!

開(kāi)發(fā) 前端
本節(jié)內(nèi)容詳細(xì)介紹了碼猿慢病云管理系統(tǒng)中完整的認(rèn)證登錄生成token的流程,相信你對(duì)整體的流程有了清晰的了解。

JWT相信大家都有所了解,一種無(wú)狀態(tài)的認(rèn)證方式,因?yàn)镴WT本身就能存儲(chǔ)一些非敏感的身份信息,這種方式目前也被廣泛使用,在陳某之前的Spring Cloud Gateway整合Spring Security OAuth2中使用的就是JWT。

但是JWT雖好,使用過(guò)程中還是要依賴緩存,比如退出登錄,JWT唯一的失效途徑就是等待過(guò)期時(shí)間失效,因此在退出登錄時(shí)必須借助外力Redis才能達(dá)到效果。這個(gè)在之前的文章中也有介紹。

既然都要用Redis,為什么不采用Redis+Spring Security+OAuth2的認(rèn)證方式呢?這種方式也是企業(yè)中經(jīng)常采用的方案。

今天就介紹一下碼猿慢病云管理系統(tǒng)中是如何將利用Redis和Spring Security 整合實(shí)現(xiàn)分布式統(tǒng)一認(rèn)證登錄的。

在學(xué)習(xí)這節(jié)內(nèi)容之前先要了解Spring Security OAuth2 各種授權(quán)模式,在知識(shí)星球中《精盡Spring Cloud Alibaba》專欄有詳細(xì)的介紹和案例代碼演示,有需要的先去學(xué)習(xí)。

一、實(shí)現(xiàn)的效果

既然是直接使用Redis+Spring Security,身份信息肯定是存儲(chǔ)在Redis中且token也不是JWT生成的令牌,如下圖:

圖片圖片

可以看到令牌和刷新令牌以及身份信息都存儲(chǔ)在Redis中。其中9d22b664-8540-48d1-98ed-4df1ce90b74f就是生成的令牌,無(wú)任何特殊含義,只是隨機(jī)生成的UUID,相較于JWT短小了很多。

二、登錄的客戶端有哪些?

碼猿慢病云管理系統(tǒng)中需要登錄的客戶端如下:

  • WEB端
  • PDA端
  • PAD端
  • 患者端
  • 小程序

今天先來(lái)介紹前三種,后面的兩種后文介紹。

1. WEB端

登錄頁(yè)面如下:

web端登錄web端登錄

三個(gè)參數(shù):

  1. 用戶名
  2. 密碼
  3. 醫(yī)院ID

請(qǐng)求的報(bào)文如下:

POST /auth/oauth2/token?grant_type=password&scope=server HTTP/1.1
Host: codeape-gateway:9999
Authorization: Basic dGVzdDp0ZXN0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=admin&password=YehdBPev&hosId=1659018792143663105

因?yàn)槭嵌嘧鈶舻哪J?,所以在登錄中做了醫(yī)院的選擇,這點(diǎn)也是對(duì)代碼改造的一部分,下文介紹如何改造。

2. PDA端

PDA是護(hù)士的手持設(shè)備,用于采集數(shù)據(jù),因此也是需要認(rèn)證才能上傳、查看數(shù)據(jù)。

PDA端登錄只需要護(hù)士輸入如下兩個(gè)參數(shù):

  • 用戶名
  • 密碼

為什么呢?不需要選擇醫(yī)院?jiǎn)幔?/p>

前面的文章中也有介紹過(guò),PDA這種手持設(shè)備只有在平臺(tái)上錄入了才能使用,錄入的地方:設(shè)備管理->設(shè)備列表->新增

圖片圖片

設(shè)備SN號(hào)是設(shè)備的唯一識(shí)別號(hào),在設(shè)備取得注冊(cè)證書后頒發(fā)的,所以可以作為唯一識(shí)別標(biāo)志。

這里就是根據(jù)根據(jù)SN號(hào)去唯一關(guān)聯(lián)這臺(tái)設(shè)備,這也就是為什么PDA登錄不用選擇醫(yī)院的原因。

PDA在發(fā)出登錄請(qǐng)求時(shí)只需要攜帶這個(gè)SN號(hào),請(qǐng)求報(bào)文如下:

POST /auth/oauth2/token?grant_type=password&scope=server HTTP/1.1
Host: codeape-gateway:9999
Authorization: Basic dGVzdDp0ZXN0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=admin&password=YehdBPev&sn=3981293B102

3. PAD端

平板一般是醫(yī)生查房時(shí)作為移動(dòng)端使用,住院醫(yī)生每天都需要去病房查看病人病情,需要結(jié)合測(cè)量的數(shù)據(jù)才能了解患者的病情,因此PAD也是需要醫(yī)生認(rèn)證登錄。

PAD端登錄其實(shí)有兩種方案:

  • 和WEB端相同,選擇醫(yī)院登錄
  • 通過(guò)設(shè)備MAC地址綁定登錄

碼猿慢病云管理系統(tǒng)采用的第一種方案,需要選擇醫(yī)院,請(qǐng)求報(bào)文如下:

POST /auth/oauth2/token?grant_type=password&scope=server HTTP/1.1
Host: codeape-gateway:9999
Authorization: Basic dGVzdDp0ZXN0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=admin&password=YehdBPev&hosId=1659018792143663105

三、 密碼模式登錄

上面介紹的WEB端、PDA端、PAD端都是基于密碼模式改造的,在介紹認(rèn)證流程之前需要將登錄接口給導(dǎo)入接口工具,這里使用的是Apifox,下載下方密碼模式腳本,直接導(dǎo)入Apifox。

導(dǎo)入成功后,你將會(huì)得到一個(gè)接口,如下圖:

圖片圖片

點(diǎn)擊運(yùn)行,發(fā)出請(qǐng)求登錄,返回的信息如下圖:

圖片圖片

上述返回信息幾個(gè)比較重要的屬性如下:

1. access_token

這個(gè)則是認(rèn)證成功生成token,后續(xù)請(qǐng)求資源時(shí)只需要攜帶這個(gè)token則能通過(guò)認(rèn)證

PS:這里的token似乎很短小,其實(shí)并不是JWT生成token,而是UUID。

2. refresh_token

這個(gè)是token過(guò)期后的刷新令牌,當(dāng)token過(guò)期后則拿著這個(gè)refresh_token即可重新獲取新的access_token,無(wú)需再次認(rèn)證登錄

3. user_info

這部分是當(dāng)前用戶登錄成功后返回一些個(gè)人信息,比如權(quán)限、醫(yī)院ID、所屬的科室/病區(qū)ID等,詳細(xì)信息如下圖:

  • username:用戶名
  • authorities:權(quán)限
  • id:主鍵ID
  • deptId:科室/病區(qū)ID
  • hosId:醫(yī)院ID
  • deptAuths:科室/病區(qū)權(quán)限
  • roleCodes:角色編碼
  • phone:手機(jī)號(hào)
  • clientId:客戶端ID
  • sn:登錄的PDA的SN號(hào)
  • name:姓名

圖片圖片

4. scope

對(duì)應(yīng)的資源的權(quán)限

四、密碼模式登錄字段加密

密碼模式的登錄有兩個(gè)點(diǎn)比較重要,以WEB端登錄報(bào)文為例:

POST /auth/oauth2/token?grant_type=password&scope=server HTTP/1.1
Host: codeape-gateway:9999
Authorization: Basic dGVzdDp0ZXN0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=admin&password=YehdBPev&hosId=1659018792143663105

從上面的報(bào)文可以看到有兩處進(jìn)行了加密,如下:

  1. Authorization:這里是對(duì)client_id:client_secret,這里采用的是base64編碼,比如WEB端的原始Authorization為:Basic web:web
  2. password:這里也對(duì)密碼進(jìn)行了AES加密處理

五、服務(wù)端認(rèn)證的流程

先上一張整體的流程圖,如下:

圖片圖片

按照Apifox的密碼模式登錄接口發(fā)出登錄請(qǐng)求后,將會(huì)按照上方的流程圖逐一處理,流程解析如下:

1. 網(wǎng)關(guān)前置處理

網(wǎng)關(guān)的前置處理分為兩個(gè)部分:

  • 驗(yàn)證碼校驗(yàn)
  • 密碼解密

這兩個(gè)功能都是使用過(guò)濾器處理的,在網(wǎng)關(guān)的配置文件中可以看到對(duì)認(rèn)證中心codeape-auth配置了兩個(gè)過(guò)濾器,如下:

圖片圖片

關(guān)于網(wǎng)關(guān)的過(guò)濾器不理解的請(qǐng)看知識(shí)星球中《精盡Spring Cloud Alibaba》專欄網(wǎng)關(guān)的部分。

1)驗(yàn)證碼校驗(yàn)

在前面文章中介紹了碼猿慢病云管理系統(tǒng)中是對(duì)WEB端、PDA端、PAD端將驗(yàn)證碼關(guān)閉的,但是對(duì)于院外患者端,比如患者APP端還是需要驗(yàn)證碼的。

驗(yàn)證碼對(duì)應(yīng)的代碼在com.code.ape.codeape.gateway.filter.ValidateCodeGatewayFilter中,里面的邏輯在前文介紹過(guò),這里就不再詳細(xì)說(shuō)了,有一行代碼需要注意一下,代碼如下:

//解析請(qǐng)求頭中的ClientId,和配置文件configProperties中的比較,忽略不需要校驗(yàn)clientId
boolean isIgnoreClient =configProperties.getIgnoreClients().contains(WebUtils.getClientId(request));

為什么需要注意呢?

上文說(shuō)過(guò),客戶端ID和客戶端秘鑰是放在Authorization中經(jīng)過(guò)base64編碼后發(fā)送給服務(wù)端,因此后端取client_id是不是也要經(jīng)過(guò)解碼,WebUtils.getClientId(request)這個(gè)方法就是對(duì)Authorization解碼獲取client_id,代碼如下:

/**
  * 從request 獲取CLIENT_ID
  * com.code.ape.codeape.common.core.util.WebUtils#getClientId
  */
 @SneakyThrows
 public String getClientId(ServerHttpRequest request) {
  String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
  return splitClient(header)[0];
 }

 /**
  * 對(duì)請(qǐng)求頭中的Authorization拆分且解碼
  * com.code.ape.codeape.common.core.util.WebUtils#splitClient
  */
 @NotNull
 private static String[] splitClient(String header) {
  if (header == null || !header.startsWith(BASIC_)) {
   throw new CheckedException("請(qǐng)求頭中client信息為空");
  }
  byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
  byte[] decoded;
  try {
            //解碼
   decoded = Base64.decode(base64Token);
  }
  catch (IllegalArgumentException e) {
   throw new CheckedException("Failed to decode basic authentication token");
  }

  String token = new String(decoded, StandardCharsets.UTF_8);

  int delim = token.indexOf(":");

  if (delim == -1) {
   throw new CheckedException("Invalid basic authentication token");
  }
  return new String[] { token.substring(0, delim), token.substring(delim + 1) };
 }

2) 密碼解密

密碼解密對(duì)應(yīng)的過(guò)濾器:com.code.ape.codeape.gateway.filter.PasswordDecoderFilter,邏輯很簡(jiǎn)單:

  • 校驗(yàn)是否是登錄請(qǐng)求
  • 校驗(yàn)授權(quán)類型,如果是刷新令牌則直接放行
  • 解密

代碼很簡(jiǎn)單,注釋很清楚,這里就不再詳細(xì)貼出來(lái)了。

注意:客戶端和服務(wù)端的加密因子需要保持一致才能正確加解密。

2. OAuth2ClientAuthenticationFilter

這個(gè)過(guò)濾器的作用是用于 OAuth2 的客戶端身份驗(yàn)證,主要用于處理客戶端使用客戶端憑證(client credentials)訪問(wèn)受保護(hù)資源的情況。

整體的邏輯如下圖:

代碼①

這個(gè)很好理解,只有登錄請(qǐng)求/oauth2/token才會(huì)校驗(yàn)客戶端信息,其他的請(qǐng)求直接放行

代碼②

這行代碼是將請(qǐng)求頭中客戶端信息提取出來(lái)轉(zhuǎn)換為Authentication客戶端認(rèn)證對(duì)象,這里用到了認(rèn)證轉(zhuǎn)換器AuthenticationConverter,在該過(guò)濾器構(gòu)造時(shí)默認(rèn)傳入了四個(gè),如下圖:

this.authenticationConverter.convert(request)該方法調(diào)用的是DelegatingAuthenticationConverter#convert方法,內(nèi)部是循環(huán)調(diào)用上述的四個(gè)才轉(zhuǎn)換器,如下:

上述四個(gè)認(rèn)證轉(zhuǎn)換器比較重要的是其中兩個(gè):

1. ClientSecretBasicAuthenticationConverter

這個(gè)是處理將客戶端信息存放在請(qǐng)求頭中轉(zhuǎn)換器,在內(nèi)部對(duì)請(qǐng)求頭中的客戶端信息進(jìn)行base64解碼,具體的代碼邏輯如下:

這個(gè)轉(zhuǎn)換器正好是碼猿慢病云管理系統(tǒng)中的請(qǐng)求方式相匹配,因此走的則是這個(gè)邏輯。

2. ClientSecretPostAuthenticationConverter

這個(gè)轉(zhuǎn)換器是處理POST請(qǐng)求,且客戶端信息通過(guò)Body傳輸?shù)?,里面邏輯也是非常?jiǎn)單,直接從請(qǐng)求參數(shù)中獲取client_id和client_secret,具體的代碼就不帶大家看了,有興趣可以看一下。

代碼③

這里就是執(zhí)行真正的校驗(yàn)邏輯了,內(nèi)部調(diào)用的RegisteredClientRepository#findByClientId()方法校驗(yàn)。

對(duì)應(yīng)的則是整體的流程圖的第②部分,這里調(diào)用的則是自定義的CodeapeRemoteRegisteredClientRepository#findByClientId方法,內(nèi)部邏輯非常簡(jiǎn)單:查詢Redis緩存,存在緩存直接取,不存在則查數(shù)據(jù)庫(kù)codeape/sys_oauth_client_details(通過(guò)feign接口遠(yuǎn)程調(diào)用服務(wù)查詢)。

代碼如下圖:

代碼④

這部分是客戶端認(rèn)證成功的處理邏輯,是將客戶端認(rèn)證的信息存放到SecurityContext上下文中,方便后面流程獲取,代碼OAuth2ClientAuthenticationFilter#onAuthenticationSuccess如下:

代碼⑤

處理客戶端認(rèn)證失敗的結(jié)果,這里最終執(zhí)行的是自定義的失敗處理器CodeapeAuthenticationFailureEventHandler#onAuthenticationFailure(),這個(gè)下文會(huì)介紹。

3. RegisteredClientRepository

這個(gè)是客戶端的持久層查詢的類,在上文已經(jīng)介紹過(guò)

4. OAuth2TokenEndpointFilter

OAuth2TokenEndpointFilter 這個(gè)過(guò)濾器的作用是用于處理 OAuth2 認(rèn)證和授權(quán)請(qǐng)求的。它會(huì)攔截所有請(qǐng)求,并根據(jù)請(qǐng)求的 URI 判斷是否是授權(quán)請(qǐng)求(/oauth2/token)。

如果是授權(quán)請(qǐng)求,則它會(huì)根據(jù)請(qǐng)求的參數(shù)構(gòu)造一個(gè) OAuth2AuthenticationToken 對(duì)象,并將其交給 AuthenticationManager 進(jìn)行身份認(rèn)證。如果認(rèn)證成功,則根據(jù)請(qǐng)求中攜帶的授權(quán)類型(grant_type)決定使用哪個(gè) OAuth2 授權(quán)提供者來(lái)生成授權(quán)令牌(access_token),并將生成的授權(quán)令牌返回給請(qǐng)求方。

如果認(rèn)證失敗,則返回相應(yīng)的錯(cuò)誤信息。該過(guò)濾器通常用于實(shí)現(xiàn) OAuth2 認(rèn)證和授權(quán)功能的后端服務(wù)。

這個(gè)過(guò)濾器才是真正處理登錄請(qǐng)求邏輯

整體的邏輯如下:

5. AuthenticationConverter

這個(gè)在第4步中的第②個(gè)步驟,會(huì)根據(jù)請(qǐng)求中的參數(shù)和授權(quán)類型組裝成對(duì)應(yīng)的授權(quán)認(rèn)證對(duì)象。它的幾個(gè)重要的實(shí)現(xiàn)類如下:

先來(lái)看一下自定義的抽象類:OAuth2ResourceOwnerBaseAuthenticationConverter,三個(gè)抽象方法如下:

  • boolean support(String grantType):判斷是否支持指定的授權(quán)類型
  • void checkParams(HttpServletRequest request):校驗(yàn)請(qǐng)求參數(shù),比如密碼模式下的username、password不能為空,手機(jī)驗(yàn)證碼登錄則手機(jī)號(hào)不能為空都是在這校驗(yàn)
  • T buildToken():這個(gè)是構(gòu)建認(rèn)證登錄對(duì)象的方法

實(shí)現(xiàn)的convert()方法代碼如下:

@Override
 public Authentication convert(HttpServletRequest request) {

  // grant_type (REQUIRED) ① 校驗(yàn)授權(quán)類型,調(diào)用抽象方法support
  String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
  if (!support(grantType)) {
   return null;
  }

  //② 獲取請(qǐng)求參數(shù),比如密碼模式:username、password、hosId,scope...
  MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
  // scope (OPTIONAL)  ③ 提取出scope
  String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
  if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
   OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,
     OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
  }

  Set<String> requestedScopes = null;
  if (StringUtils.hasText(scope)) {
   requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
  }

  // ④ 校驗(yàn)個(gè)性化參數(shù)
  checkParams(request);

  // ⑤ 獲取當(dāng)前已經(jīng)認(rèn)證的客戶端信息,這個(gè)是在OAuth2ClientAuthenticationFilter認(rèn)證成功客戶端認(rèn)證對(duì)象
  Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
  if (clientPrincipal == null) {
   OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.INVALID_CLIENT,
     OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
  }

  // ⑥ 擴(kuò)展信息
  Map<String, Object> additionalParameters = parameters.entrySet()
   .stream()
   .filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE)
     && !e.getKey().equals(OAuth2ParameterNames.SCOPE))
   .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));

  // ⑦ 創(chuàng)建token 調(diào)用抽象方法buildToken()
  return buildToken(clientPrincipal, requestedScopes, additionalParameters);

 }

注釋非常清晰了,這里不再詳細(xì)解釋了。

其中密碼模式認(rèn)證登錄的實(shí)現(xiàn)類是:OAuth2ResourceOwnerPasswordAuthenticationConverter,里面的邏輯非常簡(jiǎn)單,這里不介紹了。

6. AuthenticationToken

`AuthenticationToken`是登錄認(rèn)證對(duì)象,在第4步中的第②步組裝,[碼猿慢病云管理系統(tǒng)](https://mp.weixin.qq.com/s?__biz=MzU3MDAzNDg1MA==&mid=2247526866&idx=1&sn=3820b44ff80c46749efa1a2c0b1f8aa7&chksm=fcf7b61fcb803f090688a542cfb766f5dc06385b5e6e04c2309b4af7b07c07580fb19ffc9d0a&scene=178&cur_album_id=2989600933141807115#rd)中對(duì)其進(jìn)行了擴(kuò)展,有如下三個(gè)類:
  1. OAuth2ResourceOwnerBaseAuthenticationToken:抽象類
  2. OAuth2ResourceOwnerPasswordAuthenticationToken:密碼模式的登錄認(rèn)證對(duì)象
  3. OAuth2ResourceOwnerSmsAuthenticationToken:短信驗(yàn)證碼登錄認(rèn)證對(duì)象

后續(xù)如有其他授權(quán)模式,直接繼承OAuth2ResourceOwnerBaseAuthenticationToken擴(kuò)展

7. AuthenticationProvider

AuthenticationProvider是Spring Security提供的一種機(jī)制,用于接收和驗(yàn)證用戶名和密碼等認(rèn)證信息,并返回一個(gè)已認(rèn)證的Authentication對(duì)象。其作用是封裝了整個(gè)認(rèn)證過(guò)程,包括認(rèn)證用戶的來(lái)源、密碼的加密和解密、對(duì)用戶賬戶狀態(tài)的判斷等。

AuthenticationProvider在第4步中的第③步中被調(diào)用,用于認(rèn)證;碼猿慢病云管理系統(tǒng)中自定義了三個(gè)實(shí)現(xiàn)類,如下:

1)OAuth2ResourceOwnerBaseAuthenticationProvider

抽象類,封裝了具體的執(zhí)行邏輯,有三個(gè)抽象方法供子類實(shí)現(xiàn),如下:

/**
  * 構(gòu)建登錄認(rèn)證對(duì)象
  * @param reqParameters
  * @return
  */
 public abstract UsernamePasswordAuthenticationToken buildToken(Map<String, Object> reqParameters);

 /**
  * 當(dāng)前provider是否支持此令牌類型
  * @param authentication
  * @return
  */
 @Override
 public abstract boolean supports(Class<?> authentication);

 /**
  * 當(dāng)前的請(qǐng)求客戶端是否支持此模式
  * @param registeredClient
  */
 public abstract void checkClient(RegisteredClient registeredClient);

具體的執(zhí)行邏輯都在OAuth2ResourceOwnerBaseAuthenticationProvider#authenticate()方法中,關(guān)鍵邏輯如下:

//① 構(gòu)建登錄認(rèn)證對(duì)象,交由子類實(shí)現(xiàn)
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = buildToken(reqParameters);

//② 交由Spring Security 認(rèn)證
Authentication usernamePasswordAuthentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

// ----- Access token ----- ③ 構(gòu)建Access token
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);

// ----- Refresh token -----  ④ 認(rèn)證成功后,構(gòu)建刷新令牌
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);

//⑤ 存儲(chǔ)令牌
this.authorizationService.save(authorization);

代碼①:構(gòu)建認(rèn)證登錄對(duì)象,提供了一個(gè)buildToken抽象方法交由子類實(shí)現(xiàn)

剩余代碼下文介紹

2)OAuth2ResourceOwnerPasswordAuthenticationProvider

密碼模式的AuthenticationProvider,繼承抽象類OAuth2ResourceOwnerBaseAuthenticationProvider實(shí)現(xiàn)三個(gè)抽象方法,邏輯很簡(jiǎn)單。

3)OAuth2ResourceOwnerSmsAuthenticationProvider

短信驗(yàn)證碼登錄模式的AuthenticationProvider,繼承抽象類OAuth2ResourceOwnerBaseAuthenticationProvider實(shí)現(xiàn)三個(gè)抽象方法。

8. DaoAuthenticationProvider

從DaoAuthenticationProvider這里就進(jìn)入真正的認(rèn)證邏輯了,從名字就可以看出涉及到數(shù)據(jù)庫(kù)的操作了。內(nèi)部的邏輯很簡(jiǎn)單,就是通過(guò)UserDetailService調(diào)用查詢用戶信息封裝成UserDetails

在第7步中的第②步驟中則會(huì)進(jìn)入:

//② 交由Spring Security 認(rèn)證
Authentication usernamePasswordAuthentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

碼猿慢病云管理系統(tǒng)中自定義了一個(gè)CodeapeDaoAuthenticationProvider,執(zhí)行的邏輯將會(huì)在這個(gè)類中,先看下其中重載的兩個(gè)重要的方法:

//方法一:feign遠(yuǎn)程調(diào)用根據(jù)username查詢用戶信息,組裝成UserDetails
UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication);

//方法二:校驗(yàn)用戶信息、密碼
void additionalAuthenticationChecks(UserDetails userDetails,
   UsernamePasswordAuthenticationToken authentication);

1. retrieveUser 查詢用戶信息

retrieveUser這個(gè)方法邏輯很簡(jiǎn)單,則是調(diào)用UserDetailService查詢用戶信息,邏輯如下:

圖片圖片

代碼①

從Request中獲取相關(guān)參數(shù):

  1. clientId:客戶端ID,由于是base64編碼傳輸,因此需要調(diào)用的convert方法解碼
  2. hosId:醫(yī)院ID,WEB、PAD登錄所需參數(shù)
  3. sn:設(shè)備的唯一識(shí)別SN號(hào),用于PDA登錄

代碼②

從IOC容器中獲取UserDetailSevice,碼猿慢病云管理系統(tǒng)中目前實(shí)現(xiàn)類有三個(gè):

  1. CodeapeAppUserDetailsServiceImpl:處理APP端的手機(jī)號(hào)登錄
  2. CodeapePDAUserDetailsServiceImpl:處理PDA端登錄
  3. CodeapeUserDetailsServiceImpl:處理PAD端和WEB端登錄

代碼③

調(diào)用UserDetailService中的loadUserByUsernameAndOther方法獲取UserDetails

2. additionalAuthenticationChecks 密碼校驗(yàn)

這個(gè)方法核心邏輯則是校驗(yàn)密碼,碼猿慢病云管理系統(tǒng)中的密碼校驗(yàn)是通過(guò)PasswordEncoder加密。

圖片圖片

3. 用戶狀態(tài)校驗(yàn)

核心邏輯在:AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks#check方法中,代碼如下:

圖片圖片

九、 UserDetailService

在第8步中說(shuō)到查詢用戶信息是通過(guò)UserDetailService查詢,碼猿慢病云管理系統(tǒng)中目前內(nèi)置三個(gè)實(shí)現(xiàn)類:

  1. CodeapeAppUserDetailsServiceImpl:處理APP端的手機(jī)號(hào)登錄
  2. CodeapePDAUserDetailsServiceImpl:處理PDA端登錄
  3. CodeapeUserDetailsServiceImpl:處理PAD端和WEB端登錄

這里都是通過(guò)feign調(diào)用解耦,當(dāng)然你也可以在auth模塊嵌入數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)查詢

這里調(diào)用的方法是loadUserByUsernameAndOther,比如CodeapeUserDetailsServiceImpl實(shí)現(xiàn)如下:

圖片圖片

最終的組裝UserDetails通過(guò)getUserDetails方法,如下:

圖片圖片

需要注意的是:碼猿慢病云管理系統(tǒng)中的用戶信息是封裝在CodeapeUser中,方便后續(xù)擴(kuò)展,其中的屬性如下:

圖片圖片

可以看到這里和登錄返回的信息中user_info是對(duì)應(yīng)的:

圖片圖片

十. 生成OAuth2AccessToken

在第7步中的第③步中生成access_token,自定義的實(shí)現(xiàn)類為:CustomeOAuth2AccessTokenGenerator

圖片圖片

十一. OAuth2AuthorizationService 令牌持久化

在第7步中的第⑤步驟中執(zhí)行了令牌的持久化,Spring Security 默認(rèn)支持兩種持久化方式:

  1. InMemoryOAuth2AuthorizationService:持久化在內(nèi)存中
  2. JdbcOAuth2AuthorizationService:持久化在數(shù)據(jù)庫(kù)中

碼猿慢病云管理系統(tǒng)中擴(kuò)展了Redis中持久化,自定義的實(shí)現(xiàn)類:CodeapeRedisOAuth2AuthorizationService

圖片圖片

持久化成功后將會(huì)在Redis中看到對(duì)應(yīng)的信息:

圖片圖片

十二. AuthenticationSuccessHandler 登錄成功處理

在第4步中的第④步驟中認(rèn)證成功,則調(diào)用AuthenticationSuccessHandler 處理登錄成功的邏輯,將認(rèn)證信息輸出返回給客戶端。

碼猿慢病云管理系統(tǒng)中自定義類:CodeapeAuthenticationSuccessEventHandler

圖片圖片

總結(jié)

本節(jié)內(nèi)容詳細(xì)介紹了碼猿慢病云管理系統(tǒng)中完整的認(rèn)證登錄生成token的流程,相信你對(duì)整體的流程有了清晰的了解。

責(zé)任編輯:武曉燕 來(lái)源: 碼猿技術(shù)專欄
相關(guān)推薦

2023-10-10 19:25:44

VSCodePython網(wǎng)站

2010-02-22 13:01:54

HTML 5谷歌

2010-01-15 10:36:59

MozillaFirefox 3.7

2022-02-19 23:40:31

iOS蘋果Face ID

2024-10-17 14:14:29

2022-12-19 07:38:02

Rust項(xiàng)目代碼

2020-08-18 10:35:18

JWTredis認(rèn)證

2024-06-24 07:58:00

2023-07-23 17:19:34

人工智能系統(tǒng)

2015-12-03 15:33:06

2017-08-16 09:55:36

2020-07-07 09:19:01

LombokJava IDE

2023-02-27 16:24:17

架構(gòu)開(kāi)發(fā)數(shù)字化

2023-02-08 07:05:44

2023-10-20 17:37:38

MariaDB數(shù)據(jù)庫(kù)MySQL

2017-10-31 08:43:14

2012-05-27 20:47:16

Cius

2013-09-11 09:57:41

蘋果iPhone5S售價(jià)iPhone5C售價(jià)

2013-05-24 15:24:31

戴爾OpenStackVMware

2018-12-21 11:26:49

MySQLMongoDB數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)