Apache olth學習筆記

簡介

Apache olth是oauth2.0協議的java實現,可簡化oauth應用的開發,提供了受權服務器,資源服務器以及客戶端的實現。咱們這裏主要使用oauth2.0協議作受權服務,所以主要學習受權服務器的實現。
 

代碼結構

 
上圖爲apache olth的受權服務器的代碼組織結構,從包的組織能夠看到分爲四個模塊:
  • issuser        主要提供用於生成受權碼(authorization code)、訪問令牌(access token)和刷新令牌(refresh token)的通用實現
  • request       用於封裝受權碼請求和令牌請求的通用邏輯,並提供響應的校驗手段
  • response    用於封裝受權流程中通用的響應邏輯,提供生成不一樣響應結果的方法
  • validator    爲request提供校驗服務
 

issuser代碼分析

 
一共包含2個接口和3個類,其中 OAuthIssuser接口定義issuer的通用功能:
public interface OAuthIssuer {
    public String accessToken() throws OAuthSystemException;

    public String authorizationCode() throws OAuthSystemException;

    public String refreshToken() throws OAuthSystemException;
}
OAuthIssuer的實現中使用 ValueGenerator來生成實際的值:
public interface ValueGenerator {
    public String generateValue() throws OAuthSystemException;

    public String generateValue(String param) throws OAuthSystemException;
}
 
ValueGenerator提供了兩個通用的實現類: MD5GeneratorUUIDValueGenerator.
 

request代碼分析

 
request包中包含5個類,其中OAuthRequest是其餘四個類的父類,提供最基礎最通用的邏輯和工具方法, OAuthAuthzRequest類用於受權碼請求,而 OAuthTokenRequestOAuthUnauthenticatedTokenRequest用於訪問令牌和刷新訪問令牌請求。
 
請求封裝的主要做用是根據oauth2.0規範中規定的各個步驟中相關參數是否可選等規則,來對實際的請求進行校驗。校驗的邏輯又有validator包中的各類validator實現來完成,request包中只須要根據不一樣的業務需求組合不一樣的validator便可完成對應的校驗工做。
 
首先看父類 OAuthRequest提供的方法:
 
除了提供從實際請求中獲取oauth2.0規定的參數的方法外,還有兩個protected方法:validate和initValidator,其中initValidator方法由子類負責實現。也就是說子類負責提供validator,validator方法中會調用提供的validator:
protected void validate() throws OAuthSystemException, OAuthProblemException {
    try {
        // 拿到validator
        validator = initValidator();
        validator.validateMethod(request);
        validator.validateContentType(request);
        // 校驗必填的參數是否知足
        validator.validateRequiredParameters(request);
        // 校驗憑證認證
        validator.validateClientAuthenticationCredentials(request);
    } catch (OAuthProblemException e) {
        try {
            String redirectUri = request.getParameter(OAuth.OAUTH_REDIRECT_URI);
            if ( !OAuthUtils.isEmpty(redirectUri)) {
                e.setRedirectUri(redirectUri);
            }
        } catch (Exception ex) {
            if (log.isDebugEnabled()) {
                log.debug( "Cannot read redirect_url from the request: {}", new String[] {ex.getMessage()});
            }
        }

        throw e;
    }

}
 
接着咱們看子類 OAuthAuthzRequest的initValidator方法:
protected OAuthValidator <HttpServletRequest > initValidator() throws OAuthProblemException, OAuthSystemException {
    // 請求受權碼時response_type參數能夠是code或token,詳情看oauth2.0規範
    validators.put(ResponseType.CODE.toString(), CodeValidator. class);
    validators.put(ResponseType.TOKEN.toString(), TokenValidator. class);
   
    // 從實際請求中獲取response_type參數,跟根據其值返回對應的validator實例
    final String requestTypeValue = getParam(OAuth.OAUTH_RESPONSE_TYPE);
    if (OAuthUtils.isEmpty(requestTypeValue)) {
        throw OAuthUtils.handleOAuthProblemException( "Missing response_type parameter value");
    }
    final Class < ? extends OAuthValidator <HttpServletRequest >> clazz = validators.get(requestTypeValue);
    if (clazz == null) {
        throw OAuthUtils.handleOAuthProblemException( "Invalid response_type parameter value");
    }

    return OAuthUtils.instantiateClass(clazz);
}
 
