content: package openjoe.smart.sso.client.util;
  
  import openjoe.smart.sso.base.constant.BaseConstant;
  import openjoe.smart.sso.base.entity.ExpirationWrapper;
  import openjoe.smart.sso.base.entity.Result;
  import openjoe.smart.sso.base.entity.Token;
  import openjoe.smart.sso.base.entity.TokenPermission;
  import openjoe.smart.sso.base.util.CookieUtils;
  import openjoe.smart.sso.client.ClientProperties;
  import openjoe.smart.sso.client.token.TokenPermissionStorage;
  import openjoe.smart.sso.client.token.TokenStorage;
  import openjoe.smart.sso.client.token.TokenWrapper;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.util.StringUtils;
  
  import jakarta.servlet.http.HttpServletRequest;
  import java.io.UnsupportedEncodingException;
  import java.net.URLEncoder;
  import java.util.Collections;
  
  /**
   * SSO工具
   *
   * @author Joe
   */
  public class SSOUtils {
  
      private static final Logger logger = LoggerFactory.getLogger(SSOUtils.class);
  
      private static ClientProperties properties;
      private static TokenStorage tokenStorage;
      private static TokenPermissionStorage tokenPermissionStorage;
  
      public static void setTokenStorage(ClientProperties cp, TokenStorage ts, TokenPermissionStorage tps) {
          properties = cp;
          tokenStorage = ts;
          tokenPermissionStorage = tps;
      }
  
      /**
       * 获取当前token包装器
       *
       * @return
       */
      public static TokenWrapper getTokenWrapper() {
          String accessToken = getAccessToken();
          if (!StringUtils.hasLength(accessToken)) {
              return null;
          }
          return tokenStorage.get(accessToken);
      }
  
      /**
       * 获取当前accessToken
       *
       * @return
       */
      private static String getAccessToken() {
          HttpServletRequest request = ClientContextHolder.getRequest();
          String tokenName = getTokenName();
          // 从cookie中获取当前accessToken
          String accessToken = CookieUtils.getCookieValue(tokenName, request);
          if (StringUtils.hasLength(accessToken)) {
              return accessToken;
          }
          // 从header中获取当前accessToken
          return request.getHeader(tokenName);
      }
  
      /**
       * 发送http请求获取accessToken，并写入cookie
       *
       * @param code
       * @return
       */
      public static Result<Token> getHttpAccessTokenInCookie(String code) {
          Result<Token> result = getHttpAccessToken(code);
          if (result.isSuccess()) {
              // 写入cookie
              addCookieAccessToken(result.getData().getAccessToken());
          }
          return result;
      }
  
      /**
       * 写入accessToken到cookie
       *
       * @param accessToken
       */
      private static void addCookieAccessToken(String accessToken) {
          CookieUtils.addCookie(getTokenName(), accessToken, "/", ClientContextHolder.getRequest(), ClientContextHolder.getResponse());
      }
  
      private static String getTokenName() {
          return properties.getTokenNamePrefix() + properties.getClientId();
      }
  
      /**
       * 发送http请求获取accessToken
       *
       * @param code
       */
      public static Result<Token> getHttpAccessToken(String code) {
          Result<Token> result = OAuth2Utils.getAccessToken(properties.getServerUrl(), properties.getClientId(),
                  properties.getClientSecret(), code, getLocalUrl() + properties.getLogoutPath());
          if (result.isSuccess()) {
              Token token = result.getData();
              // 将token存储到本地
              tokenStorage.create(token);
              // 用accessToken请求用户权限信息，存储到本地
              TokenPermission tokenPermission = getHttpTokenPermission(token.getAccessToken());
              tokenPermissionStorage.create(token, tokenPermission);
          } else {
              logger.error("getHttpAccessToken has error, message:{}", result.getMessage());
          }
          return result;
      }
  
      /**
       * 获取未过期的用户权限信息
       *
       * @return
       */
      public static TokenPermission getTokenPermission(String accessToken) {
          ExpirationWrapper<TokenPermission> wrapper = tokenPermissionStorage.get(accessToken);
          if (wrapper == null || wrapper.checkExpired()) {
              return null;
          }
          return wrapper.getObject();
      }
  
      /**
       * 发送http请求刷新token，并写入cookie
       *
       * @param refreshToken
       * @return
       */
      public static Result<Token> getHttpRefreshTokenInCookie(String refreshToken) {
          Result<Token> result = getHttpRefreshToken(refreshToken);
          if (result.isSuccess()) {
              // Response写入cookie
              addCookieAccessToken(result.getData().getAccessToken());
              // 更新当前Request中Cookie的token值，让之后的请求能在Cookie中拿到新的token值。
              CookieUtils.updateCookie(getTokenName(), result.getData().getAccessToken(), ClientContextHolder.getRequest());
          }
          return result;
      }
  
      /**
       * 发送http请求刷新token
       *
       * @param refreshToken
       * @return
       */
      public static Result<Token> getHttpRefreshToken(String refreshToken) {
          String accessToken = tokenStorage.getAccessToken(refreshToken);
          if (!StringUtils.hasLength(accessToken)) {
              return Result.error("accessToken is null or expired!");
          }
          TokenPermission tokenPermission = getTokenPermission(accessToken);
          if (tokenPermission == null) {
              return Result.error("tokenPermission is null or expired!");
          }
          Result<Token> result = OAuth2Utils.getRefreshToken(properties.getServerUrl(), properties.getClientId(), refreshToken);
          if (result.isSuccess()) {
              // 删除旧token
              tokenStorage.remove(accessToken);
              // 删除旧用户权限信息
              tokenPermissionStorage.remove(accessToken);
  
              // 将token存储到本地
              tokenStorage.create(result.getData());
              // 存储用户权限信息到本地
              tokenPermissionStorage.create(result.getData(), tokenPermission);
          } else {
              logger.error("getHttpRefreshToken has error, message:{}", result.getMessage());
          }
          return result;
      }
  
      /**
       * 发送http请求用户权限信息
       *
       * @param accessToken
       */
      private static TokenPermission getHttpTokenPermission(String accessToken) {
          Result<TokenPermission> result = PermissionUtils.getUserPermission(properties.getServerUrl(), accessToken);
          if (!result.isSuccess()) {
              logger.error("getHttpTokenPermission has error, message:{}", result.getMessage());
              return new TokenPermission(Collections.emptySet(), Collections.emptySet(), Collections.emptyList());
          }
          return result.getData();
      }
  
      /**
       * 构建登录url
       *
       * @param redirectUri
       * @return
       */
      public static String buildLoginUrl(String redirectUri) {
          try {
              redirectUri = URLEncoder.encode(redirectUri, "utf-8");
          } catch (UnsupportedEncodingException e) {
              logger.error("buildLoginUrl has error, message:{}", e.getMessage());
          }
          return new StringBuilder()
                  .append(properties.getServerUrl())
                  .append(BaseConstant.LOGIN_PATH)
                  .append("?")
                  .append(BaseConstant.CLIENT_ID)
                  .append("=")
                  .append(properties.getClientId())
                  .append("&")
                  .append(BaseConstant.REDIRECT_URI)
                  .append("=")
                  .append(redirectUri).toString();
      }
  
      /**
       * 构建退出url
       *
       * @return
       */
      public static String buildLogoutUrl() {
          return buildLogoutUrl(getLocalUrl());
      }
  
      /**
       * 构建退出url
       *
       * @param redirectUri
       * @return
       */
      public static String buildLogoutUrl(String redirectUri) {
          try {
              redirectUri = URLEncoder.encode(redirectUri, "utf-8");
          } catch (UnsupportedEncodingException e) {
              logger.error("buildLogoutUrl has error, message:{}", e.getMessage());
          }
          return new StringBuilder()
                  .append(properties.getServerUrl())
                  .append(BaseConstant.LOGOUT_PATH)
                  .append("?")
                  .append(BaseConstant.REDIRECT_URI)
                  .append("=")
                  .append(redirectUri).toString();
      }
  
      /**
       * 获取当前应用访问路径
       *
       * @return
       */
      public static String getLocalUrl() {
          HttpServletRequest request = ClientContextHolder.getRequest();
          StringBuilder url = new StringBuilder();
          url.append(request.getScheme()).append("://").append(request.getServerName());
          if (request.getServerPort() != 80 && request.getServerPort() != 443) {
              url.append(":").append(request.getServerPort());
          }
          url.append(request.getContextPath());
          return url.toString();
      }
  }