Java develops an interface to provide third-party calls

1. Environment

Write an interface based on SpringBoot to provide third-party calls. Similar to how we use Alibaba's speech recognition function, we can call Alibaba's packaged API, which is to do speech recognition by sending HTTP requests. This article mainly records how we develop interfaces in SpringBoot and allow others to call them safely.

Dependencies used:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.lukeewin</groupId>
    <artifactId>Signature</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Signature</name>
    <description>Signature</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. Selection of encryption algorithm

Encryption using the MD5 algorithm is not very secure, so here we use HmacSHA256the encryption algorithm in the hash algorithm to generate a signature. When we request the interface, we use the signature and timestamp. Why do we need to bring the timestamp? Well, it's because if we want to control the expiration time of the signature later, we need to calculate the expiration time based on the timestamp passed by the front end.

The following is the encryption tool class:

public class SignatureUtil {
    
    
    public static String getSignature(String timestamp, String apiKey, String apiSecret) {
    
    
        // 构建签名字符串
        String signatureString = apiKey + timestamp;

        String signature = null;

        // 计算签名
        try {
    
    
            Mac sha256Hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha256Hmac.init(secretKey);
            byte[] signatureBytes = sha256Hmac.doFinal(signatureString.getBytes(StandardCharsets.UTF_8));
            signature = Base64.getEncoder().encodeToString(signatureBytes);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
    
    
            throw new RuntimeException(e);
        }
        return signature;
    }
}

3. Write an interface

Here we simply write an audio transcoding interface to simulate the entire process of developing our interface.

@RestController
public class TransferController {
    
    

    @RequestMapping("/transfer")
    public BaseResponse transfer() {
    
    
        return BaseResponse.success("转码成功");
    }

    @RequestMapping("/ban")
    public BaseResponse ban() {
    
    
        return BaseResponse.error(ErrorCode.VERIFY_NO_PASS);
    }
}

4. Customize an interceptor

Customize the interceptor to intercept the comprehensive request and determine whether the signature and timestamp are passed. It also determines whether the signature passed is inconsistent with the signature calculated by the backend. It also needs to determine whether the signature has expired when it is passed to the backend. If If one of the above conditions is not met, it will be intercepted, otherwise it will be released.

@Component
public class SignatureInterceptor implements HandlerInterceptor {
    
    
    @Value("${apiKey}")
    private String apiKey;

    @Value("${apiSecret}")
    private String apiSecret;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        String sign = request.getParameter("sign");
        String timestamp = request.getParameter("timestamp");
        if (StringUtils.isNotBlank(sign) && StringUtils.isNotBlank(timestamp)) {
    
    
            String signature = SignatureUtil.getSignature(timestamp, apiKey, apiSecret);
            if (StringUtils.isNotBlank(signature) && signature.equals(sign) && System.currentTimeMillis() - Long.parseLong(timestamp) < 50 * 1000) {
    
    
                return true;
            } else {
    
    
                request.getRequestDispatcher("/ban").forward(request, response);
                return false;
            }
        } else {
    
    
            request.getRequestDispatcher("/ban").forward(request, response);
            return false;
        }
    }
}

important point:

  1. This class must be handed over to the Spring IOC container for management, that is, an annotation needs to be added to the class.@Component
  2. After interception, a prompt needs to be given to the caller, otherwise the caller does not know whether it has been intercepted, so it needs to be used hererequest.getRequestDispatcher("/ban").forward(request, response);
  3. The interception must be released URL. If not released, an infinite loop will occur. Here, /banthe interface needs to be released.

5. Write an interceptor configuration class

Write an interceptor configuration class and add the custom interceptor to the configuration class.

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    
    
    @Resource
    private SignatureInterceptor signatureInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(signatureInterceptor).addPathPatterns("/**").excludePathPatterns("/ban");
    }
}

important point:

  1. @ConfigurationAnnotations are required
  2. It cannot be added using new custom interceptor class, it must be injected using injection. That is to say, it cannot be written like thisregistry.addInterceptor(new SignatureInterceptor()).addPathPatterns("/**")

6. Unify the response format of the interface

Create two tool classes, one is the response base class and the other is the error class.

Response base class:BaseResponse

@Data
public class BaseResponse<T> implements Serializable {
    
    
    private static final long serialVersionUID = 4L;
    private Integer code;
    private String message;
    private Long timestamp = System.currentTimeMillis();
    private T data;

    public static <T> BaseResponse<T> success(T data) {
    
    
        BaseResponse<T> resultData = new BaseResponse<>();
        resultData.setCode(200);
        resultData.setMessage("OK");
        resultData.setData(data);
        return resultData;
    }

    public static BaseResponse error(ErrorCode errorCode) {
    
    
        BaseResponse resultData = new BaseResponse();
        resultData.setCode(errorCode.getCode());
        resultData.setMessage(errorCode.getMessage());
        return resultData;
    }
}

Annotations are used here @Data, which are lombokprovided, so you need to pom.xmlintroduce lombokdependencies in them.

Write error code class:ErrorCode

public enum ErrorCode {
    
    
    VERIFY_NO_PASS(300, "签名验证未通过");
    private final Integer code;
    private final String message;

    ErrorCode(Integer code, String message) {
    
    
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
    
    
        return code;
    }

    public String getMessage() {
    
    
        return message;
    }
}

7. Configuration file

In the custom interceptor, we obtain the @Valuesum from the project's configuration file through annotations .application.ymlapiKeyapiSecret

application.ymlThe files are as follows:

apiKey: dhkadj123fda
apiSecret: hgjdakf12314sdf

The corresponding video tutorial has been uploaded to Station B. If you don’t like to read text content, you can also watch the video.

Guess you like

Origin blog.csdn.net/qq_43907505/article/details/135327702