溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么看待Spring security oauth2的認(rèn)證流程

發(fā)布時間:2021-10-20 16:06:52 來源:億速云 閱讀:171 作者:柒染 欄目:大數(shù)據(jù)

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)怎么看待Spring security oauth2的認(rèn)證流程,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

Spring Security OAuth3 整理

  1. 隱式授權(quán)模式(Implicit Grant)

需要提供的參數(shù)
    地址:  
        oauth/token
    請求頭參數(shù):
        Authorization=Basic base64.encode(client_id:client_secret) 
    ==如果secret為空,只對client_id進(jìn)行編碼==
     
    POST參數(shù) 
     grant_type:password 
     username: 用戶名
     password:密碼
  1. client_credentials 認(rèn)證

需要傳遞的參數(shù)
   地址:  
       oauth/token
  Header參數(shù)
      client_id:
      client_secret:
      
  Header頭添加
     Authorization=Basic base64.encode(client_id:client_secret) 
    ==如果secret為空,只對client_id進(jìn)行編碼==
  
  Post參數(shù)
      grant_type: password
      scope: 作用域
  1. password 模式

需要傳遞的參數(shù)
   地址:  
       oauth/token
  Header參數(shù)
      client_id:
      client_secret:
      
  Header頭添加
     Authorization=Basic base64.encode(client_id:client_secret) 
    ==如果secret為空,只對client_id進(jìn)行編碼==
  
  Post參數(shù)
      grant_type: password
      username: 用戶名
      password: 密碼
      scope: 作用域
  1. 授權(quán)碼模式

   
   1.首先請求oauth/authorize 獲取code
   
    需要提交的參數(shù)
     Header參數(shù)
      client_id:
      client_secret:
      
     Header頭添加
      Authorization=Basic base64.encode(client_id:client_secret) 
         如果secret為空,只對client_id進(jìn)行編碼
      
    post參數(shù)  
      grant_type: password
      username: 用戶名
      password: 密碼
      scope: 作用域
   
   
   2. 通過code 訪問 oauth/token接口,換取回應(yīng)的accessToken

Spring Security OAuth3 認(rèn)證流程

  1. 首先開啟@EnableAuthorizationServer 注解

AuthorizationServerConfigurerAdapterm默認(rèn)方法配置

public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
 
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security ) throws Exception {
    // 配置AuthorizationServer安全認(rèn)證的相關(guān)信息,創(chuàng)建ClientCredentialsTokenEndpointFilter核心過濾器
    }
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    //配置OAuth3的客戶端相關(guān)信息
    }
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    //配置AuthorizationServerEndpointsConfigurer眾多相關(guān)類,包括配置身份認(rèn)證器,配置認(rèn)證方式,TokenStore,TokenGranter,OAuth3RequestFactory
    }
  1. 客戶端身份認(rèn)證核心過濾器ClientCredentialsTokenEndpointFilter

核心代碼如下

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException, IOException, ServletException {
    ...
    String clientId = request.getParameter("client_id");
    String clientSecret = request.getParameter("client_secret");
 
    ...
    clientId = clientId.trim();
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId,
            clientSecret);
 
    return this.getAuthenticationManager().authenticate(authRequest);
  1. 頂級身份管理者AuthenticationManager

