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

作者: [Aoho's Blog]

引言: 本文系《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)》系列的第二篇,本文重點(diǎn)講解用戶身份的認(rèn)證與token發(fā)放的具體實(shí)現(xiàn)乾闰。本文篇幅較長色罚,對涉及到的大部分代碼進(jìn)行了分析,可收藏于閑暇時(shí)間閱讀,歡迎訂閱本系列文章益咬。

1. 系統(tǒng)概覽

在上一篇 認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(一)介紹了該項(xiàng)目的背景以及技術(shù)調(diào)研與最后選型,并且對于最終實(shí)現(xiàn)的endpoint執(zhí)行結(jié)果進(jìn)行展示帜平。對系統(tǒng)架構(gòu)雖然有提到幽告,但是并未列出詳細(xì)流程圖。在筆者的應(yīng)用場景中裆甩,Auth系統(tǒng)與網(wǎng)關(guān)進(jìn)行結(jié)合冗锁。在網(wǎng)關(guān)出配置相應(yīng)的端點(diǎn)信息,如登錄系統(tǒ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é)在后面另寫文章介紹茉帅。(此處流程圖的繪制中叨叙,筆者使用極簡的語言描述,各位同學(xué)輕噴???芭臁)

[
logi](http://upload-images.jianshu.io/upload_images/2830896-a7973571efe486ed.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
logi](http://upload-images.jianshu.io/upload_images/2830896-a7973571efe486ed.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

授權(quán)流程圖

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

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

[
g](http://upload-images.jianshu.io/upload_images/2830896-a126993168419ec0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
g](http://upload-images.jianshu.io/upload_images/2830896-a126993168419ec0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

這篇文章就重點(diǎn)講解我們在第一篇文章中提到的用戶身份的認(rèn)證與token發(fā)放奸笤。這個(gè)也主要包含兩個(gè)方面:

  • 用戶合法性的認(rèn)證
  • 獲取到授權(quán)的token

2. 配置與類圖

2.1 AuthorizationServer主要配置

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

//對應(yīng)于配置AuthorizationServer安全認(rèn)證的相關(guān)信息监右,創(chuàng)建ClientCredentialsTokenEndpointFilter核心過濾器
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 
}

//配置OAuth2的客戶端相關(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類的類圖

[
aut](http://upload-images.jianshu.io/upload_images/2830896-500b87ba35b5f982.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
aut](http://upload-images.jianshu.io/upload_images/2830896-500b87ba35b5f982.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

AuthorizationServer UML類圖

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

@Configuration
public class AuthenticationManagerConfig extends GlobalAuthenticationConfigurerAdapter {

    @Autowired
    CustomAuthenticationProvider customAuthenticationProvider;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);//使用自定義的AuthenticationProvider
    }

}

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

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

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

3.1 內(nèi)置端點(diǎn)TokenEndpoint

Spring-Security-Oauth2的提供的jar包中內(nèi)置了與token相關(guān)的基礎(chǔ)端點(diǎn)膳殷。本文認(rèn)證與授權(quán)token與/oauth/token有關(guān),其處理的接口類為TokenEndpoint九火。下面我們來看一下對于認(rèn)證與授權(quán)token流程的具體處理過程赚窃。

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {
    ... 
    @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
    public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
    Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
    //首先對client信息進(jìn)行校驗(yàn)
        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException(
                    "There is no client authentication. Try adding an appropriate authentication filter.");
        }

        String clientId = getClientId(principal);
        //根據(jù)請求中的clientId,加載client的具體信息
        ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

        TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

        ... 
        
        //驗(yàn)證scope域范圍
        if (authenticatedClient != null) {
            oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
        }
        //授權(quán)方式不能為空
        if (!StringUtils.hasText(tokenRequest.getGrantType())) {
            throw new InvalidRequestException("Missing grant type");
        }
        //token endpoint不支持Implicit模式
        if (tokenRequest.getGrantType().equals("implicit")) {
            throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
        }
        ...
        
        //進(jìn)入CompositeTokenGranter岔激,匹配授權(quán)模式勒极,然后進(jìn)行password模式的身份驗(yàn)證和token的發(fā)放
        OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
        if (token == null) {
            throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
        }

        return getResponse(token);

    }
    ...
}

