zoukankan      html  css  js  c++  java
  • 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类,该类中定义了不同场景下通用的错误标识,在程序开发时可以直接使用它提供的常量。
     
     
  • 相关阅读:
    CS229 6.4 Neurons Networks Autoencoders and Sparsity
    CS229 6.3 Neurons Networks Gradient Checking
    【Leetcode】【Easy】Min Stack
    【Leetcode】【Easy】Merge Sorted Array
    【Leetcode】【Easy】ZigZag Conversion
    【Leetcode】【Easy】Valid Palindrome
    【Leetcode】【Easy】Reverse Integer
    【Leetcode】【Easy】Palindrome Number
    【Leetcode】【Easy】Length of Last Word
    【Leetcode】【Easy】Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/jdluojing/p/4201729.html
Copyright © 2011-2022 走看看