最新消息: 新版网站上线了!!!

SpringMvc/SpringBoot HTTP通信加解密的实现

Ç°ÑÔ

´ÓÈ¥Äê10Ô·ݵ½ÏÖÔÚæµÄûʱ¼äд²©¿ÍÁË£¬½ñÌì¾Í˦¸ø´ó¼ÒÒ»¸ö¸É»õ°É£¡£¡£¡

½üÀ´ºÜ¶àÈËÎʵ½ÏÂÃæµÄÎÊÌâ

  1. ÎÒÃDz»ÏëÔÚÿ¸öController·½·¨ÊÕµ½×Ö·û´®±¨ÎĺóÔÙµ÷ÓÃÒ»´Î½âÃÜ£¬ËäÈ»¿ÉÒÔÍê³É£¬µ«ÊǺÜlow£¬ÇÒÈç¹ûÏë²»ÔÙʹÓüӽâÃÜ£¬ÐÞ¸ÄÆðÀ´ºÜÊÇÂé·³¡£
  2. ÎÒÃÇÏëÔÚʹÓÃRest¹¤¾ß»òswaggerÇëÇóµÄʱºò²»½øÐмӽâÃÜ£¬¶øÔÚappµ÷ÓõÄʱºò´¦Àí¼Ó½âÃÜ£¬Õâ¿ÉÈçºÎ²Ù×÷¡£

Õë¶ÔÒÔÉϵÄÎÊÌ⣬ÏÂÃæÖ±½Ó¸ø³ö½â¾ö·½°¸£º

ʵÏÖ˼·

  1. APPµ÷ÓÃAPIµÄʱºò£¬Èç¹ûÐèÒª¼Ó½âÃܵĽӿڣ¬ÐèÒªÔÚhttpHeaderÖиø³ö¼ÓÃÜ·½Ê½£¬Èçheader[encodeMethod]¡£
  2. Rest¹¤¾ß»òswaggerÇëÇóµÄʱºòÎÞÐèÖ¸¶¨´Ëheader¡£
  3. ºó¶ËAPIÊÕµ½requestºó£¬ÅжÏheaderÖеÄencodeMethod×ֶΣ¬Èç¹ûÓÐÖµ£¬ÔòÈÏΪÊÇÐèÒª½âÃÜ£¬·ñÔò¾ÍÈÏΪÊÇÃ÷ÎÄ¡£

Ô¼¶¨

ΪÁ˾«¼ò·ÖÏí¼¼Êõ£¬ÏÈÔ¼¶¨Ö»´¦ÀíPOSTÉÏ´«JSON(application/json)Êý¾ÝµÄ¼Ó½âÃÜ´¦Àí¡£

ÇëÇó½âÃÜʵÏÖ·½Ê½

1. Ïȶ¨Òåcontroller

@Controller
@RequestMapping("/api/demo")
public class MyDemoController {

  @RequestDecode
  @ResponseBody
  @RequestMapping(value = "user", method = RequestMethod.POST)
  public ResponseDto addUser(
      @RequestBody User user
  ) throws Exception {
    //TODO ...
  }

}
 

