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

com.dahuatech.icc.oauth.handle.TokenHandleSingle Maven / Gradle / Ivy

There is a newer version: 1.0.13.7
Show newest version
package com.dahuatech.icc.oauth.handle;

import com.dahuatech.hutool.core.date.DateUtil;
import com.dahuatech.hutool.core.thread.NamedThreadFactory;
import com.dahuatech.hutool.core.util.StrUtil;
import com.dahuatech.hutool.http.Method;
import com.dahuatech.hutool.json.JSONUtil;
import com.dahuatech.hutool.log.Log;
import com.dahuatech.hutool.log.LogFactory;
import com.dahuatech.icc.common.ErrorConstants;
import com.dahuatech.icc.exception.ClientException;
import com.dahuatech.icc.exception.ServerException;
import com.dahuatech.icc.oauth.constant.OauthConstant;
import com.dahuatech.icc.oauth.http.DefaultClient;
import com.dahuatech.icc.oauth.http.IClient;
import com.dahuatech.icc.oauth.http.IccHttpHttpRequest;
import com.dahuatech.icc.oauth.http.IccTokenResponse;
import com.dahuatech.icc.oauth.model.v202010.*;
import com.dahuatech.icc.oauth.profile.GrantType;
import com.dahuatech.icc.oauth.profile.IccProfile;
import com.dahuatech.icc.oauth.utils.LogUtils;
import com.dahuatech.icc.util.BeanUtil;
import com.dahuatech.icc.util.SignUtil;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;

/**
 * 单例,获取access_token,刷新access_token,登录状态保活
 *
 * @author 232676
 * @since 1.0.0 2020-10-24 20:59:11
 */
public class TokenHandleSingle {
  private static final Log logger = LogFactory.get();

  /** access_token 首次获取状态 */
  private static final AtomicBoolean TOKEN_INITED = new AtomicBoolean(Boolean.FALSE);
  /** token刷新间隔 30s */
  private static final long FRESH_TOKEN_INTERVAL = 30 * 1000;
  /** 线程刷新token和保活 */
  private final ScheduledExecutorService REFRESH_TOKEN_SCHEDULED =
      newSingleThreadScheduledExecutor(new NamedThreadFactory("Icc-Refresh-Token-Old", true));
  /** 密码和客户端认证存储Map */
  private final Map tokenMap = new ConcurrentHashMap<>();

  public Map getTokenMap() {
    return tokenMap;
  }
  /** 定时任务刷新token */
  private TokenHandleSingle() {
    REFRESH_TOKEN_SCHEDULED.scheduleWithFixedDelay(
        new Runnable() {
          @Override
          public void run() {
            try {
              refreshTokenAndKeepAlive();
            } catch (Throwable t) { // Defensive fault tolerance
              logger.error("Unexpected error occur at token refresh, cause: " + t.getMessage(), t);
            }
          }
        },
        FRESH_TOKEN_INTERVAL,
        FRESH_TOKEN_INTERVAL,
        TimeUnit.MILLISECONDS);
  }

  /**
   * 获取TokenHandleSingle实例
   *
   * @return TokenHandleSingle实例
   */
  public static synchronized TokenHandleSingle getInstance() {
    return SingletonHolder.INSTANCE;
  }

  /**
   * 根据授权类型刷新token
   *
   * @param grantType 授权类型
   * @return IccToken
   */
  public synchronized IccTokenResponse.IccToken refreshToken(GrantType grantType) {

    IccTokenResponse.IccToken token = null;
    try {
      switch (grantType) {
        case password:
          token = password();
          break;
        case client_credentials:
          token = clientCredentials();
          break;
        default:
      }
    } catch (ClientException e) {
      logger.error("get token failure");
    }
    /** 设置已首次加载,线程可以开始刷新或保活token */
    if (!TOKEN_INITED.get() && token != null) {
      TOKEN_INITED.set(Boolean.TRUE);
    }
    return token;
  }