[
clien](http://upload-images.jianshu.io/upload_images/2830896-490b18a359766189.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
clien](http://upload-images.jianshu.io/upload_images/2830896-490b18a359766189.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

endpoint

上面給代碼進(jìn)行了注釋,讀者感興趣可以看看虑鼎。接口處理的主要流程就是對authentication信息進(jìn)行檢查是否合法辱匿,不合法直接拋出異常,然后對請求的GrantType進(jìn)行處理炫彩,根據(jù)GrantType匾七,進(jìn)行password模式的身份驗(yàn)證和token的發(fā)放。下面我們來看下TokenGranter的類圖江兢。

[
grante](http://upload-images.jianshu.io/upload_images/2830896-b08e9f39dd2ef345.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
grante](http://upload-images.jianshu.io/upload_images/2830896-b08e9f39dd2ef345.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

TokenGranter

可以看出TokenGranter的實(shí)現(xiàn)類CompositeTokenGranter中有一個(gè)List昨忆,對應(yīng)五種GrantType的實(shí)際授權(quán)實(shí)現(xiàn)。這邊涉及到的getTokenGranter()杉允,代碼也列下:

public class CompositeTokenGranter implements TokenGranter {

    //GrantType的集合邑贴,有五種席里,之前有講
    private final List<TokenGranter> tokenGranters;

    public CompositeTokenGranter(List<TokenGranter> tokenGranters) {
        this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);
    }
    
    //遍歷list,匹配到相應(yīng)的grantType就進(jìn)行處理
    public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
        for (TokenGranter granter : tokenGranters) {
            OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
            if (grant!=null) {
                return grant;
            }
        }
        return null;
    }
    ...
}

本次請求是使用的password模式拢驾,隨后進(jìn)入其GrantType具體的處理流程奖磁,下面是grant()方法。

public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {

    if (!this.grantType.equals(grantType)) {
        return null;
    }
    
    String clientId = tokenRequest.getClientId();
    //加載clientId對應(yīng)的ClientDetails独旷,為了下一步的驗(yàn)證
    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
    //再次驗(yàn)證clientId是否擁有該grantType模式署穗,安全
    validateGrantType(grantType, client);
    //獲取token
    return getAccessToken(client, tokenRequest);

}

protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
//進(jìn)入創(chuàng)建token之前寥裂,進(jìn)行身份驗(yàn)證
    return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
}

protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
//身份驗(yàn)證
    OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(client, tokenRequest);
    return new OAuth2Authentication(storedOAuth2Request, null);
}

上面一段代碼是grant()方法具體的實(shí)現(xiàn)細(xì)節(jié)嵌洼。GrantType匹配到其對應(yīng)的grant()后,先進(jìn)行基本的驗(yàn)證確保安全封恰,然后進(jìn)入主流程麻养,就是下面小節(jié)要講的驗(yàn)證身份和發(fā)放token。

3.2 自定義的驗(yàn)證類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");
       //通過調(diào)用user服務(wù),校驗(yàn)用戶信息
       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,簡單褥赊,略去
   private CustomUserDetails buildCustomUserDetails(String username, String password, String userId, String clientId) {
   }
//構(gòu)造一個(gè)請求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ì)講到。

//繼承抽象類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.3 關(guān)于JWT

