什么是程序式提交❓
它是相对于普通的提交的,普通的提交是怎么样的?我们需要登录验证我们自己,然后1.要么是服务端在他们的服务器上面记录登录信息,2.要么是服务端返回一个登录凭证(本项目的做法),后续所有的操作,客户端都需要带上这个登录凭证。这两种方法都存在一个不足,即登录信息存在时效性。
当然也不能算是缺点,有的时候我们其实要的就是时效性,是缺点还是优点看场景需要。
如果你的程序向外通过了开发API
功能,那么需要用户登录这个流程的做法就是不好的,更好的做法是提供一个API Key
,用户访问API
时带上这个“永久性凭证”。
不过本项目中使用的是公钥系统,用户提交代码时不仅要提供Access key
,还需要使用Secrect key
对自己的数据签名,防止中间人攻击。
API 说明
请求地址 | 请求方式 | Header |
---|---|---|
https://code.yalexin.top/api/v1/programExecuteCode | POST | Content-Type:application/json |
请求体和响应:
request
{
"payload": "string",
"publicKey": "string",
"signature": "string"
}
response
{
"code": number,
"msg": "string",
"data": {
"executeMessages": [
{
"exitCode": number,
"message": "string",
"errorMessage": "string",
"timeCost": number,
"memoryCost": number
}
]
}
}
提交过程
1.准备你的Access key
和Secret key
登录平台后访问/user/info
,点击API Key
选项卡,找到你的你的Access key
和Secret key
,如果还没有,则点击“新增”按钮。

2. 准备你的Payload
go
type ExecuteCodeRequest struct {
Code string `json:"code"`
Language string `json:"language"`
// 运行用例组,每一个元素代表一个用例,例如 1 2\n
InputList []string `json:"inputList"`
}
func main() {
code := `
#include<stdio.h>
int main(){
int num;
scanf("%d", &num);
printf("%d", num * (num + 1));
return 0;
}
`
inputList := []string{"4\n", "6\n"}
language := "C"
codeRequest := ExecuteCodeRequest{
Code: code,
Language: language,
InputList: inputList,
}
}
java
python
3. 转为json字符串
go
origidata, err := json.Marshal(codeRequest)
if err != nil {
fmt.Printf("json.Marshal %v", err)
return
}
4. 使用你的Access key
加密
先编写加密的函数:
go
type CryptoService struct {
}
const AES_LEN int = 32
var KEY_DATA_SEPARATOR [3]byte = [3]byte{0x00, 0x00, 0x00}
// 使用公钥Base64字符串加密数据(采用混合加密的方式,即结合AES对称加密,否则直接采用 RSA 会收到加密数据长度的限制,虽然也可以使用分组加密的方式)
func (service *CryptoService) EncryptWithPublicKeyBase64(publicKeyBase64, originData string) (string, error) {
// 先解码为字节数组
publicKeyPEM, err := base64.StdEncoding.DecodeString(publicKeyBase64)
if err != nil {
return "", fmt.Errorf("error decoding public key: %v", err)
}
// 从字节数组还原
block, _ := pem.Decode(publicKeyPEM)
if block == nil || block.Type != "PUBLIC KEY" {
return "", fmt.Errorf("invalid public key PEM")
}
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return "", fmt.Errorf("error parsing public key: %v", err)
}
// 断言是 rsa 公钥类型
rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
if !ok {
return "", fmt.Errorf("not an RSA public key")
}
// 生成一个随机的对称密钥
symmetricKey := make([]byte, AES_LEN)
if _, err = rand.Read(symmetricKey); err != nil {
return "", err
}
// 使用对称密钥和AES-GCM模式加密数据
aesBlock, err := aes.NewCipher(symmetricKey)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(aesBlock)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return "", err
}
encryptedData := gcm.Seal(nonce, nonce, []byte(originData), nil)
// 使用RSA公钥加密对称密钥
encryptedSymmetricKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsaPublicKey, symmetricKey, nil)
if err != nil {
return "", err
}
// 将加密后的对称密钥和加密后的数据组合(分隔符之前是加密密钥,分隔符后是加密数据)
separator := KEY_DATA_SEPARATOR[:]
dataBytes := append(append(encryptedSymmetricKey, separator...), encryptedData...)
// 字节转 base64
encryptedBase64 := base64.StdEncoding.EncodeToString(dataBytes)
return encryptedBase64, nil
}
再调用我们的加密函数:
accessKey := ""
cryptoServiceInstance := new(CryptoService)
payload, err := cryptoServiceInstance.EncryptWithPublicKeyBase64(accessKey, string(origidata))
if err != nil {
fmt.Printf("EncryptWithPublicKeyBase64 fail: %v", err)
return
}
5. 使用你的Secret key
对加密数据计算md5
值
func mD5Str(inputStr string) string {
// 创建一个新的MD5哈希实例
hasher := md5.New()
// 将字符串转换为字节切片并写入哈希对象
hasher.Write([]byte(inputStr))
// 计算哈希值
hashBytes := hasher.Sum(nil)
// 将字节切片转换为16进制字符串表示形式
return hex.EncodeToString(hashBytes)
}
secretKey := ""
signature := mD5Str(payload + secretKey)
6. 发送加密后的数据以及md5
request := ProgramExecuteCodeRequest{
Payload: payload,
Signature: signature,
PublicKey: accessKey,
}
marshal, err := json.Marshal(request)
if err != nil {
fmt.Printf("Marshal fail:%v", err)
}
const API_URL = "https://code.yalexin.top/api/v1/programExecuteCode"
if err != nil {
fmt.Println("Error creating request:", err)
return
}
// 创建一个请求
req, err := http.NewRequest("POST", API_URL, bytes.NewBuffer(marshal))
// 设置请求头
req.Header.Set("Content-Type", "application/json")
// 使用 http.Client 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
// 打印响应
fmt.Println("Response from server:")
fmt.Println(string(body))