2021SC@SDUSC
APIJSON—Verifier_Interface检查类接口
我们都知道检查的一般作用就是说可以在测试执行完成之后做一些校验,以验证测试结果是不是正确,如果验证检查失败了,其可以将通过测试的方法转换为失败的测试。
当然具体实现是会有相对较大的不一样
我们首先来看一下APIJSON中的检查类接口:
public interface Verifier<T>
这是用了一个类属类也就是我们所说的模板类,这里的模板类作用就是描述适用于一组类型的通用样板,由于在这里处理对象的数据类型尚未确定,因而我们不可用类属类直接创建对象实例,类属类必须经过实例化后才能成为可创建对象实例的类类型。
在这里此接口的作用为: 校验器(权限、请求参数、返回结果等)
接口定义的方法:
//验证权限是否通过
boolean verify(SQLConfig config) throws Exception;
//验证权限是否通过
boolean verifyAccess(SQLConfig config) throws Exception;
//允许请求,角色不好判断,让访问者发过来角色名,OWNER,CONTACT,ADMIN等
void verifyRole(String table, RequestMethod method, RequestRole role) throws Exception;
//登录校验
void verifyLogin() throws Exception;
//管理员角色校验
void verifyAdmin() throws Exception;
//验证是否重复
void verifyRepeat(String table, String key, Object value) throws Exception;
//验证是否重复
void verifyRepeat(String table, String key, Object value, long exceptId) throws Exception;
//验证请求参数的数据和结构
JSONObject verifyRequest(RequestMethod method, String name, JSONObject target, JSONObject request,int maxUpdateCount, String globleDatabase, String globleSchema, SQLCreator creator) throws Exception;
//验证返回结果的数据和结构
JSONObject verifyResponse(RequestMethod method, String name, JSONObject target, JSONObject response,String database, String schema, SQLCreator creator, OnParseCallback callback) throws Exception;
@NotNull
Parser<T> createParser();
@NotNull
Visitor<T> getVisitor();
Verifier<T> setVisitor(@NotNull Visitor<T> visitor);
String getVisitorIdKey(SQLConfig config);
大致就是分为以下部分的核验
权限验证
登录校验
角色判断
管理员判断
重复验证
验证请求参数Request是否合法
验证返回参数Response是否合法。
首先看一下最基本也是最重要的的verify,这个就是权限的核验,我们在前面的分析中已经提到过,在SqlConfig里面是有对应的权限与角色属性,所以我们只需要在其他地方给SQLConfig对象赋值,等到用到的时候便去检验一下是否权限足够便可以进行后续操作,这也是编程的原则。
public boolean verify(SQLConfig config) throws Exception {
return verifyAccess(config);
}
也就是说调用了verifyAccess来实现。
public boolean verifyAccess(SQLConfig config) throws Exception {
String table = config == null ? null : config.getTable();
if (table == null) {
return true;
}
RequestRole role = config.getRole();
if (role == null) {
role = RequestRole.UNKNOWN;
}
if (role != RequestRole.UNKNOWN) {//未登录的角色
verifyLogin();
}
RequestMethod method = config.getMethod();
verifyRole(table, method, role);//验证允许的角色
//验证角色,假定真实强制匹配<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
String visitorIdkey = getVisitorIdKey(config);
Object requestId;
switch (role) {
case LOGIN://verifyRole通过就行
break;
case CONTACT:
case CIRCLE:
//不能在Visitor内null -> [] ! 否则会导致某些查询加上不需要的条件!
List<Object> list = visitor.getContactIdList() == null
? new ArrayList<Object>() : new ArrayList<Object>(visitor.getContactIdList());
if (role == RequestRole.CIRCLE) {
list.add(visitorId);
}
//key!{}:[] 或 其它没有明确id的条件 等 可以和key{}:list组合。类型错误就报错
requestId = (Number) config.getWhere(visitorIdkey, true);//JSON里数值不能保证是Long,可能是Integer
@SuppressWarnings("unchecked")
Collection<Object> requestIdArray = (Collection<Object>) config.getWhere(visitorIdkey + "{}", true);//不能是 &{}, |{} 不要传,直接{}
if (requestId != null) {
if (requestIdArray == null) {
requestIdArray = new JSONArray();
}
requestIdArray.add(requestId);
}
if (requestIdArray == null) {//可能是@得到 || requestIdArray.isEmpty()) {//请求未声明key:id或key{}:[...]条件,自动补全
config.putWhere(visitorIdkey+"{}", JSON.parseArray(list), true); //key{}:[]有效,SQLConfig里throw NotExistException
}
else {//请求已声明key:id或key{}:[]条件,直接验证
for (Object id : requestIdArray) {
if (id == null) {
continue;
}
if (id instanceof Number == false) {//不能准确地判断Long,可能是Integer
throw new UnsupportedDataTypeException(table + ".id类型错误,id类型必须是Long!");
}
if (list.contains(Long.valueOf("" + id)) == false) {//Integer等转为Long才能正确判断。强转崩溃
throw new IllegalAccessException(visitorIdkey + " = " + id + " 的 " + table
+ " 不允许 " + role.name() + " 用户的 " + method.name() + " 请求!");
}
}
}
break;
case OWNER:
case ADMIN:
verifyAdmin();
break;
default://unknown,verifyRole通过就行
break;
}
return true;
}
这里先进性角色role的核验,如果role不是未知,那么我们便需要进行登录的核验。
这里是组员的内容,但我们为了更好的理解代码便对其大致进行了录取,只显示了case=CIRCLE的时候的情况,其实也很容易的进行理解,就是去进行一个数据结构的维护,获取到当前的用户,并且对其id等属性进行判断,如果合法就加入到我们的用户列表里面。
这里大致列举其它每种角色相对应的处理,但隐藏了处理的细节,在case=Admin时,这里不好做,在特定接口内部判断。 可以是固定秘钥 Parser#noVerify,之后全局跳过验证。
然后是登录校验verifyLogin
public void verifyLogin() throws Exception {
//未登录没有权限操作
if (visitorId == null) {
throw new NotLoggedInException("未登录,请登录后再操作!");
}
if (visitorId instanceof Number) {
if (((Number) visitorId).longValue() <= 0) {
throw new NotLoggedInException("未登录,请登录后再操作!");
}
}
else if (visitorId instanceof String) {
if (StringUtil.isEmpty(visitorId, true)) {
throw new NotLoggedInException("未登录,请登录后再操作!");
}
}
else {
throw new UnsupportedDataTypeException("visitorId 只能是 Long 或 String 类型!");
}
}
这里是用了一个Java的反射概念,是根据visitorId的instance来判断登录的一些判断,根据不同的情况给出具体的应答。
与小组成员讨论
这几次的分析中,组员和我都一直遇到SQLConfig对象的getMethod方法,我们觉得应该一起讨论一下各个方法的用途,以便于加深我们对项目代码的理解,也是更好的进行学习,我们经过交流经验得到它们各自的作用于功能:
我们本项目的method方法大概有六个:
GET
GET请求会显示请求指定的资源。一般来说GET方法应该只用于数据的读取,而不应当用于会产生副作用的非幂等的操作中。
GET会方法请求指定的页面信息,并返回响应主体,GET被认为是不安全的方法,因为GET方法会被网络蜘蛛等任意的访问。
HEAD
HEAD方法与GET方法一样,都是向服务器发出指定资源的请求。但是,服务器在响应HEAD请求时不会回传资源的内容部分,即:响应主体。这样,我们可以不传输全部内容的情况下,就可以获取服务器的响应头信息。HEAD方法常被用于客户端查看服务器的性能。
POST
POST请求会 向指定资源提交数据,请求服务器进行处理,如:表单数据提交、文件上传等,请求数据会被包含在请求体中。POST方法是非幂等的方法,因为这个请求可能会创建新的资源或/和修改现有资源。
PUT
PUT请求会身向指定资源位置上传其最新内容,PUT方法是幂等的方法。通过该方法客户端可以将指定资源的最新数据传送给服务器取代指定的资源的内容。
DELETE
DELETE请求用于请求服务器删除所请求URI(统一资源标识符,Uniform Resource Identifier)所标识的资源。DELETE请求后指定资源会被删除,DELETE方法也是幂等的。