其餘幾個實現類邏輯基本相同,就不在作分析了。
 

validator代碼分析

 
這裏展現的類只是validator體系中和受權服務器相關的部分,其接口定義部分在org.apache.olth.oauth2.common.validators包中,全部validator都實現了 OAuthValidator接口:
public interface OAuthValidator <T extends HttpServletRequest > {

    public void validateMethod(T request) throws OAuthProblemException;

    public void validateContentType(T request) throws OAuthProblemException;

    public void validateRequiredParameters(T request) throws OAuthProblemException;

    public void validateOptionalParameters(T request) throws OAuthProblemException;

    public void validateNotAllowedParameters(T request) throws OAuthProblemException;

    public void validateClientAuthenticationCredentials(T request) throws OAuthProblemException;

    public void performAllValidations(T request) throws OAuthProblemException;

}
而且系統提供了實現了全部方法和功能邏輯的 AbstractValidator類:
// 必填字段列表
protected List <String > requiredParams = new ArrayList <String >();
// 可選字段列表
protected Map <String, String[] > optionalParams = new HashMap <String, String[] >();
// 不容許出現字段列表
protected List <String > notAllowedParams = new ArrayList <String >();
// 是否必須進行權限認證
protected boolean enforceClientAuthentication;
 
該類中包含四個成員變量,分別用於保存一些信息,在其餘各個方法中使用這些成員變量來進行處理,例如validateRequiredParameters方法:
public void validateRequiredParameters(T request) throws OAuthProblemException {
    final Set <String > missingParameters = new HashSet <String >();
    for (String requiredParam : requiredParams) {
        String val = request.getParameter(requiredParam);
        if (OAuthUtils.isEmpty(val)) {
            missingParameters.add(requiredParam);
        }
    }
    if ( !missingParameters.isEmpty()) {
        throw OAuthUtils.handleMissingParameters(missingParameters);
    }
}
只須要遍歷對應成員變量中的數據,而後進行檢測便可。那麼這些成員變量中的數據從什麼地方來呢?答案就是子類!例如查看在受權碼請求中使用到的CodeValidator:
public class CodeValidator extends AbstractValidator <HttpServletRequest > {

    public CodeValidator() {
        requiredParams.add(OAuth.OAUTH_RESPONSE_TYPE);
        requiredParams.add(OAuth.OAUTH_CLIENT_ID);
    }

    @Override
    public void validateMethod(HttpServletRequest request) throws OAuthProblemException {
        String method = request.getMethod();
        if ( !OAuth.HttpMethod.GET.equals(method) && !OAuth.HttpMethod.POST.equals(method)) {
            throw OAuthProblemException.error(OAuthError.CodeResponse.INVALID_REQUEST)
                .description( "Method not correct.");
        }
    }

    @Override
    public void validateContentType(HttpServletRequest request) throws OAuthProblemException {
    }
}
 
經過在構造方法中操做父類的成員變量和覆蓋AbstractValidator中的方法便可。其餘validator實現方式相似,就不在分析了。
 

response代碼分析

response包中只有一個類 OAuthASReponse,該類提供了組裝不一樣請求的基本方法,具體要返回哪些參數可在程序中自由指定。
構造方法是protected,所以不容許獲取該類的實例,實際上也不必直接操做該類的實例,由於實際咱們須要使用的他的兩個靜態內部類: OAuthAuthorizationResponseBuilderOAuthTokenResponseBuilder,而後經過他們提供的方法來構造和生成最終的響應數據。
 
實際上這兩個Builder類只是根據不一樣的業務場景提供一些特定的方法,好比OAuthTokenResponseBuilder用於構造訪問令牌響應數據,所以他提供瞭如setAccessToken和setRefreshToken之類的方法。最終實際的實現實在他們的父類 OAuthResponseBuilder類中(該類是OAuthASResponse的父類OAuthResponse類的靜態內部類)。
 

