一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(二)

 WindySky 2017-11-23


引言: 本文系《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)》系列的第二篇,本文重點(diǎn)講解用戶(hù)身份的認(rèn)證與token發(fā)放的具體實(shí)現(xiàn)。


1. 系統(tǒng)概覽


在上一篇《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(一)》介紹了該項(xiàng)目的背景以及技術(shù)調(diào)研與最后選型,并且對(duì)于最終實(shí)現(xiàn)的Endpoint執(zhí)行結(jié)果進(jìn)行展示。對(duì)系統(tǒng)架構(gòu)雖然有提到,但是并未列出詳細(xì)流程圖。在筆者的應(yīng)用場(chǎng)景中,Auth系統(tǒng)與網(wǎng)關(guān)進(jìn)行結(jié)合。在網(wǎng)關(guān)出配置相應(yīng)的端點(diǎn)信息,如登錄系統(tǒng)申請(qǐng)token授權(quán),校驗(yàn)check_token等端點(diǎn)。


下圖為網(wǎng)關(guān)與Auth系統(tǒng)結(jié)合的流程圖,網(wǎng)關(guān)系統(tǒng)的具體實(shí)現(xiàn)細(xì)節(jié)在后面另寫(xiě)文章介紹。(此處流程圖的繪制中,筆者使用極簡(jiǎn)的語(yǔ)言描述,各位同學(xué)輕噴?。?/p>



授權(quán)流程圖


上圖展示了系統(tǒng)登錄的簡(jiǎn)單流程,其中的細(xì)節(jié)有省略,用戶(hù)信息的合法性校驗(yàn)實(shí)際是調(diào)用用戶(hù)系統(tǒng)。大體流程是這樣,客戶(hù)端請(qǐng)求到達(dá)網(wǎng)關(guān)之后,根據(jù)網(wǎng)關(guān)識(shí)別的請(qǐng)求登錄端點(diǎn),轉(zhuǎn)發(fā)到Auth系統(tǒng),將用戶(hù)的信息進(jìn)行校驗(yàn)。


另一方面是對(duì)于一般請(qǐng)求的校驗(yàn)。一些不需要權(quán)限的公開(kāi)接口,在網(wǎng)關(guān)處配置好,請(qǐng)求到達(dá)網(wǎng)關(guān)后,匹配了路徑將會(huì)直接放行。如果需要對(duì)該請(qǐng)求進(jìn)行校驗(yàn),會(huì)將該請(qǐng)求的相關(guān)驗(yàn)證信息截取,以及API權(quán)限校驗(yàn)所需的上下文信息(筆者項(xiàng)目對(duì)于一些操作進(jìn)行權(quán)限前置驗(yàn)證,下一盤(pán)文章會(huì)講到),調(diào)用Auth系統(tǒng),校驗(yàn)成功后進(jìn)行路由轉(zhuǎn)發(fā)。



身份及API權(quán)限校驗(yàn)的流程圖


這篇文章就重點(diǎn)講解我們?cè)诘谝黄恼轮刑岬降挠脩?hù)身份的認(rèn)證與token發(fā)放。這個(gè)也主要包含兩個(gè)方面:


  • 用戶(hù)合法性的認(rèn)證

  • 獲取到授權(quán)的token


2. 配置與類(lèi)圖


2.1 AuthorizationServer主要配置


關(guān)于AuthorizationServer和ResourceServer的配置在上一篇文章已經(jīng)列出。AuthorizationServer主要是繼承了AuthorizationServerConfigurerAdapter,覆寫(xiě)了其實(shí)現(xiàn)接口的三個(gè)方法:


//對(duì)應(yīng)于配置AuthorizationServer安全認(rèn)證的相關(guān)信息,創(chuàng)建ClientCredentialsTokenEndpointFilter核心過(guò)濾器
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 
}
//配置OAuth2的客戶(hù)端相關(guān)信息
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
//配置身份認(rèn)證器,配置認(rèn)證方式,TokenStore,TokenGranter,OAuth2RequestFactory
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}


2.2 主要Authentication類(lèi)的類(lèi)圖



AuthorizationServer UML類(lèi)圖


