【小工具】WebClient远程调用,返回值将Long类型转换为String,自定义注解

文章目录

    • 1. 使用WebClient使用远程调用
    • 2. 返回值将Long类型转换为String
    • 3. 自定义注解
    • 4. 加密方式

1. 使用WebClient使用远程调用


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webfluxartifactId>
dependency>
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.Map;
===========================================================================

/**
 * 远程调用,发送post请求
 *
 * @param pathUrl     请求的访问的路径 如:/art/approval
 * @param requestBody 请求体(也可以是自定义的某个实体类)
 * @return 请求返回的结果,用R接收
 */
private R remotePostCall(String pathUrl, Map<String, String> requestBody) {
    WebClient client = WebClient.create();
    // 发起POST请求,并设置请求头和请求体
    Mono<R> responseMono = client.post()
            .uri(uri + pathUrl)
            // 设置请求头
            .header(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8")
            .header(HttpHeaders.AUTHORIZATION, "you token")
            // 设置请求体
            .body(BodyInserters.fromValue(requestBody))
            .retrieve()
            // 将返回结果转换为R类型
            .bodyToMono(R.class);
    R block = responseMono.block();
    return block;
}

这个请求是异步的。在代码中,使用了 Mono 来表示返回结果, Mono 是一个Reactive类型,它表示可能会在未来某个时间点返回的结果。通过使用 bodyToMono() 方法将返回结果转换为 Mono 类型,然后使用 block() 方法来阻塞等待结果的返回。这种异步的方式可以提高应用程序的性能和并发处理能力。

可以使用 subscribe() 方法来实现不阻塞等待结果的返回。通过调用 subscribe() 方法,可以注册一个回调函数来处理异步返回的结果。这样,可以在结果返回时执行相应的操作,不需要阻塞等待。

private void remotePostCall(String pathUrl, Map<String, String> requestBody) {
    WebClient client = WebClient.create();
    // 发起POST请求,并设置请求头和请求体
    client.post()
            .uri(uri + pathUrl)
            // 设置请求头
            .header(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8")
            .header(HttpHeaders.AUTHORIZATION, "you token")
            // 设置请求体
            .body(BodyInserters.fromValue(requestBody))
            .retrieve()
            // 处理返回结果
            .bodyToMono(R.class)
            .subscribe(result -> {
                // 在这里处理异步返回的结果
                // 可以执行相应的操作
                System.out.println("异步结果:" + result);
            });
}

2. 返回值将Long类型转换为String

在分布式项目中,我们主键喜欢使用雪花Id,可能会遇到一些精度丢失或数据截断的问题,特别是在处理大整数时。将 Long 类型转换为 String 类型可以避免这些问题,并确保数据的准确性和完整性。

通过自定义Jackson的对象映射器行为,可以确保在分布式项目中处理长整型数据时的一致性和可靠性。

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfiguration {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // 返回结果时,统一将 Long 转换成 String
            builder.serializerByType(Long.class, ToStringSerializer.instance);
        };
    }
}

3. 自定义注解

  1. 自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author shang tf
 * @createTime 2023/8/15 10:35
 * @Version 1.0.0
 * 需要成为数商后才能访问
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface HasPayAuth {

}
  1. 切面拦截注解
import cn.creatoo.common.core.constant.Constants;
import cn.creatoo.common.security.utils.BdSecurityUtils;
import cn.creatoo.system.exception.NotPayAuthException;
import cn.creatoo.system.domain.BdTerminalUserAuth;
import cn.creatoo.system.mapper.BdTerminalUserAuthMapper;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * @Author shang tf
 * @createTime 2023/8/15 10:41
 * @Version 1.0.0
 */
@Aspect
@Component
public class HasPayAuthAspect {

    @Pointcut("@annotation(xx.xxxxx.system.annotation.HasPayAuth)")
    private void pointcut() {
    }

    @Autowired
    private BdTerminalUserAuthMapper bdTerminalUserAuthMapper;

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 查询当前登录账号信息
        Long userId = BdSecurityUtils.getUserId();
        QueryWrapper<BdTerminalUserAuth> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId);
        BdTerminalUserAuth bdTerminalUserAuth = bdTerminalUserAuthMapper.selectOne(wrapper);
        if (ObjectUtil.isEmpty(bdTerminalUserAuth)){
            throw new NotPayAuthException("未提交认证,请先认证");
        }
        // 账号状态
        if (!bdTerminalUserAuth.getStatus().equals(Constants.AUTH_AUDIT_CERTIFIED)){
            throw new NotPayAuthException("账号未认证,请先认证");
        }
        // 判断账号是否被禁用
        if (bdTerminalUserAuth.getIsAble().equals(Constants.AUTH_IS_ABLE_OFF)){
            throw new NotPayAuthException("账号被禁用,请联系管理员");
        }
		// 放行
        return joinPoint.proceed(joinPoint.getArgs());
    }
}
  1. 自定义异常