/**
 * ½âÃÜÇëÇóÊý¾Ý
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestDecode {

  SecurityMethod method() default SecurityMethod.NULL;

}

¿ÉÒÔ¿´µ½ÕâÀïµÄController¶¨ÒåµÄºÜÆÕͨ£¬Ö»ÓÐÒ»¸ö¶îÍâµÄ×Ô¶¨Òå×¢½âRequestDecode£¬Õâ¸ö×¢½âÊÇΪÁËÏÂÃæµÄRequestBodyAdviceµÄʹÓá£

2. ½¨Éè×Ô¼ºµÄRequestBodyAdvice

ÓÐÁËÉÏÃæµÄÈë¿Ú¶¨Ò壬½ÓÏÂÀ´´¦Àí½âÃÜÕâ¼þÊ£¬Ä¿µÄºÜÃ÷È·£º

1. ÊÇ·ñÐèÒª½âÃÜÅжÏhttpHeaderÖеÄencodeMethod×ֶΡ£

2. ÔÚ½øÈëcontroller֮ǰ¾Í½âÃÜÍê³É£¬ÊÇcontroller´¦ÀíÂß¼­ÎÞ¸ÐÖª¡£

DecodeRequestBodyAdvice.java

@Slf4j
@Component
@ControllerAdvice(basePackages = "com.xxx.hr.api.controller")
public class DecodeRequestBodyAdvice implements RequestBodyAdvice {

  @Value("${hrapi.aesKey}")
  String aesKey;
  @Value("${hrapi.googleKey}")
  String googleKey;

  @Override
  public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    return methodParameter.getMethodAnnotation(RequestDecode.class) != null
      && methodParameter.getParameterAnnotation(RequestBody.class) != null;
  }

  @Override
  public Object handleEmptyBody(Object body, HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    return body;
  }

  @Override
  public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    RequestDecode requestDecode = parameter.getMethodAnnotation(RequestDecode.class);
    if (requestDecode == null) {
      return request;//controller·½·¨²»ÒªÇó¼Ó½âÃÜ
    }
    String appId = request.getHeaders().getFirst(com.xxx.hr.bean.constant.HttpHeaders.APP_ID);//ÕâÀïÊÇÀ©Õ¹£¬¿ÉÒÔÖªµÀÀ´Ô´·½£¨È翪·Åƽ̨ʹÓã©

    String encodeMethod = request.getHeaders().getFirst(com.xxx.hr.bean.constant.HttpHeaders.ENCODE_METHOD);
     if (StringUtils.isEmpty(encodeMethod)) {
      return request;
    }
    SecurityMethod encodeMethodEnum = SecurityMethod.getByCode(encodeMethod); 
    //ÕâÀïÁé»îµÄ¿ÉÒÔÖ§³Öµ½¶àÖÖ¼Ó½âÃÜ·½Ê½
    switch (encodeMethodEnum) {
      case NULL:
        break;
      case AES: {
        InputStream is = request.getBody();
        ByteBuf buf = PooledByteBufAllocator.DEFAULT.heapBuffer();
        int ret = -1;
        int len = 0;
        while((ret = is.read()) > 0) {
          buf.writeByte(ret);
          len ++;
        }
        String body = buf.toString(0, len, xxxSecurity.DEFAULT_CHARSET);
        buf.release();
        String temp = null;
        try {
          temp = XxxSecurity.aesDecodeData(body, aesKey, googleKey, new CheckCallBack() {
            @Override
            public boolean isRight(String data) {
              return data != null && (data.startsWith("{") || data.startsWith("["));
            }
          });
          log.info("½âÃÜÍê³É: {}", temp);
          return new DecodedHttpInputMessage(request.getHeaders(), new ByteArrayInputStream(temp.getBytes("UTF-8")));
        } catch (DecodeException e) {
          log.warn("½âÃÜʧ°Ü appId: {}, Name:{} ´ý½âÃÜÃÜÎÄ: {}", appId, partnerName, body, e);
          throw e;
        }
      }
    }
    return request;
  }

  @Override
  public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    return body;
  }

  static class DecodedHttpInputMessage implements HttpInputMessage {
    HttpHeaders headers;
    InputStream body;

    public DecodedHttpInputMessage(HttpHeaders headers, InputStream body) {
      this.headers = headers;
      this.body = body;
    }

    @Override
    public InputStream getBody() throws IOException {
      return body;
    }

    @Override
    public HttpHeaders getHeaders() {
      return headers;
    }
  }
}

ÖÁ´Ë¼Ó½âÃÜÍê³ÉÁË¡£

¡ª¡ª¡ª¡ª¡ª¡ª¡ª¡ª-»ªÀö·Ö¸îÏß ¡ª¡ª¡ª¡ª¡ª¡ª¡ª¡ª¡ª¨C

ÏìÓ¦¼ÓÃÜ

ÏÂÃ渽¼þÒ»ÏÂÏìÓ¦¼ÓÃܹý³Ì£¬Ä¿µÄ

1. ControllerÂß¼­´úÂëÎÞ¸ÐÖª
2. ¿ÉÒÔÒ»¼ü¿ª¹ØÏìÓ¦¼ÓÃÜ

¶¨ÒåController

  @ResponseEncode
  @ResponseBody
  @RequestMapping(value = "employee", method = RequestMethod.GET)
  public ResponseDto<UserEEInfo> userEEInfo(
      @ApiParam("Óû§±àºÅ") @RequestParam(HttpHeaders.APPID) Long userId
  ) {
    //TODO ...
  }
 
/**
 * ¼ÓÃÜÏìÓ¦Êý¾Ý
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseEncode {

  SecurityMethod method() default SecurityMethod.NULL;

}

ÕâÀïµÄController¶¨ÒåµÄÒ²ºÜÆÕͨ£¬Ö»ÓÐÒ»¸ö¶îÍâµÄ×Ô¶¨Òå×¢½âResponseEncode£¬Õâ¸ö×¢½âÊÇΪÁËÏÂÃæµÄResponseBodyAdviceµÄʹÓá£

½¨Éè×Ô¼ºµÄResponseBodyAdvice

ÕâÀïÔ¼¶¨½«ÏìÓ¦µÄDTOÐòÁл¯ÎªJSON¸ñʽÊý¾Ý£¬È»ºóÔÙ¼ÓÃÜ£¬×îºóÔÚÏìÓ¦¸øÇëÇ󷽡£

@Slf4j
@Component
@ControllerAdvice(basePackages = "com.xxx.hr.api.controller")
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {

  @Autowired
  PartnerService partnerService;

  @Override
  public boolean supports(MethodParameter returnType, Class converterType) {
    return returnType.getMethodAnnotation(ResponseEncode.class) != null;
  }

  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    ResponseEncode responseEncode = returnType.getMethodAnnotation(ResponseEncode.class);
    String uid = request.getHeaders().getFirst(HttpHeaders.PARTNER_UID);
    if (uid == null) {
      uid = request.getHeaders().getFirst(HttpHeaders.APP_ID);
    }
    PartnerConfig config = partnerService.getConfigByAppId(uid);
    if (responseEncode.method() == SecurityMethod.NULL || responseEncode.method() == SecurityMethod.AES) {
      if (config == null) {
        return ResponseDto.rsFail(ResponseCode.E_403, "ÉÌ»§²»´æÔÚ");
      }
      String temp = JSON.toJSONString(body);
      log.debug("´ý¼ÓÃÜÊý¾Ý: {}", temp);
      String encodedBody = XxxSecurity.aesEncodeData(temp, config.getEncryptionKey(), config.getGoogleKey());
      log.debug("¼ÓÃÜÍê³É: {}", encodedBody);
      response.getHeaders().set(HttpHeaders.ENCODE_METHOD, HttpHeaders.VALUE.AES);
      response.getHeaders().set(HttpHeaders.HEADER_CONTENT_TYPE, HttpHeaders.VALUE.APPLICATION_BASE64_JSON_UTF8);
      response.getHeaders().remove(HttpHeaders.SIGN_METHOD);
      return encodedBody;
    }
    return body;

  }

}

ÍØÕ¹

ÓÉÉÏÃæµÄʵÏÖ£¬ÈçºÎʵÏÖRSAÑé֤ǩÃûÄØ£¿Õâ¸ö¾Í¼òµ¥ÁË£¬Çë¿´·Ö½â¡£

Ä¿µÄ»¹ÊǺܼòµ¥£¬½øÀ´¼õÉÙ¶ÔÒµÎñÂß¼­µÄÈëÇÖ¡£

Ê×ÏÈÉ趨һÏÂÄÇЩÇëÇóÐèÒªÑé֤ǩÃû

  @RequestSign
  @ResponseEncode
  @ResponseBody
  @RequestMapping(value = "employee", method = RequestMethod.GET)
  public ResponseDto<UserEEInfo> userEEInfo(
      @RequestParam(HttpHeaders.UID) String uid
  ) {
    //TODO ...
  }

ÕâÀﻹÊÇʹÓÃÒ»¸ö×¢½âRequestSign£¬È»ºóÔÙʵÏÖÒ»¸öSignInterceptor¼´¿ÉÍê³É£º

@Slf4j
@Component
public class SignInterceptor implements HandlerInterceptor {

  @Autowired
  PartnerService partnerService;

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod method = (HandlerMethod) handler;
    RequestSign requestSign = method.getMethodAnnotation(RequestSign.class);
    if (requestSign == null) {
      return true;
    }

    String appId = request.getHeader(HttpHeaders.APP_ID);
    ValidateUtils.notTrimEmptyParam(appId, "Header[appId]");

    PartnerConfig config = partnerService.getConfigByAppId(appId);
    ValidateUtils.notNull(config, Code.E_400, "ÉÌ‘ô²»´æÔÚ");
    String partnerName = partnerService.getPartnerName(appId);

    String sign = request.getParameter(HttpHeaders.SIGN);
    String signMethod = request.getParameter(HttpHeaders.SIGN_METHOD);
    signMethod = (signMethod == null) ? "RSA" : signMethod;
    Map<String, String[]> parameters = request.getParameterMap();
    ValidateUtils.notTrimEmptyParam(sign, "sign");
    if ("RSA".equals(signMethod)) {
      sign = sign.replaceAll(" ", "+");
      boolean isOK = xxxxSecurity.signVerifyRequest(parameters, config.getRsaPublicKey(), sign, config.getSecurity());
      if (isOK) {
        log.info("ÑéÖ¤ÉÌ»§Ç©Ãûͨ¹ý {}[{}] ", appId, partnerName);
        return true;
      } else {
        log.warn("ÑéÖ¤ÉÌ»§Ç©Ãûʧ°Ü {}[{}] ", appId, partnerName);
      }
    } else {
      throw new SignVerifyException("Ôݲ»Ö§³Ö¸ÃÇ©Ãû");
    }
    throw new SignVerifyException("Ç©ÃûУÑéʧ°Ü");
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

  }
}

¸÷¸öö¾Ù¶¨Ò壺

//¼Ó½âÃÜ¡¢Ç©ÃûË㷨ö¾Ù
public enum SecurityMethod {

  NULL,

  AES,
  RSA,
  DES,
  DES3,

  SHA1,
  MD5
  ;

}

×¢½â¶¨Ò壺

/**
 * ÇëÇóÊý¾ÝÊý¾ÝÐèÒª½âÃÜ
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestDecode {

  SecurityMethod method() default SecurityMethod.NULL;

}

/**
 * ÇëÇóÊý¾ÝÐèÒªÑéÇ©
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RequestSign {

  SecurityMethod method() default SecurityMethod.RSA;

}

/**
 * Êý¾ÝÏìÓ¦ÐèÒª¼ÓÃÜ
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseEncode {

  SecurityMethod method() default SecurityMethod.NULL;

}

/**
 * ÏìÓ¦Êý¾ÝÐèÒªÉú³ÉÇ©Ãû
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ResponseSign {

  SecurityMethod method() default SecurityMethod.NULL;

}
 

aesDecodeData

/**
   * AES ½âÃÜÊý¾Ý
   *
   * @param data       ´ý½âÃÜÊý¾Ý
   * @param aesKey      AES ÃÜÔ¿(BASE64)
   * @param googleAuthKey   GoogleAuthKey(BASE64)
   * @param originDataSign  ԭʼÊý¾Ýmd5Ç©Ãû
   * @return
   */
  public static String aesDecodeDataEx(String data, String aesKey, String googleAuthKey, String originDataSign) {
    return aesDecodeData(data, aesKey, googleAuthKey, System.currentTimeMillis(), null, originDataSign);
  }

  public static String aesDecodeData(String data, String aesKey, String googleAuthKey, long tm, CheckCallBack checkCallBack, String originDataSign) {
    DecodeException lastError = null;
    long timeWindow = googleAuth.getTimeWindowFromTime(tm);
    int window = googleAuth.getConfig().getWindowSize();
    for (int i = -((window - 1) / 2); i <= window / 2; ++i) {
      String googleCode = googleAuth.calculateCode16(Base64.decodeBase64(googleAuthKey), timeWindow + i);
      log.debug((timeWindow + i) + " googleCode: " + googleCode);
      byte[] code = googleCode.getBytes(DEFAULT_CHARSET);
      byte[] iv = new byte[16];
      System.arraycopy(code, 0, iv, 0, code.length);
      try {
        String newKey = convertKey(aesKey, iv);
        String decodedData = AES.decode(data, newKey, Base64.encodeBase64String(iv));
        if (checkCallBack != null && !checkCallBack.isRight(decodedData)) {
          continue;
        }
        if (originDataSign != null) {
          String sign = DigestUtils.md5Hex(decodedData);
          if (!sign.equalsIgnoreCase(originDataSign)) {
            continue;
          }
        }
        return decodedData;
      } catch (DecodeException e) {
        lastError = e;
      }
    }
    if (lastError == null) {
      lastError = new DecodeException("Decode Failed, Error Password!");
    }
    throw lastError;
  }