主要的驗(yàn)證方法authenticate(Authentication authentication)在接口AuthenticationManager中,其實(shí)現(xiàn)類(lèi)有ProviderManager,有上圖可以看出ProviderManager又依賴(lài)于AuthenticationProvider接口,其定義了一個(gè)List<AuthenticationProvider>全局變量。筆者這邊實(shí)現(xiàn)了該接口的實(shí)現(xiàn)類(lèi)CustomAuthenticationProvider。自定義一個(gè)provider,并在GlobalAuthenticationConfigurerAdapter中配置好改自定義的校驗(yàn)provider,覆寫(xiě)configure()方法。


@Configuration
public class AuthenticationManagerConfig extends GlobalAuthenticationConfigurerAdapter {
@Autowired
CustomAuthenticationProvider customAuthenticationProvider;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(customAuthenticationProvider);//使用自定義的AuthenticationProvider
}
}


AuthenticationManagerBuilder是用來(lái)創(chuàng)建AuthenticationManager,允許自定義提供多種方式的AuthenticationProvider,比如LDAP、基于JDBC等等。


3. 認(rèn)證與授權(quán)token


下面講解認(rèn)證與授權(quán)token主要的類(lèi)與接口。


3.1 自定義的驗(yàn)證類(lèi)CustomAuthenticationProvider


CustomAuthenticationProvider中定義了驗(yàn)證方法的具體實(shí)現(xiàn)。其具體實(shí)現(xiàn)如下所示。


//主要的自定義驗(yàn)證方法
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
   String username = authentication.getName();
   String password = (String) authentication.getCredentials();
   Map data = (Map) authentication.getDetails();
   String clientId = (String) data.get("client");
   Assert.hasText(clientId,"clientId must have value" );
   String type = (String) data.get("type");
   //通過(guò)調(diào)用user服務(wù),校驗(yàn)用戶(hù)信息
   Map map = userClient.checkUsernameAndPassword(getUserServicePostObject(username, password, type));
    //校驗(yàn)返回的信息,不正確則拋出異常,授權(quán)失敗
   String userId = (String) map.get("userId");
   if (StringUtils.isBlank(userId)) {
       String errorCode = (String) map.get("code");
       throw new BadCredentialsException(errorCode);
   }
   CustomUserDetails customUserDetails = buildCustomUserDetails(username, password, userId, clientId);
   return new CustomAuthenticationToken(customUserDetails);
}
//構(gòu)造一個(gè)CustomUserDetails,簡(jiǎn)單,略去
private CustomUserDetails buildCustomUserDetails(String username, String password, String userId, String clientId) {
}
//構(gòu)造一個(gè)請(qǐng)求userService的map,內(nèi)容略
private Map<String, String> getUserServicePostObject(String username, String password, String type) {
}


authenticate()最后返回構(gòu)造的自定義CustomAuthenticationToken,在CustomAuthenticationToken中,將boolean authenticated設(shè)為true,user信息驗(yàn)證成功。這邊傳入的參數(shù)CustomUserDetails與token生成有關(guān),作為payload中的信息,下面會(huì)講到。


//繼承抽象類(lèi)AbstractAuthenticationToken
public class CustomAuthenticationToken extends AbstractAuthenticationToken {
private CustomUserDetails userDetails;
public CustomAuthenticationToken(CustomUserDetails userDetails) {
    super(null);
    this.userDetails = userDetails;
    super.setAuthenticated(true);
}
...
}


而AbstractAuthenticationToken實(shí)現(xiàn)了接口Authentication和CredentialsContainer,里面的具體信息讀者可以自己看下源碼。


3.2 關(guān)于JWT


用戶(hù)信息校驗(yàn)完成之后,下一步則是要對(duì)該用戶(hù)進(jìn)行授權(quán)。在講具體的授權(quán)之前,先補(bǔ)充下關(guān)于JWT Token的相關(guān)知識(shí)點(diǎn)。


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


從上面的描述可知JWT的定義,這邊讀者可以對(duì)比下token的認(rèn)證和傳統(tǒng)的session認(rèn)證的區(qū)別。推薦一篇文章《什么是 JWT – JSON WEB TOKEN》,筆者這邊就不詳細(xì)擴(kuò)展講了,只是簡(jiǎn)單介紹下其構(gòu)成。


JWT包含三部分:header頭部、payload信息、signature簽名。下面以上一篇生成好的access_token為例介紹。


header


JWT的頭部承載兩部分信息,一是聲明類(lèi)型,這里是JWT;二是聲明加密的算法 通常直接使用HMAC SHA256。第一部分一般固定為:


eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9


playload


