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

前言

从去年10月份到现在忙的没时间写博客了,今天就甩给大家一个干货吧!!!

近来很多人问到下面的问题

  1. 我们不想在每个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;
    }
  }
}

至此加解密完成了。

————————-华丽分割线 —————————–

响应加密

下面附件一下响应加密过程,目的

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其他代码 看这里

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • springboot添加https服务器的方法

    什么是https 要说https我们得先说SSL(Secure Sockets Layer,安全套接层),这是一种为网络通信提供安全及数据完整性的一种安全协议,SSL在网络传输层对网络连接进行加密.SSL协议可以分为两层:SSL记录协议(SSL Record Protocol),它建立在可靠的传输协议如TCP之上,为高层协议提供数据封装.压缩.加密等基本功能支持:SSL握手协议(SSL Handshake Protocol),它建立在SSL记录协议之上,用于在实际数据传输开始之前,通信双方进行身

  • Spring boot自定义http反馈状态码详解

    前言 最近在开发一些http server类型程序,通过spring boot构建一些web程序,这些web程序之间通过http进行数据访问.共享,如下图, 假设现在client发起一次保存数据的请求到server,server可能会返回如下类似的数据 { "status":1, "message":"xxxxxx" } 然后client通过解析json获得status来判断当前的请求操作是否成功,开发过程中通过都是这么做的,但是这样在restf

  • spring boot中使用http请求的示例代码

    因为项目需求,需要两个系统之间进行通信,经过一番调研,决定使用http请求. 服务端没有什么好说的,本来就是使用web 页面进行访问的,所以spring boot启动后,controller层的接口就自动暴露出来了,客户端通过调用对应的url即可,所以这里主要就客户端. 首先我自定义了一个用来处理http 请求的工具类DeviceFactoryHttp,既然是url访问,那就有两个问题需要处理,一个请求服务的url,和请求服务端的参数,客户端的消息头请求服务的url:请求服务端url我定义的是跟

  • SpringBoot2.0如何启用https协议

    SpringBoot2.0之后,启用https协议的方式与1.*时有点儿不同,贴一下代码. 我的代码能够根据配置参数中的condition.http2https,确定是否启用https协议,如果启用https协议时,会将所有http协议的访问,自动转到https协议上. 一.启动程序  package com.wallimn.iteye.sp.asset; import org.apache.catalina.Context; import org.apache.catalina.connect

  • springboot配置https访问的方法

    1.购买或本地生成ssl证书 要使用https,首先需要证书,获取证书的两种方式: 1.自己通过keytool生成 2.通过证书授权机构购买 ###### 作为演示,我们使用keytool生成: C:\Users\xxx>keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 输入密钥库口令: 再次输入新口令: 您的名字与姓氏是什

  • 在Spring Boot中实现HTTP缓存的方法

    缓存是HTTP协议的一个强大功能,但由于某些原因,它主要用于静态资源,如图像,CSS样式表或JavaScript文件,但是,HTTP缓存不仅限于这些,还可以将其用于动态计算的资源. 通过少量工作,您可以加快应用程序并改善整体用户体验.在本文中,您将学习 如何使用内置的HTTP响应缓存机制来实现缓存SpringBoot控制器的结果 . 1.如何以及何时使用HTTP响应缓存? 您可以在应用程序的多个层上进行缓存.数据库具有其缓存存储,Web客户端也在其需要重用的信息.HTTP协议负责网络通信.缓存机

  • spring boot封装HttpClient的示例代码

    最近使用到了HttpClient,看了一下官方文档:HttpClient implementations are expected to be thread safe. It is recommended that the same instance of this class is reused for multiple request executions,翻译过来的意思就是:HttpClient的实现是线程安全的,可以重用相同的实例来执行多次请求.遇到这种描述的话,我们就应该想到,需要对H

  • 详解在Spring Boot中使用Https

    本文介绍如何在Spring Boot中,使用Https提供服务,并将Http请求自动重定向到Https. Https证书 巧妇难为无米之炊,开始的开始,要先取得Https证书.你可以向证书机构申请证书,也可以自己制作根证书. 创建Web配置类 在代码中创建一个使用了Configuration注解的类,就像下面这段代码一样: @Configuration public class WebConfig { //Bean 定义... } 配置Https 在配置类中添加EmbeddedServletCo

  • spring boot配置ssl实现HTTPS的方法

    传输层安全性协议(英语:Transport Layer Security,缩写作 TLS),及其前身安全套接层(Secure Sockets Layer,缩写作 SSL)是一种安全协议,目的是为互联网通信,提供安全及数据完整性保障.网景公司(Netscape)在1994年推出首版网页浏览器,网景导航者时,推出HTTPS协议,以SSL进行加密,这是SSL的起源.IETF将SSL进行标准化,1999年公布第一版TLS标准文件.随后又公布RFC 5246 (2008年8月)与 RFC 6176 (20

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

    前言 从去年10月份到现在忙的没时间写博客了,今天就甩给大家一个干货吧!!! 近来很多人问到下面的问题 我们不想在每个Controller方法收到字符串报文后再调用一次解密,虽然可以完成,但是很low,且如果想不再使用加解密,修改起来很是麻烦. 我们想在使用Rest工具或swagger请求的时候不进行加解密,而在app调用的时候处理加解密,这可如何操作. 针对以上的问题,下面直接给出解决方案: 实现思路 APP调用API的时候,如果需要加解密的接口,需要在httpHeader中给出加密方式,如h

  • SpringBoot接口数据加解密实战记录

    这日,刚撸完2行代码,正准备掏出手机摸鱼放松放松,只见老大朝我走过来,并露出一个”善意“的微笑,兴伟呀,xx项目有于安全问题,需要对接口整体进行加密处理,你这方面比较有经验,就给你安排上了哈,看这周内提测行不...,额,摸摸头上飘摇着而稀疏的长发,感觉我爱了. 和产品.前端同学对外需求后,梳理了相关技术方案, 主要的需求点如下: 尽量少改动,不影响之前的业务逻辑: 考虑到时间紧迫性,可采用对称性加密方式,服务需要对接安卓.IOS.H5三端,另外考虑到H5端存储密钥安全性相对来说会低一些,故分针对

  • SpringBoot使用jasypt加解密密码的实现方法(二)

    在我们的服务中不可避免的需要使用到一些秘钥(数据库.redis等) 开发和测试环境还好,但生产如果采用明文配置讲会有安全问题,jasypt是一个通用的加解密库,我们可以使用它. <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>2.1.1</ve

  • SpringBoot使用jasypt加解密密码的实现方法

    jasypt是一个通用的加解密库,我们可以使用它在配置文件中对数据库密码进行加密,以确保其安全性. 1.注入依赖 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> 2.

  • SpringBoot实现接口数据的加解密功能

    一.加密方案介绍 对接口的加密解密操作主要有下面两种方式: 自定义消息转换器 优势:仅需实现接口,配置简单. 劣势:仅能对同一类型的MediaType进行加解密操作,不灵活. 使用spring提供的接口RequestBodyAdvice和ResponseBodyAdvice 优势:可以按照请求的Referrer.Header或url进行判断,按照特定需要进行加密解密. 比如在一个项目升级的时候,新开发功能的接口需要加解密,老功能模块走之前的逻辑不加密,这时候就只能选择上面的第二种方式了,下面主要

  • springboot中request和response的加解密实现代码

    目录 1.新建一个springboot工程,pom依赖如下 2.自定义加密.解密的注解 3.加密算法 4.对请求数据进行解密处理 5.对响应数据进行加密处理 6.加解密的key的配置类,从配置文件中读取 7.测试 在系统开发中,需要对请求和响应分别拦截下来进行解密和加密处理,在springboot中提供了RequestBodyAdviceAdapter和ResponseBodyAdvice,利用这两个工具可以非常方便的对请求和响应进行预处理. 1.新建一个springboot工程,pom依赖如下

  • SpringBoot项目使用jasypt加解密的方法

    目录 一.添加依赖 二.生成加密数据 三.对加密数据进行解密 四.在SpringBoot项目中使用 Jasypt 是一个 Java 库,它允许开发者以最小的努力为他 / 她的项目添加基本的加密功能,而且不需要对密码学的工作原理有深刻的了解. 地址:https://github.com/jasypt/jasypt 一.添加依赖 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId&g

  • 详解PHP数据压缩、加解密(pack, unpack)

    网络通信.文件存储中经常需要交换数据,为了减少网络通信流量.文件存储大小以及加密通信规则,经常需要对数据进行双向加解密以保证数据的安全. PHP中实现此功能主要需要使用的函数主要是pack及unpack函数 pack 压缩资料到位字符串之中. 语法: string pack(string format, mixed [args]...); 返回值: 字符串 本函数用来将资料压缩打包到位的字符串之中. a - NUL- 字符串填满[padded string] 将字符串空白以 NULL 字符填满

  • python实现AES和RSA加解密的方法

    本文实例为大家分享了python实现AES和RSA加解密的具体代码,供大家参考,具体内容如下 AES AES 是一种对称加密算法,用key对一段text加密,则用同一个key对密文解密, from Crypto import Random from Crypto.Hash import SHA from Crypto.Cipher import AES from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 from Crypto.Si

  • Spring boot配置文件加解密详解

    功能介绍 大家都知道在Spring boot开发过程中,需要在配置文件里配置许多信息,如数据库的连接信息等,如果不加密,传明文,数据库就直接暴露了,相当于"裸奔"了,因此需要进行加密处理才行. 在项目中使用jasypt-1.9.4.jar包,能够实现对明文进行加密,对密文进行解密.配置相关加密信息,就能够实现在项目运行的时候,自动把配置文件中已经加密的信息解密成明文,供程序使用 下面话不多说了,来一起看看详细的介绍吧 使用说明 1.pom引入依赖 <dependency>

随机推荐