signVerifyRequest

static boolean signVerifyRequest(Map<String, String[]> parameters, String rsaPublicKey, String sign, String security) throws SignVerifyException {
    String preSignData = getHttpPreSignData(parameters, security);
    log.debug("´ýÑéÇ©×Ö·û´®£º" + preSignData);
    return RSA.verify(preSignData.getBytes(DEFAULT_CHARSET), rsaPublicKey, sign);
  }
 

GoogleAuth

public class GoogleAuth {

  private GoogleAuthenticatorConfig config;
  private GoogleAuthenticator googleAuthenticator;

  public GoogleAuth() {
    GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder gacb =
        new GoogleAuthenticatorConfig.GoogleAuthenticatorConfigBuilder()
            .setTimeStepSizeInMillis(TimeUnit.MINUTES.toMillis(2))
            .setWindowSize(3)
            .setCodeDigits(8)
            .setKeyRepresentation(KeyRepresentation.BASE64);

    config = gacb.build();
    googleAuthenticator = new GoogleAuthenticator(config);
  }

  public GoogleAuthenticatorConfig getConfig(){
    return config;
  }

  public void setConfig(GoogleAuthenticatorConfig c) {
    config = c;
    googleAuthenticator = new GoogleAuthenticator(config);
  }


  /**
   * ÈÏÖ¤
   * @param encodedKey(Base 32/64)
   * @param code
   * @return ÊÇ·ñͨ¹ý
   */
  public boolean authorize(String encodedKey, int code) {
    return googleAuthenticator.authorize(encodedKey, code);
  }