自定义异常时需要继承RuntimeException

/**
 * @Author shang tf
 * @createTime 2023/8/15 10:58
 * @Version 1.0.0
 * 资质认证过期或未认证异常
 */
public class NotPayAuthException extends RuntimeException {
    public NotPayAuthException() {
    }

    public NotPayAuthException(String message){
        super(message);
    }
}

4. 加密方式

MD5加密

请注意,MD5算法是单向散列函数,不可逆。因此,我们无法从MD5加密后的值还原出原始字符串。MD5算法通常用于密码存储和验证等场景,但由于其安全性较低,现在更常用的是更强大的加密算法,如SHA-256等。

ADCryptUtils
通过此工具类可以将字符串进行加密解密,可以解析出原本的字符串

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;

public class ADCryptUtils {

    static { try { Security.addProvider(new BouncyCastleProvider()); } catch (Exception e) { e.printStackTrace(); } }

    // vi 076ce634d7da11eb
    private static final byte[] DES_DEFAULT_KEY=new byte[] { 10, 20, 50, 99, 101, 54, 51, 52, 100, 55, 100, 97, 49, 49, 112, 98 };
    private static final byte[] AES_DEFAULT_KEY=new byte[] { 10, 20, 50, 99, 101, 54, 51, 52, 100, 55, 100, 97, 49, 49, 112, 98 };


    /**
     * content: 加密内容
     */
    public static String encryptAes(String content) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(AES_DEFAULT_KEY, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(AES_DEFAULT_KEY));
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return Base64.encode(encrypted);
    }
    /**
     * content: 解密内容
     */
    public static String decryptAes(String base64Content) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(AES_DEFAULT_KEY, "AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(AES_DEFAULT_KEY));
        byte[] content = Base64.decode(base64Content);
        byte[] encrypted = cipher.doFinal(content);
        return new String(encrypted);
    }
    /**
     * content: 加密内容
     */
    public static String encryptDes(String content) throws Exception {
        Cipher cipher = Cipher.getInstance("DES");
        SecretKey secretKey = new SecretKeySpec(DES_DEFAULT_KEY, "DES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return Base64.encode(encrypted);
    }
    /**
     * content: 解密内容
     */
    public static String decryptDes(String base64Content) throws Exception {
        Cipher cipher = Cipher.getInstance("DES");
        SecretKey secretKey = new SecretKeySpec(DES_DEFAULT_KEY, "DES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] content = Base64.decode(base64Content);
        byte[] encrypted = cipher.doFinal(content);
        return new String(encrypted);
    }
    /**
     * content: 加密内容
     * slatKey: 加密的盐,16位字符串
     */
    public static String encryptAes(String content, String slatKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(AES_DEFAULT_KEY));
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return Base64.encode(encrypted);
    }
    /**
     * content: 解密内容
     * slatKey: 加密时使用的盐,16位字符串
     */
    public static String decryptAes(String base64Content, String slatKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(AES_DEFAULT_KEY));
        byte[] content = Base64.decode(base64Content);
        byte[] encrypted = cipher.doFinal(content);
        return new String(encrypted);
    }
    /**
     * content: 加密内容
     * slatKey: 加密的盐,16位字符串
     * vectorKey: 加密的向量,16位字符串
     */
    public static String encryptAes(String content, String slatKey, String vectorKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return Base64.encode(encrypted);
    }
    /**
     * content: 解密内容(base64编码格式)
     * slatKey: 加密时使用的盐,16位字符串
     * vectorKey: 加密时使用的向量,16位字符串
     */
    public static String decryptAes(String base64Content, String slatKey, String vectorKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
        byte[] content = Base64.decode(base64Content);
        byte[] encrypted = cipher.doFinal(content);
        return new String(encrypted);
    }
    /**
     * content: 加密内容
     * slatKey: 加密的盐,8位字符串
     */
    public static String encryptDes(String content, String slatKey) throws Exception {
        Cipher cipher = Cipher.getInstance("DES");
        SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "DES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return Base64.encode(encrypted);
    }
    /**
     * content: 解密内容
     * slatKey: 加密时使用的盐,8位字符串
     */
    public static String decryptDes(String base64Content, String slatKey) throws Exception {
        Cipher cipher = Cipher.getInstance("DES");
        SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "DES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] content = Base64.decode(base64Content);
        byte[] encrypted = cipher.doFinal(content);
        return new String(encrypted);
    }

    public static void main(String[] args) {
        try {
            String encryptDes = ADCryptUtils.encryptAes("测试AES加密");
            System.out.println(encryptDes);
            String decryptDes = ADCryptUtils.decryptAes(encryptDes);
            System.out.println(decryptDes);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(java,spring,boot,开发语言)