用戶信息校驗(yàn)完成之后,下一步則是要對該用戶進(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的開放標(biāo)準(zhǔn)(RFC 7519)回窘。該token被設(shè)計(jì)為緊湊且安全的诺擅,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息啡直,以便于從資源服務(wù)器獲取資源烁涌,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息苍碟,該token也可直接被用于認(rèn)證,也可被加密撮执。

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

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

  • header

    header
    jwt的頭部承載兩部分信息早芭,一是聲明類型,這里是jwt诅蝶;二是聲明加密的算法 通常直接使用 HMAC SHA256退个。第一部分一般固定為:

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    
  • playload

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

    eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsImV4cCI6MTUwODQ0Nzc1NiwidXNlcl9uYW1lIjoia2VldHMiLCJqdGkiOiJiYWQ3MmIxOS1kOWYzLTQ5MDItYWZmYS0wNDMwZTdkYjc5ZWQiLCJjbGllbnRfaWQiOiJmcm9udGVuZCIsInNjb3BlIjpbImFsbCJdfQ
    
  • signature

    signature
    jwt的第三部分是一個(gè)簽證信息匀谣,這個(gè)簽證信息由三部分組成:header (base64后的)照棋、payload (base64后的)、secret武翎。

    signature
    jwt的第三部分是一個(gè)簽證信息烈炭,這個(gè)簽證信息由三部分組成:header (base64后的)、payload (base64后的)宝恶、secret符隙。
    關(guān)于secret,細(xì)心的讀者可能會(huì)發(fā)現(xiàn)之前的配置里面有具體設(shè)置垫毙。前兩部分連接組成的字符串霹疫,通過header中聲明的加密方式進(jìn)行加鹽secret組合加密,然后就構(gòu)成了jwt的第三部分综芥。第三部分結(jié)果為:

    5ZNVN8TLavgpWy8KZQKArcbj7ItJLLaY1zBRaAgMjdo
    

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

3.3 自定義的AuthorizationTokenServices

現(xiàn)在到了為用戶創(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);

由于篇幅限制红省,筆者這邊僅對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 {
        //通過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)在上面有注釋麻诀,基本沒有改寫多少痕寓,讀者此處可以參閱源碼。createAccessToken()還調(diào)用了兩個(gè)私有方法蝇闭,分別創(chuàng)建accessToken和refreshToken呻率。創(chuàng)建accessToken,需要基于refreshToken丁眼。

這邊具體的實(shí)現(xiàn)在上面有注釋筷凤,基本沒有改寫多少,讀者此處可以參閱源碼苞七。createAccessToken()還調(diào)用了兩個(gè)私有方法,分別創(chuàng)建accessToken和refreshToken挪丢。創(chuàng)建accessToken蹂风,需要基于refreshToken。
此處可以自定義設(shè)置token的時(shí)效長度乾蓬,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) {
  //對應(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對應(yīng)作用范圍
      token.setScope(authentication.getOAuth2Request().getScope());
//上一節(jié)介紹的自定義TokenEnhancer,這邊使用
      return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
  }

既然提到TokenEnhancer任内,這邊簡單貼一下代碼撵渡。

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;
    }

}

自此,用戶身份校驗(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é)

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


參考

  1. 什么是 JWT – JSON WEB TOKEN
  2. Re:從零開始的Spring Security OAuth2(二)
  3. spring-security-oauth

相關(guān)閱讀

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

我的官網(wǎng)


我的博客

我的官網(wǎng)http://guan2ye.com
我的CSDN地址http://blog.csdn.net/chenjianandiyi
我的簡書地址http://www.reibang.com/u/9b5d1921ce34
我的githubhttps://github.com/javanan
我的碼云地址https://gitee.com/jamen/
阿里云優(yōu)惠券https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=vf2b5zld&utm_source=vf2b5zld

阿里云教程系列網(wǎng)站http://aliyun.guan2ye.com

1.png

我的開源項(xiàng)目spring boot 搭建的一個(gè)企業(yè)級快速開發(fā)腳手架

1.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翼雀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子孩擂,更是在濱河造成了極大的恐慌狼渊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件类垦,死亡現(xiàn)場離奇詭異狈邑,居然都是意外死亡坦弟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門官地,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酿傍,“玉大人,你說我怎么就攤上這事驱入〕喑矗” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵亏较,是天一觀的道長莺褒。 經(jīng)常有香客問我,道長雪情,這世上最難降的妖魔是什么遵岩? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮巡通,結(jié)果婚禮上尘执,老公的妹妹穿的比我還像新娘。我一直安慰自己宴凉,他們只是感情好誊锭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弥锄,像睡著了一般丧靡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上籽暇,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天温治,我揣著相機(jī)與錄音,去河邊找鬼戒悠。 笑死熬荆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的救崔。 我是一名探鬼主播惶看,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼六孵!你這毒婦竟也來了纬黎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤劫窒,失蹤者是張志新(化名)和其女友劉穎本今,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冠息,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年挪凑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逛艰。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躏碳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出散怖,到底是詐尸還是另有隱情菇绵,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布镇眷,位于F島的核電站咬最,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏欠动。R本人自食惡果不足惜永乌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望具伍。 院中可真熱鬧翅雏,春花似錦、人聲如沸沿猜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啼肩。三九已至,卻和暖如春衙伶,著一層夾襖步出監(jiān)牢的瞬間祈坠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工矢劲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赦拘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓芬沉,卻偏偏與公主長得像躺同,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子丸逸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容