  /**
   * Éú³É GoogleAuth Code
   * @param keyBase64
   * @return
   */
  public int getCodeValidCode(String keyBase64) {
    int code = googleAuthenticator.getTotpPassword(keyBase64);
    return code;
  }

  public long getTimeWindowFromTime(long time)
  {
    return time / this.config.getTimeStepSizeInMillis();
  }

  private static String formatLabel(String issuer, String accountName) {
    if (accountName == null || accountName.trim().length() == 0) {
      throw new IllegalArgumentException("Account name must not be empty.");
    }
    StringBuilder sb = new StringBuilder();
    if (issuer != null) {
      if (issuer.contains(":")) {
        throw new IllegalArgumentException("Issuer cannot contain the \':\' character.");
      }
      sb.append(issuer);
      sb.append(":");
    }
    sb.append(accountName);
    return sb.toString();
  }

  public String getOtpAuthTotpURL(String keyBase64) throws EncoderException{
    return getOtpAuthTotpURL("MLJR", "myname@mljr.com", keyBase64);
  }

  /**
   * Éú³ÉGoogleAuthÈÏÖ¤µÄURL,±ãÓÚÉú³É¶þάÂë
   * @param issuer
   * @param accountName
   * @param keyBase32
   * @return
   */
  public String getOtpAuthTotpURL(String issuer, String accountName, String keyBase32) throws EncoderException {
    StringBuilder url = new StringBuilder();
    url.append("otpauth://")
        .append("totp")
        .append("/").append(formatLabel(issuer, accountName));
    Map<String, String> parameter = new HashMap<String, String>();
    /**
     * https://github.com/google/google-authenticator/wiki/Key-Uri-Format
     * The secret parameter is an arbitrary key value encoded in Base32 according to RFC 3548.
     */
    parameter.put("secret", keyBase32);
    if (issuer != null) {
      if (issuer.contains(":")) {
        throw new IllegalArgumentException("Issuer cannot contain the \':\' character.");
      }
      parameter.put("issuer", issuer);
    }
    parameter.put("algorithm", "SHA1");
    parameter.put("digits", String.valueOf(config.getCodeDigits()));
    parameter.put("period", String.valueOf(TimeUnit.MILLISECONDS.toSeconds(config.getTimeStepSizeInMillis())));
    URLCodec urlCodec = new URLCodec();
    if (!parameter.isEmpty()) {
      url.append("?");
      for(String key : parameter.keySet()) {
        String value = parameter.get(key);
        if (value == null){
          continue;
        }
        value = urlCodec.encode(value);
        url.append(key).append("=").append(value).append("&");
      }
    }
    return url.toString();

  }