從請求中獲取client_id,client_secret,組裝成一個UsernamePasswordAuthenticationToken作為身份標(biāo)識,使用容器中的頂級身份管理器AuthenticationManager去進(jìn)行身份認(rèn)證(AuthenticationManager的實現(xiàn)類一般是ProviderManager。而ProviderManager內(nèi)部維護(hù)了一個List,真正的身份認(rèn)證是由一系列AuthenticationProvider去完成。而AuthenticationProvider的常用實現(xiàn)類則是DaoAuthenticationProvider,DaoAuthenticationProvider內(nèi)部又聚合了一個UserDetailsService接口,UserDetailsService才是獲取用戶詳細(xì)信息的最終接口

怎么看待Spring security oauth2的認(rèn)證流程

client模式是不存在“用戶”的概念的,那么這里的身份認(rèn)證是在認(rèn)證什么呢?debug可以發(fā)現(xiàn)UserDetailsService的實現(xiàn)被適配成了ClientDetailsUserDetailsService,這個設(shè)計是將client客戶端的信息(client_id,client_secret)適配成用戶的信息(username,password)經(jīng)過ClientCredentialsTokenEndpointFilter之后,身份信息已經(jīng)得到了AuthenticationManager的驗證。接著便到達(dá)了

TokenEndpoint。

  1. Token處理端點TokenEndpoint

前面的兩個ClientCredentialsTokenEndpointFilter和AuthenticationManager可以理解為一些前置校驗,和身份封裝

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {
 
    @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
    public ResponseEntity<OAuth3AccessToken> postAccessToken(Principal principal, @RequestParam
    Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
         ...
        String clientId = getClientId(principal);
        ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
//加載客戶端信息
        ...
        TokenRequest tokenRequest = getOAuth3RequestFactory().createTokenRequest(parameters, authenticatedClient);
//結(jié)合請求信息,創(chuàng)建TokenRequest
        ...
        OAuth3AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
//將TokenRequest傳遞給TokenGranter頒發(fā)token
        ...
        return getResponse(token);
    }
    private TokenGranter tokenGranter;

真正的/oauth/token端點,其中方法參數(shù)中的Principal經(jīng)過之前的過濾器,已經(jīng)被填充了相關(guān)的信息,而方法的內(nèi)部則是依賴了一個TokenGranter 來頒發(fā)token。其中OAuth3AccessToken的實現(xiàn)類DefaultOAuth3AccessToken就是最終在控制臺得到的token序列化之前的原始類:

public class DefaultOAuth3AccessToken implements Serializable, OAuth3AccessToken {
 
    private static final long serialVersionUID = 914967629530462926L;
 
    private String value;
 
    private Date expiration;
 
    private String tokenType = BEARER_TYPE.toLowerCase();
 
    private OAuth3RefreshToken refreshToken;
 
    private Set<String> scope;
 
    private Map<String, Object> additionalInformation = Collections.emptyMap();
 
    //getter,setter
}
 
@org.codehaus.jackson.map.annotate.JsonSerialize(using = OAuth3AccessTokenJackson1Serializer.class)
@org.codehaus.jackson.map.annotate.JsonDeserialize(using = OAuth3AccessTokenJackson1Deserializer.class)
@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = OAuth3AccessTokenJackson2Serializer.class)
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = OAuth3AccessTokenJackson2Deserializer.class)
 
public interface OAuth3AccessToken {
 
    public static String BEARER_TYPE = "Bearer";
 
    public static String OAUTH2_TYPE = "OAuth3";
 
    /**
     * The access token issued by the authorization server. This value is REQUIRED.
     */
    public static String ACCESS_TOKEN = "access_token";
 
    /**
     * The type of the token issued as described in <a
     * href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-7.1">Section 7.1</a>. Value is case insensitive.
     * This value is REQUIRED.
     */
    public static String TOKEN_TYPE = "token_type";
 
    /**
     * The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will
     * expire in one hour from the time the response was generated. This value is OPTIONAL.
     */
    public static String EXPIRES_IN = "expires_in";
 
    /**
     * The refresh token which can be used to obtain new access tokens using the same authorization grant as described
     * in <a href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-6">Section 6</a>. This value is OPTIONAL.
     */
    public static String REFRESH_TOKEN = "refresh_token";

    /**
     * The scope of the access token as described by <a
     * href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.3">Section 3.3</a>
     */
    public static String SCOPE = "scope";

    ...
  1. TokenGranter

怎么看待Spring security oauth2的認(rèn)證流程

TokenGranter的設(shè)計思路是使用CompositeTokenGranter管理一個List列表,每一種grantType對應(yīng)一個具體的真正授權(quán)者,在debug過程中可以發(fā)現(xiàn)CompositeTokenGranter 內(nèi)部就是在循環(huán)調(diào)用五種TokenGranter實現(xiàn)類的grant方法,而granter內(nèi)部則是通過grantType來區(qū)分是否是各自的授權(quán)類型。

public class CompositeTokenGranter implements TokenGranter {
 
    private final List<TokenGranter> tokenGranters;
 
    public CompositeTokenGranter(List<TokenGranter> tokenGranters) {
        this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);
    }
 
    public OAuth3AccessToken grant(String grantType, TokenRequest tokenRequest) {
        for (TokenGranter granter : tokenGranters) {
            OAuth3AccessToken grant = granter.grant(grantType, tokenRequest);
            if (grant!=null) {
                return grant;
            }
        }
        return null;
    }
}

五種類型分別是:

  • ResourceOwnerPasswordTokenGranter ==> password密碼模式

  • AuthorizationCodeTokenGranter ==> authorization_code授權(quán)碼模式

  • ClientCredentialsTokenGranter ==> client_credentials客戶端模式

  • ImplicitTokenGranter ==> implicit簡化模式

  • RefreshTokenGranter ==>refresh_token 刷新token專用

以客戶端模式為例,思考如何產(chǎn)生token的,則需要繼續(xù)研究5種授權(quán)者的抽象類:AbstractTokenGranter

public abstract class AbstractTokenGranter implements TokenGranter {
 
    protected final Log logger = LogFactory.getLog(getClass());
 
    //與token相關(guān)的service,重點
    private final AuthorizationServerTokenServices tokenServices;
    //與clientDetails相關(guān)的service,重點
    private final ClientDetailsService clientDetailsService;
    //創(chuàng)建oauth3Request的工廠,重點
    private final OAuth3RequestFactory requestFactory;
 
    private final String grantType;
    ...
 
    public OAuth3AccessToken grant(String grantType, TokenRequest tokenRequest) {
 
        ...
        String clientId = tokenRequest.getClientId();
        ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
        validateGrantType(grantType, client);
 
        logger.debug("Getting access token for: " + clientId);
 
        return getAccessToken(client, tokenRequest);
 
    }
 
    protected OAuth3AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
        return tokenServices.createAccessToken(getOAuth3Authentication(client, tokenRequest));
    }
 
    protected OAuth3Authentication getOAuth3Authentication(ClientDetails client, TokenRequest tokenRequest) {
        OAuth3Request storedOAuth3Request = requestFactory.createOAuth3Request(client, tokenRequest);
        return new OAuth3Authentication(storedOAuth3Request, null);
    }
 
    ...
}
  1. AuthorizationServerTokenServices分析重點

AuthorizationServer端的token操作service,接口設(shè)計如下:

public interface AuthorizationServerTokenServices {
    //創(chuàng)建token
    OAuth3AccessToken createAccessToken(OAuth3Authentication authentication) throws AuthenticationException;
    //刷新token
    OAuth3AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
            throws AuthenticationException;
    //獲取token
    OAuth3AccessToken getAccessToken(OAuth3Authentication authentication);
 
}

在默認(rèn)的實現(xiàn)類DefaultTokenServices中,可以看到token是如何產(chǎn)生的,并且了解了框架對token進(jìn)行哪些信息的關(guān)聯(lián)。

@Transactional
public OAuth3AccessToken createAccessToken(OAuth3Authentication authentication) throws AuthenticationException {
 
    OAuth3AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
    OAuth3RefreshToken refreshToken = null;
    if (existingAccessToken != null) {
        if (existingAccessToken.isExpired()) {
            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);
        }
        else {
            // Re-store the access token in case the authentication has changed
            tokenStore.storeAccessToken(existingAccessToken, authentication);
            return existingAccessToken;
        }
    }
 
    // Only create a new refresh token if there wasn't an existing one
    // associated with an expired access token.
    // Clients might be holding existing refresh tokens, so we re-use it in
    // the case that the old access token
    // expired.
    if (refreshToken == null) {
        refreshToken = createRefreshToken(authentication);
    }
    // But the refresh token itself might need to be re-issued if it has
    // expired.
    else if (refreshToken instanceof ExpiringOAuth3RefreshToken) {
        ExpiringOAuth3RefreshToken expiring = (ExpiringOAuth3RefreshToken) refreshToken;
        if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
            refreshToken = createRefreshToken(authentication);
        }
    }
 
    OAuth3AccessToken accessToken = createAccessToken(authentication, refreshToken);
    tokenStore.storeAccessToken(accessToken, authentication);
    // In case it was modified
    refreshToken = accessToken.getRefreshToken();
    if (refreshToken != null) {
        tokenStore.storeRefreshToken(refreshToken, authentication);
    }
    return accessToken;
 
}

AuthorizationServerTokenServices的作用,他提供了創(chuàng)建token,刷新token,獲取token的實現(xiàn)。在創(chuàng)建token時,他會調(diào)用tokenStore對產(chǎn)生的token和相關(guān)信息存儲到對應(yīng)的實現(xiàn)類中,可以是redis,數(shù)據(jù)庫,內(nèi)存,jwt。

上述就是小編為大家分享的怎么看待Spring security oauth2的認(rèn)證流程了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI