耗时一个月开发的OJ在线判题系统,文末有项目地址,目前还在更新代码~
给代码沙箱提供开放API
直接在 controller 暴露 CodeSandbox 定义的接口:
/**
* 执行代码
*
* @param executeCodeRequest
* @return
*/
@PostMapping("/executeCode")
ExecuteCodeResponse executeCode(@RequestBody ExecuteCodeRequest executeCodeRequest) {
if (executeCodeRequest == null) {
throw new RuntimeException("请求参数为空");
}
return javaNativeCodeSandbox.executeCode(executeCodeRequest);
}
OJ后台项目调用远程代码沙箱
/**
* 远程代码沙箱:自己开发的沙箱
*/
public class RemoteCodeSandbox implements CodeSandbox {
@Override
public ExecutecodeResponse executeCode(ExecutecodeCodeRequest excodeCodeRequest) {
String url = "http://localhost:8090/executeCode";
String jsonStr = JSONUtil.toJsonStr(excodeCodeRequest);
String responseStr = HttpUtil.createPost(url)
.body(jsonStr)
.execute()
.body();
if(StrUtil.isBlank(responseStr)){
throw new BusinessException(ErrorCode.API_REQUEST_ERROR,"调用远程代码沙箱接口失败:" + responseStr);
}
return JSONUtil.toBean(responseStr, ExecutecodeResponse.class);
}
}
调用安全性
如果将服务不做任何的权限校验,直接发到公网,是不安全的。
1)调用方与服务提供方之间约定一个字符串(最好加密)
优点:实现最简单,比较适合内部系统之间相互调用(相对可信的环境内部调用)
缺点:不够灵活,如果 key 泄漏或变更,需要重启代码
代码沙箱服务,先定义约定的字符串:
// 定义鉴权请求头和密钥
private static final String AUTH_REQUEST_HEADER = "auth";
private static final String AUTH_REQUEST_SECRET = "secretKey";
改造请求,从请求头中获取认证信息,并校验:
@PostMapping("/executeCode")
ExecuteCodeResponse executeCode(@RequestBody ExecuteCodeRequest executeCodeRequest, HttpServletRequest request,
HttpServletResponse response) {
// 基本的认证
String authHeader = request.getHeader(AUTH_REQUEST_HEADER);
if (!AUTH_REQUEST_SECRET.equals(authHeader)) {
response.setStatus(403);
return null;
}
if (executeCodeRequest == null) {
throw new RuntimeException("请求参数为空");
}
return javaNativeCodeSandbox.executeCode(executeCodeRequest);
}
调用方,在调用时补充请求头:
@Override
public ExecuteCodeResponse executeCode1680129124930162690_0.5118488017228444(ExecuteCodeRequest executeCodeRequest) {
System.out.println("远程代码沙箱");
String1680129124930162690_0.40790531622583837 url = "http://localhost:8090/executeCode"1680129124930162690_0.9483484149140926;
String json =1680129124930162690_0.19810350616928618 JSONUtil.toJsonStr(executeCodeRequest);
String responseStr =1680129124930162690_0.2206213437218354 HttpUtil.createPost(url)
.header(AUTH_REQUEST_HEADER, AUTH_REQUEST_SECRET)
.body(json)
.execute()
.body();
if (StringUtils.isBlank(responseStr)) {
throw new1680129124930162690_0.2870843725724177 BusinessException(ErrorCode.API_REQUEST_ERROR, "executeCode remoteSandbox error, message = " + responseStr);
}
return1680129124930162690_0.3405605732300334 JSONUtil.toBean(responseStr, ExecuteCodeResponse.class);
}
2)API签名认证
给允许调用的人员分配 accessKey,secretKey,然后校验这两组 key 是否匹配
在 API 开放平台中做了这些事情
项目地址
(求求大佬们赏个star~)
前端:https://github.com/IMZHEYA/yoj-frontend
后端:https://github.com/IMZHEYA/yoj-backend
代码沙箱:https://github.com/IMZHEYA/yoj-code-sandbox