  private static final String DEFAULT_RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";
  private static final String DEFAULT_RANDOM_NUMBER_ALGORITHM_PROVIDER = "SUN";
  private static final String HMAC_HASH_FUNCTION = "HmacSHA1";
  private static final String HMAC_MD5_FUNCTION = "HmacMD5";

  /**
   * »ùÓÚʱ¼ä Éú³É16λµÄ code
   * @param key
   * @param tm
   * @return
   */
  public String calculateCode16(byte[] key, long tm)
  {
    // Allocating an array of bytes to represent the specified instant
    // of time.
    byte[] data = new byte[8];
    long value = tm;

    // Converting the instant of time from the long representation to a
    // big-endian array of bytes (RFC4226, 5.2. Description).
    for (int i = 8; i-- > 0; value >>>= 8)
    {
      data[i] = (byte) value;
    }

    // Building the secret key specification for the HmacSHA1 algorithm.
    SecretKeySpec signKey = new SecretKeySpec(key, HMAC_HASH_FUNCTION);
    try
    {
      // Getting an HmacSHA1 algorithm implementation from the JCE.
      Mac mac = Mac.getInstance(HMAC_HASH_FUNCTION);
      // Initializing the MAC algorithm.
      mac.init(signKey);
      // Processing the instant of time and getting the encrypted data.
      byte[] hash = mac.doFinal(data);
      // Building the validation code performing dynamic truncation
      // (RFC4226, 5.3. Generating an HOTP value)
      int offset = hash[hash.length - 1] & 0xB;
      // We are using a long because Java hasn't got an unsigned integer type
      // and we need 32 unsigned bits).
      long truncatedHash = 0;
      for (int i = 0; i < 8; ++i)
      {
        truncatedHash <<= 8;
        // Java bytes are signed but we need an unsigned integer:
        // cleaning off all but the LSB.
        truncatedHash |= (hash[offset + i] & 0xFF);
      }

      truncatedHash &= Long.MAX_VALUE;
      truncatedHash %= 10000000000000000L;
      // module with the maximum validation code value.
      // Returning the validation code to the caller.
      return String.format("%016d", truncatedHash);
    } catch (InvalidKeyException e) {
      throw new GoogleAuthenticatorException("The operation cannot be "
          + "performed now.");
    } catch (NoSuchAlgorithmException ex) {
      // We're not disclosing internal error details to our clients.
      throw new GoogleAuthenticatorException("The operation cannot be "
          + "performed now.");
    }
  }
}

GoogleAuthÆäËû´úÂë ¿´ÕâÀï

ÒÔÉϾÍÊDZ¾ÎĵÄÈ«²¿ÄÚÈÝ£¬Ï£Íû¶Ô´ó¼ÒµÄѧϰÓÐËù°ïÖú£¬Ò²Ï£Íû´ó¼Ò¶à¶àÖ§³Ö½Å±¾Ö®¼Ò¡£

转载请注明:谷谷点程序 » SpringMvc/SpringBoot HTTP通信加解密的实现