  /**
   * 密码认证获取token
   *
   * @return IccTokenResponse.IccToken
   * @throws ClientException 客户端异常
   * @throws ServerException 服务端异常
   */
  private IccTokenResponse.IccToken password() throws ClientException, ServerException {
    IccHttpHttpRequest pubRequest =
        new IccHttpHttpRequest(
            OauthConstant.url(OauthConstant.OAUTH_URL_PUBLIC_KEY_GET), Method.GET);
    String pubBody = pubRequest.execute();
    LogUtils.printOut(logger,pubRequest.getRequestUniqueCode(),pubBody);
    OauthPublicKeyResponse keyResp = BeanUtil.toBean(pubBody, OauthPublicKeyResponse.class);
    if (keyResp.getData() == null) {
      logger.error("get public key faiure [{}]", OauthConstant.OAUTH_URL_PUBLIC_KEY_GET);
      throw new ServerException("get public key faiure");
    }
    Map map = new HashMap<>();
    map.put("grant_type", "password");
    map.put("username", IccProfile.username);
    map.put("password", SignUtil.encryptRSANew(IccProfile.password, keyResp.getData().getPublicKey()));
    /*web_client*/
    map.put("client_id", IccProfile.pwdClientId);
    /*web_client*/
    map.put("client_secret", IccProfile.pwdClientSecret);
    map.put("public_key", keyResp.getData().getPublicKey());
    IccHttpHttpRequest pr =
        new IccHttpHttpRequest(
            OauthConstant.url(OauthConstant.OAUTH_URL_PWD_AUTH_POST),
            Method.POST,
            JSONUtil.toJsonStr(map));
    LogUtils.printIn(logger,pr);
    String prBody = pr.execute();
    LogUtils.printOut(logger,pr.getRequestUniqueCode(),prBody);
    IccTokenResponse token = BeanUtil.toBean(prBody, IccTokenResponse.class);
    if (token == null || !token.isSuccess()) {
      logger.error(
          " auth failure [{}] reason [{}]",
          OauthConstant.url(OauthConstant.OAUTH_URL_PWD_AUTH_POST),
          token == null ? "" : token.getErrMsg());
      throw new ClientException(
          "GrantType [password] username=["
              + IccProfile.username
              + "],password=["
              + IccProfile.password
              + "] get access_token failure");
    }
    IccTokenResponse.IccToken iccToken = token.getData();
    /** 设置存活时间ttl,其中 expires_in 单位是秒 */
    iccToken.setTtl(System.currentTimeMillis() + (iccToken.getExpires_in() * 1000));

    iccToken.setExpireDate(DateUtil.formatBetween(iccToken.getTtl()));
    iccToken.setExpireDate(DateUtil.formatBetween(iccToken.getTtl()));
    tokenMap.put(enGrantKeyName(GrantType.password.name()), iccToken);
    return iccToken;
  }

  /**
   * 客户端认证获取token
   *
   * @return IccTokenResponse.IccToken
   * @throws ClientException 客户端异常
   * @throws ServerException 服务端异常
   */
  private IccTokenResponse.IccToken clientCredentials() throws ClientException, ServerException {
    Map map = new HashMap<>();
    map.put("grant_type", "client_credentials");
    map.put("client_id", IccProfile.clientId);
    map.put("client_secret", IccProfile.clientSecret);
    /** 客户端校验数据使用form表单形式 */
    IccHttpHttpRequest cr =
        new IccHttpHttpRequest(OauthConstant.url(OauthConstant.OAUTH_URL_CLIENT_AUTH), Method.POST);
    LogUtils.printIn(logger,cr);
    cr.form(map);
    String crBody = cr.execute();
    LogUtils.printOut(logger,cr.getRequestUniqueCode(),crBody);
    IccTokenResponse token = BeanUtil.toBean(crBody, IccTokenResponse.class);
    if (token == null || !token.isSuccess()) {
      logger.error(
          "client auth failure [{}] reason [{}]",
          OauthConstant.url(OauthConstant.OAUTH_URL_CLIENT_AUTH),
          token == null ? "" : token.getErrMsg());
      throw new ClientException("client auth failure" + (token == null ? "" : token.getErrMsg()));
    }
    IccTokenResponse.IccToken iccToken = token.getData();
    /** expires_in 单位是秒 */
    iccToken.setTtl(System.currentTimeMillis() + (iccToken.getExpires_in() * 1000));
    tokenMap.put(enGrantKeyName(GrantType.client_credentials.name()), iccToken);
    return iccToken;
  }

  /**
   * 从缓存获取token
   *
   * @param grantType 授权类型
   * @return IccTokenResponse.IccToken
   */
  public IccTokenResponse.IccToken getTokenCache(String grantType) {
    if(REFRESH_TOKEN_SCHEDULED.isShutdown() || REFRESH_TOKEN_SCHEDULED.isTerminated()){
      logger.warn("定时任务进程异常关闭,重新加载");
      REFRESH_TOKEN_SCHEDULED.scheduleWithFixedDelay(
              new Runnable() {
                @Override
                public void run() {
                  try {
                    refreshTokenAndKeepAlive();
                  } catch (Throwable t) { // Defensive fault tolerance
                    logger.error("Unexpected error occur at token refresh, cause: " + t.getMessage(), t);
                  }
                }
              },
              FRESH_TOKEN_INTERVAL,
              FRESH_TOKEN_INTERVAL,
              TimeUnit.MILLISECONDS);
    }

    String key = enGrantKeyName(grantType);
    IccTokenResponse.IccToken token = tokenMap.get(enGrantKeyName(grantType));
    Long currentTime = System.currentTimeMillis();
    if(token != null && token.getTtl() < currentTime){//说明token过期了
      logger.warn("[{}]的token已过期,清理token信息:{}",key, JSONUtil.toJsonStr(token));
      clearExpiredToken();
      return null;
    }
    return tokenMap.get(enGrantKeyName(grantType));
  }