ResponseBuilder代碼分析

用於構造響應數據(OAuthResponse)的Builder類被做爲OAuthResponse類的靜態內部類的形式存在:
 
根據類結構圖看以看到有兩個builder: OAuthResponseBuilderOAuthErrorResponseBuilder,其中後者又是前者的子類。咱們先看一個實際使用中的場景:
// 受權碼
OAuthResponse oAuthResponse = OAuthASResponse. authorizationResponse(request, 200)
        .location(jdUrl)
        .setCode(oauthCode)
        .setScope(state)
        .buildQueryMessage();
String url =oAuthResponse.getLocationUri();
response.sendRedirect(url);

// 訪問令牌
OAuthResponse authASResponse = OAuthASResponse. tokenResponse(200)
        .setAccessToken(access_token)
        .setExpiresIn( "7200")
        .setRefreshToken(refreshToken)
        .setTokenType(TokenType.BEARER.toString())
        .setParam( "re_expires_in", "14400")
        .buildJSONMessage();
String  json =  authASResponse.getBody();

// 錯誤響應
OAuthResponse authASResponse = OAuthASResponse. errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
        .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
        .setErrorDescription( "invald expired")
        .buildJSONMessage();
return new ResponseEntity <String >(authASResponse.getBody(), headers, HttpStatus.UNAUTHORIZED);
 
能夠看出咱們調用的各類set方法實際上就是在設置響應參數,當咱們調用buildJSONMessage之類的方法時會生成一個OAuthResponse對象,其中已經包含了響應的數據,咱們只須要根據返回方式調用OAuthResponse對象的getBody或getHeaders之類的方法便可獲取到構造好的響應數據。
public static class OAuthResponseBuilder {

    protected OAuthParametersApplier applier;
    protected Map <String, Object > parameters = new HashMap <String, Object >();
    protected int responseCode;
    protected String location;

    public OAuthResponseBuilder( int responseCode) {
        this.responseCode = responseCode;
    }

    public OAuthResponseBuilder location(String location) {
        this.location = location;
        return this;
    }

    public OAuthResponseBuilder setScope(String value) {
        this.parameters.put(OAuth.OAUTH_SCOPE, value);
        return this;
    }

    public OAuthResponseBuilder setParam(String key, String value) {
        this.parameters.put(key, value);
        return this;
    }

    public OAuthResponse buildQueryMessage() throws OAuthSystemException {
        OAuthResponse msg = new OAuthResponse(location, responseCode);
        this.applier = new QueryParameterApplier();

        if (parameters.containsKey(OAuth.OAUTH_ACCESS_TOKEN)) {
            this.applier = new FragmentParametersApplier();
        } else{
            this.applier = new QueryParameterApplier();
        }
       
        return (OAuthResponse)applier.applyOAuthParameters(msg, parameters);
    }

    public OAuthResponse buildBodyMessage() throws OAuthSystemException {
        OAuthResponse msg = new OAuthResponse(location, responseCode);
        this.applier = new BodyURLEncodedParametersApplier();
        return (OAuthResponse)applier.applyOAuthParameters(msg, parameters);
    }

    public OAuthResponse buildJSONMessage() throws OAuthSystemException {
        OAuthResponse msg = new OAuthResponse(location, responseCode);
        this.applier = new JSONBodyParametersApplier();
        return (OAuthResponse)applier.applyOAuthParameters(msg, parameters);
    }

    public OAuthResponse buildHeaderMessage() throws OAuthSystemException {
        OAuthResponse msg = new OAuthResponse(location, responseCode);
        this.applier = new WWWAuthHeaderParametersApplier();
        return (OAuthResponse)applier.applyOAuthParameters(msg, parameters);
    }
}
至於 OAuthParameterApplier的實現,這裏就不作深刻了解了,其做用就是生成不一樣格式的數據並設置到OAuthResponse對象的成員變量中。
 
另外對應錯誤響應中的error字段,Apache olth中還提供了一個 OAuthError類,該類中定義了不一樣場景下通用的錯誤標識,在程序開發時能夠直接使用它提供的常量。
相關文章
相關標籤/搜索