存放的有效信息,這些有效信息包含三個(gè)部分、標(biāo)準(zhǔn)中注冊(cè)的聲明、公共的聲明、私有的聲明。這邊筆者額外添加的信息為X-KEETS-UserId和X-KEETS-ClientId。讀者可根據(jù)實(shí)際項(xiàng)目需要進(jìn)行定制。最后playload經(jīng)過(guò)base64編碼后的結(jié)果為:


eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsImV4cCI6MTUwODQ0Nzc1NiwidXNlcl9uYW1lIjoia2VldHMiLCJqdGkiOiJiYWQ3MmIxOS1kOWYzLTQ5MDItYWZmYS0wNDMwZTdkYjc5ZWQiLCJjbGllbnRfaWQiOiJmcm9udGVuZCIsInNjb3BlIjpbImFsbCJdfQ


signature


JWT的第三部分是一個(gè)簽證信息,這個(gè)簽證信息由三部分組成:header(base64后的)、payload(base64后的)、secret。


關(guān)于secret,細(xì)心的讀者可能會(huì)發(fā)現(xiàn)之前的配置里面有具體設(shè)置。前兩部分連接組成的字符串,通過(guò)header中聲明的加密方式進(jìn)行加鹽secret組合加密,然后就構(gòu)成了JWT的第三部分。第三部分結(jié)果為:


5ZNVN8TLavgpWy8KZQKArcbj7ItJLLaY1zBRaAgMjdo


至于具體應(yīng)用方法,可以參見(jiàn)第一篇文章中構(gòu)建的/logout端點(diǎn)。


3.3 自定義的AuthorizationTokenServices


現(xiàn)在到了為用戶(hù)創(chuàng)建token,這邊主要與自定義的接口AuthorizationServerTokenServices有關(guān)。AuthorizationServerTokenServices主要有如下三個(gè)方法:


//創(chuàng)建token
OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException;
//刷新token
OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
    throws AuthenticationException;
//獲取token
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication)


由于篇幅限制,筆者這邊僅對(duì)createAccessToken()的實(shí)現(xiàn)方法進(jìn)行分析,其他的方法實(shí)現(xiàn),讀者可以下關(guān)注筆者的GitHub項(xiàng)目。


public class CustomAuthorizationTokenServices implements AuthorizationServerTokenServices, ConsumerTokenServices {
...

public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
    //通過(guò)TokenStore,獲取現(xiàn)存的AccessToken
    OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
    OAuth2RefreshToken refreshToken;
    //移除已有的AccessToken和refreshToken
    if (existingAccessToken != null) {
        if (existingAccessToken.getRefreshToken() != null) {
            refreshToken = existingAccessToken.getRefreshToken();
            // The token store could remove the refresh token when the
                // access token is removed, but we want to be sure
            tokenStore.removeRefreshToken(refreshToken);
        }
        tokenStore.removeAccessToken(existingAccessToken);
    }
    //recreate a refreshToken
    refreshToken = createRefreshToken(authentication);
    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    if (accessToken != null) {
        tokenStore.storeAccessToken(accessToken, authentication);
    }
    refreshToken = accessToken.getRefreshToken();
    if (refreshToken != null) {
        tokenStore.storeRefreshToken(refreshToken, authentication);
    }
    return accessToken;
}
...
}


這邊具體的實(shí)現(xiàn)在上面有注釋?zhuān)緵](méi)有改寫(xiě)多少,讀者此處可以參閱源碼。createAccessToken()還調(diào)用了兩個(gè)私有方法,分別創(chuàng)建accessToken和refreshToken。創(chuàng)建accessToken,需要基于refreshToken。


此處可以自定義設(shè)置token的時(shí)效長(zhǎng)度,accessToken創(chuàng)建實(shí)現(xiàn)如下:


private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.

private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
//對(duì)應(yīng)tokenId,存儲(chǔ)的標(biāo)識(shí)
  DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
  int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
  if (validitySeconds > 0) {
      token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
  }
  token.setRefreshToken(refreshToken);
  //scope對(duì)應(yīng)作用范圍
  token.setScope(authentication.getOAuth2Request().getScope());
//上一節(jié)介紹的自定義TokenEnhancer,這邊使用
  return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}


既然提到TokenEnhancer,這邊簡(jiǎn)單貼一下代碼。


public class CustomTokenEnhancer extends JwtAccessTokenConverter {
private static final String TOKEN_SEG_USER_ID = "X-KEETS-UserId";
private static final String TOKEN_SEG_CLIENT = "X-KEETS-ClientId";
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
                                 OAuth2Authentication authentication) {
    CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
    Map<String, Object> info = new HashMap<>();
    //從自定義的userDetails中取出UserId
    info.put(TOKEN_SEG_USER_ID, userDetails.getUserId());
    DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
    customAccessToken.setAdditionalInformation(info);
    OAuth2AccessToken enhancedToken = super.enhance(customAccessToken, authentication);
    //設(shè)置ClientId
    enhancedToken.getAdditionalInformation().put(TOKEN_SEG_CLIENT, userDetails.getClientId());
    return enhancedToken;
}
}


自此,用戶(hù)身份校驗(yàn)與發(fā)放授權(quán)token結(jié)束。最終成功返回的結(jié)果為:


{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsImV4cCI6MTUwODQ0Nzc1NiwidXNlcl9uYW1lIjoia2VldHMiLCJqdGkiOiJiYWQ3MmIxOS1kOWYzLTQ5MDItYWZmYS0wNDMwZTdkYjc5ZWQiLCJjbGllbnRfaWQiOiJmcm9udGVuZCIsInNjb3BlIjpbImFsbCJdfQ.5ZNVN8TLavgpWy8KZQKArcbj7ItJLLaY1zBRaAgMjdo",   
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsInVzZXJfbmFtZSI6ImtlZXRzIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImJhZDcyYjE5LWQ5ZjMtNDkwMi1hZmZhLTA0MzBlN2RiNzllZCIsImV4cCI6MTUxMDk5NjU1NiwianRpIjoiYWE0MWY1MjctODE3YS00N2UyLWFhOTgtZjNlMDZmNmY0NTZlIiwiY2xpZW50X2lkIjoiZnJvbnRlbmQifQ.mICT1-lxOAqOU9M-Ud7wZBb4tTux6OQWouQJ2nn1DeE",
"expires_in": 43195,
"scope": "all",
"X-KEETS-UserId": "d6448c24-3c4c-4b80-8372-c2d61868f8c6",
"jti": "bad72b19-d9f3-4902-affa-0430e7db79ed",
"X-KEETS-ClientId": "frontend"
}


4. 總結(jié)


本文開(kāi)頭給出了Auth系統(tǒng)概述,畫(huà)出了簡(jiǎn)要的登錄和校驗(yàn)的流程圖,方便讀者能對(duì)系統(tǒng)的實(shí)現(xiàn)有個(gè)大概的了解。然后主要講解了用戶(hù)身份的認(rèn)證與token發(fā)放的具體實(shí)現(xiàn)。對(duì)于其中主要的類(lèi)和接口進(jìn)行了分析與講解。下一篇文章主要講解token的鑒定和API級(jí)別的上下文權(quán)限校驗(yàn)。


本文的源碼地址:


  • GitHub:https://github.com/keets2012/Auth-service

  • 碼云: 

    https:///keets/Auth-Service

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    国产不卡一区二区四区| 亚洲高清中文字幕一区二区三区| 久久99精品日韩人妻| 色婷婷亚洲精品综合网| 99久久精品国产日本| 欧美一级不卡视频在线观看| 国产精品成人一区二区三区夜夜夜 | 久草国产精品一区二区| 欧美午夜国产在线观看| 国产精品成人免费精品自在线观看| 激情内射日本一区二区三区| 欧美黑人在线一区二区| 又黄又色又爽又免费的视频| 人妻中文一区二区三区| 日韩一区二区三区在线欧洲| 国产成人av在线免播放观看av| 国产又黄又爽又粗视频在线| 久久国产青偷人人妻潘金莲| 国产精品视频一区麻豆专区| 日本99精品在线观看| 91爽人人爽人人插人人爽| 人妻一区二区三区在线| 成年人黄片大全在线观看| 成人综合网视频在线观看| 日本精品啪啪一区二区三区| 免费人妻精品一区二区三区久久久| 久久精品国产亚洲av久按摩| 中文字幕久热精品视频在线| 国产午夜精品久久福利| 欧美日韩校园春色激情偷拍 | 国产精品偷拍一区二区| 欧美极品欧美精品欧美| 婷婷基地五月激情五月| 国产男女激情在线视频| 久久碰国产一区二区三区| 男女激情视频在线免费观看| 视频一区二区黄色线观看| 欧美日韩国产福利在线观看| 日本大学生精油按摩在线观看| 日韩精品人妻少妇一区二区| 亚洲成人黄色一级大片|