  /**
   * 刷新token
   *
   * @param grantType 授权类型
   * @param refreshToken 刷新fresh_token
   * @return IccTokenResponse.IccToken
   */
  public IccTokenResponse.IccToken refreshToken(GrantType grantType, String refreshToken) {
    try {
      IClient iClient = new DefaultClient();
      // 刷新token
      OauthRefreshTokenRequest refreshTokenRequest = new OauthRefreshTokenRequest();
      if (grantType.equals(GrantType.password)) {
        refreshTokenRequest.setClient_id(IccProfile.pwdClientId);
        refreshTokenRequest.setClient_secret(IccProfile.pwdClientSecret);
        refreshTokenRequest.setGrant_type(GrantType.refresh_token.name());
        refreshTokenRequest.setRefresh_token(refreshToken);
        OauthRefreshTokenResponse refreshTokenResponse =
            iClient.doAction(refreshTokenRequest, refreshTokenRequest.getResponseClass());
        if (refreshTokenResponse.isSuccess()) {
          OauthRefreshTokenResponse.IccReFreshToken freshToken = refreshTokenResponse.getData();
          IccTokenResponse.IccToken iccToken = new IccTokenResponse.IccToken();
          iccToken.setTtl(System.currentTimeMillis() + (freshToken.getExpires_in() * 1000));
          iccToken.setAccess_token(freshToken.getAccess_token());
          iccToken.setExpires_in(freshToken.getExpires_in());
          iccToken.setMagicId(freshToken.getMagicId());
          iccToken.setUserId(freshToken.getUserId());
          iccToken.setToken_type(freshToken.getToken_type());
          iccToken.setRefresh_token(freshToken.getRefresh_token());
          iccToken.setScope(freshToken.getScope());
          return iccToken;
        }
      }
      /*客户端没有fresh_token 字段,无法调用刷新token接口,故只能重新获取*/
      if (grantType.equals(GrantType.client_credentials)) {
        return clientCredentials();
      }

    } catch (ClientException e) {
      logger.error(
          "fresh token error , grantType=[{}],freshToken=[{}]", grantType.name(), refreshToken);
    }
    return null;
  }

  /**
   * 刷新token并保持心跳
   */
  private void refreshTokenAndKeepAlive() {
    if (TOKEN_INITED.get() && tokenMap.size() > 0) {
      for (Map.Entry entry : tokenMap.entrySet()) {
        IccTokenResponse.IccToken token = entry.getValue();
        Long currentTime = System.currentTimeMillis();
        /** 如果时间还剩120s,则刷新token */
        if (token.getTtl() - currentTime <= 120 * 1000) {
          GrantType grantType = deGrantType(entry.getKey());
          token = refreshToken(grantType, token.getRefresh_token());
          if (token != null) {
            tokenMap.put(entry.getKey(), token);
            logger.debug("refresh token success, [{}],token=[{}]", entry.getKey(), token);
          }
        }
      }
      keepalive();
    }
  }

  /** 保活接口 */
  private void keepalive() {
    try {
      IClient iClient = new DefaultClient();
      if (IccProfile.inited) {
        for (Map.Entry entry : tokenMap.entrySet()) {
          if (GrantType.client_credentials == deGrantType(entry.getKey())) {
            continue;
          }
          IccTokenResponse.IccToken token = entry.getValue();
          BrmKeepAliveRequest request = new BrmKeepAliveRequest();
          request.setMagicId(token.getMagicId());
          // [password] 密码认证,使用web
          request.setClientType(OauthConstant.ClientType.WEB.getClientType());
          BrmKeepAliveResponse brmKeepAliveResponse =
              iClient.doAction(request, request.getResponseClass());
          if (brmKeepAliveResponse.isSuccess()) {
            logger.debug("evo-brm [{}] keeplive success", entry.getKey());
          } else {
            // token失效了,重新获取token
            if (ErrorConstants.OAUTH_INVALID_TOKEN.equalsIgnoreCase(
                brmKeepAliveResponse.getCode())) {
              logger.info("[{}] token invalid , get new token", entry.getKey());
              // 移除缓存,重新获取
              tokenMap.remove(entry.getKey());
              refreshToken(deGrantType(entry.getKey()));
            }
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
      logger.error("keepalive error = {}", e);
    }
  }

  /**
   * 根据授权类型获取授权密钥名称
   *
   * @param grantType 授权类型
   * @return 授权密钥名称
   */
  private String enGrantKeyName(String grantType) {
    return grantType + StrUtil.COLON + IccProfile.host;
  }

  /**
   * 根据授权密钥名称获取授权类型
   *
   * @param grantKeyName 授权密钥名称
   * @return 授权类型
   */
  private GrantType deGrantType(String grantKeyName) {
    return GrantType.valueOf(grantKeyName.substring(0, grantKeyName.indexOf(StrUtil.COLON)));
  }

  /**
   * 清除过期的token
   */
  public void clearExpiredToken() {
    tokenMap.clear();
  }

  private static class SingletonHolder {
    private static final TokenHandleSingle INSTANCE = new TokenHandleSingle();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy