All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nofdev.http.oauth2.OAuthJsonProxy.groovy Maven / Gradle / Ivy

package org.nofdev.http.oauth2

import com.fasterxml.jackson.databind.ObjectMapper
import groovy.transform.CompileStatic
import org.apache.oltu.oauth2.client.OAuthClient
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest
import org.apache.oltu.oauth2.client.request.OAuthClientRequest
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse
import org.apache.oltu.oauth2.common.OAuth
import org.apache.oltu.oauth2.common.exception.OAuthProblemException
import org.apache.oltu.oauth2.common.message.types.GrantType
import org.nofdev.http.*
import org.nofdev.servicefacade.ServiceContext
import org.nofdev.servicefacade.ServiceContextHolder
import org.nofdev.servicefacade.UnhandledException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.slf4j.MDC

import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
/**
 * Created by HouDongQiang on 2016/3/10.
 * for OAuth proxy handler. 用于OAuth认证的代理器
 */
@CompileStatic
class OAuthJsonProxy implements InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(OAuthJsonProxy.class);

    private Class inter
    private ProxyStrategy proxyStrategy
    private PoolingConnectionManagerFactory poolingConnectionManagerFactory
    private DefaultRequestConfig defaultRequestConfig
    private OAuthConfig oAuthConfig

    public OAuthJsonProxy(Class inter, OAuthConfig oAuthConfig, ProxyStrategy proxyStrategy, PoolingConnectionManagerFactory poolingConnectionManagerFactory, DefaultRequestConfig defaultRequestConfig) {
        this.inter = inter
        this.proxyStrategy = proxyStrategy
        this.oAuthConfig = oAuthConfig

        if (poolingConnectionManagerFactory == null) {
            try {
                this.poolingConnectionManagerFactory = new PoolingConnectionManagerFactory()
            } catch (Exception e) {
                e.printStackTrace()
            }
        } else {
            this.poolingConnectionManagerFactory = poolingConnectionManagerFactory;
        }
        if (defaultRequestConfig == null) {
            this.defaultRequestConfig = new DefaultRequestConfig()
        } else {
            this.defaultRequestConfig = defaultRequestConfig
        }
    }

    /**
     * 不支持不被信任证书的 ssl 访问, 请使用支持传入new PoolingConnectionManagerFactory(true)的构造函数
     * @param inter
     * @param oAuthConfig
     * @param proxyStrategy
     */
    public OAuthJsonProxy(Class inter, OAuthConfig oAuthConfig, ProxyStrategy proxyStrategy) {
        this(inter, oAuthConfig, proxyStrategy, null, null)
    }

    /**
     * 不支持不被信任证书的 ssl 访问, 请使用支持传入new PoolingConnectionManagerFactory(true)的构造函数
     * @param inter
     * @param oAuthConfig
     * @param url
     */
    public OAuthJsonProxy(Class inter, OAuthConfig oAuthConfig, String url) {
        this(inter, oAuthConfig, new DefaultProxyStrategyImpl(url))
    }

    public TokenContext getAccessToken() {
        logger.info("Getting access token.")
        if (TokenContext.instance.isExpire()) {
            return getNewAccessToken()
        } else {
            // 什么也不做
        }
        logger.debug("The access token is ${TokenContext.instance.access_token}, and expires in ${TokenContext.instance.expires_in}")
        return TokenContext.instance
    }

    private TokenContext getNewAccessToken() throws Exception {
            //todo 默认为client_credentials方式 待改造成4种都支持的
            if (GrantType.CLIENT_CREDENTIALS.toString().equals(oAuthConfig.grantType)) {
                OAuthClientRequest request = OAuthClientRequest
                        .tokenLocation(oAuthConfig.authenticationServerUrl)
                        .setGrantType(GrantType.CLIENT_CREDENTIALS)
                        .setClientId(oAuthConfig.getClientId())
                        .setClientSecret(oAuthConfig.getClientSecret())
                        .buildBodyMessage();

                long timeNow = new Date().getTime();
                //TODO 这里每次都 new 一个不合适
                OAuthClient oAuthClient = new OAuthClient(new CustomURLConnectionClient(poolingConnectionManagerFactory, defaultRequestConfig))
                try {
                    OAuthJSONAccessTokenResponse oAuthResponse = oAuthClient.accessToken(request, OAuthJSONAccessTokenResponse.class)
                    TokenContext.instance.access_token = oAuthResponse.getAccessToken()
                    TokenContext.instance.expires_in = oAuthResponse.getExpiresIn()
                    TokenContext.instance.startTime = timeNow
                    TokenContext.instance.stopTime = timeNow + oAuthResponse.getExpiresIn() * 1000
                } catch (OAuthProblemException e) {
                    logger.error(e.message, e)
                    //发送请求成功了但是token验证错误
                    throw new AuthenticationException("token认证失败", e);
                } catch (Exception e) {
                    logger.error(e.message, e)
                    //请求没有发送成功(400和401以外的异常)
                    throw new UnhandledException("token认证服务器系统异常", e);
                }
            } else {
                logger.error("现在只支持 ${GrantType.CLIENT_CREDENTIALS.toString()} 方式")
                throw new AuthenticationException("grant_type not support!")
            }
        return TokenContext.instance
    }

    private CustomOAuthResourceResponse resource(String token, String url, Map params, Map headers) {
        //init headers
        OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest(url).setAccessToken(token).buildHeaderMessage();
        // add headers
        headers.each {
            bearerClientRequest.addHeader(it.key, it.value)
        }
        // add body
//        bearerClientRequest.setBody(new ObjectMapper().writeValueAsString(params))
        bearerClientRequest.setBody(this.paramsToQueryString(params))

        // post request
        CustomURLConnectionClient customURLConnectionClient = new CustomURLConnectionClient(poolingConnectionManagerFactory, defaultRequestConfig)
        OAuthClient oAuthClient = new OAuthClient(customURLConnectionClient);
        return oAuthClient.resource(bearerClientRequest, OAuth.HttpMethod.POST, CustomOAuthResourceResponse.class);
    }

    @Override
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        def start = new Date()
        final endl = System.properties.'line.separator'

        def serviceContext = ServiceContextHolder.getServiceContext()

        if(serviceContext?.getCallId()){
            MDC.put(ServiceContext.CALLID.toString(), ObjectMapperFactory.createObjectMapper().writeValueAsString(serviceContext?.getCallId()))
        }

        if ("hashCode".equals(method.getName())) {
            return inter.hashCode();
        }
        if ("toString".equals(method.getName())) {
            return inter.toString();
        }

        String remoteURL = proxyStrategy.getRemoteURL(inter, method);
        Map params = proxyStrategy.getParams(args)
        Map context = serviceContextToMap(serviceContext)

        logger.debug("RPC call: ${remoteURL} ${ObjectMapperFactory.createObjectMapper().writeValueAsString(params)}");

        CustomOAuthResourceResponse resourceResponse = this.resource(getAccessToken().access_token, remoteURL, params, context)
        if (resourceResponse.getResponseCode() == 400 || resourceResponse.getResponseCode() == 401) {
            if (resourceResponse.getResponseCode() == 401) {
                logger.debug("对${remoteURL}的访问未取得授权,有可能token已经过期,尝试重新获取token")
                //重试一次
                resourceResponse = this.resource(getNewAccessToken().access_token, remoteURL, params, context)
                if (resourceResponse.getResponseCode() == 400 || resourceResponse.getResponseCode() == 401) {
                    logger.error("对${remoteURL}的访问未取得授权")
                    throw new AuthorizationException("对${remoteURL}的访问未取得授权")
                }
            } else {
                logger.error("对${remoteURL}的访问未取得授权")
                throw new AuthorizationException("对${remoteURL}的访问未取得授权")
            }
        }

        HttpMessageWithHeader response = new HttpMessageWithHeader(resourceResponse.getResponseCode(), resourceResponse.getContentType(), resourceResponse.getBody(), resourceResponse.getHeaders())
        def result = proxyStrategy.getResult(method, response)

        def end = new Date()
        long millis = end.time - start.time
        def slow = ''
        if(millis > 500) {
            slow = "${endl}SLOW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!${endl}"
            logger.warn("${inter}.${method?.getName()} result($slow$millis ms$slow): ${endl}${ObjectMapperFactory.createObjectMapper().writeValueAsString(result)}")
        }else {
            logger.debug("${inter}.${method?.getName()} result($slow$millis ms$slow): ${endl}${ObjectMapperFactory.createObjectMapper().writeValueAsString(result)}")
        }

        result
    }


    private Map serviceContextToMap(ServiceContext serviceContext) {
        Map context = new HashMap<>();
        ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper()
        if (serviceContext != null && serviceContext.getCallId() != null) {
            context.put(ServiceContext.CALLID.toString(), objectMapper.writeValueAsString(serviceContext.getCallId()));
        }
        context
    }

    public Object getObject() {
        Class[] interfaces = [inter];
        return Proxy.newProxyInstance(inter.getClassLoader(), interfaces, this);
    }



    private String paramsToQueryString(Map params){
        StringBuilder sb = new StringBuilder();
        for (def entry : params.entrySet()) {
            if (sb.length() > 0) {
                sb.append('&');
            }
            sb.append(URLEncoder.encode(entry.key,'UTF-8'));
            if (entry.value) {
                sb.append('=');
                sb.append(URLEncoder.encode(entry.value,'UTF-8'));
            }
        }
        return sb.toString();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy