代码分析(二)

2021SC@SDUSC

AbstractParser类(解析器)

将request解析为SQL集

对应的相关核心代码:(在这里需要注意的是这个角色判断必须在parseCorrectRequest后面,因为parseCorrectRequest可能会添加 @role)

	/**解析请求json并获取对应结果
	 * @param request
	 * @return requestObject
	 */
public JSONObject parseResponse(JSONObject request) {
		long startTime = System.currentTimeMillis();
		Log.d(TAG, "parseResponse  startTime = " + startTime
				+ "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n\n ");

		requestObject = request;

		verifier = createVerifier().setVisitor(getVisitor());

		if (RequestMethod.isPublicMethod(requestMethod) == false) {
			try {
				if (isNeedVerifyLogin()) {
					onVerifyLogin();
				}
				if (isNeedVerifyContent()) {
					onVerifyContent();
				}
			} catch (Exception e) {
				return extendErrorResult(requestObject, e);
			}
		}

		if (isNeedVerifyRole() && globleRole == null) {
			try {
				setGlobleRole(RequestRole.get(requestObject.getString(JSONRequest.KEY_ROLE)));
				requestObject.remove(JSONRequest.KEY_ROLE);
			} catch (Exception e) {
				return extendErrorResult(requestObject, e);
			}
		}

		try {
			setGlobleFormat(requestObject.getBoolean(JSONRequest.KEY_FORMAT));
			setGlobleDatabase(requestObject.getString(JSONRequest.KEY_DATABASE));
			setGlobleSchema(requestObject.getString(JSONRequest.KEY_SCHEMA));
			setGlobleDatasource(requestObject.getString(JSONRequest.KEY_DATASOURCE));
			setGlobleExplain(requestObject.getBoolean(JSONRequest.KEY_EXPLAIN));
			setGlobleCache(requestObject.getString(JSONRequest.KEY_CACHE));

			requestObject.remove(JSONRequest.KEY_FORMAT);
			requestObject.remove(JSONRequest.KEY_DATABASE);
			requestObject.remove(JSONRequest.KEY_SCHEMA);
			requestObject.remove(JSONRequest.KEY_DATASOURCE);
			requestObject.remove(JSONRequest.KEY_EXPLAIN);
			requestObject.remove(JSONRequest.KEY_CACHE);
		} catch (Exception e) {
			return extendErrorResult(requestObject, e);
		}

		final String requestString = JSON.toJSONString(request);//request传进去解析后已经变了


		queryResultMap = new HashMap<String, Object>();

		Exception error = null;
		sqlExecutor = createSQLExecutor();
		onBegin();
		try {
			queryDepth = 0;
			requestObject = onObjectParse(request, null, null, null, false);

			onCommit();
		}
		catch (Exception e) {
			e.printStackTrace();
			error = e;

			onRollback();
		}

		requestObject = error == null ? extendSuccessResult(requestObject) : extendErrorResult(requestObject, error);

		JSONObject res = (globleFormat != null && globleFormat) && JSONResponse.isSuccess(requestObject) ? new JSONResponse(requestObject) : requestObject;

		long endTime = System.currentTimeMillis();
		long duration = endTime - startTime;

		if (Log.DEBUG) {
			requestObject.put("sql:generate|cache|execute|maxExecute", getSQLExecutor().getGeneratedSQLCount() + "|" + getSQLExecutor().getCachedSQLCount() + "|" + getSQLExecutor().getExecutedSQLCount() + "|" + getMaxSQLCount());
			requestObject.put("depth:count|max", queryDepth + "|" + getMaxQueryDepth());
			requestObject.put("time:start|duration|end", startTime + "|" + duration + "|" + endTime);
			if (error != null) {
				requestObject.put("throw", error.getClass().getName());
				requestObject.put("trace", error.getStackTrace());
			}
		}

		onClose();
		if (IS_PRINT_REQUEST_STRING_LOG||Log.DEBUG||error != null) {
			Log.sl("\n\n\n",'<',"");
			Log.fd(TAG , requestMethod + "/parseResponse  request = \n" + requestString + "\n\n");
		}
		if (IS_PRINT_BIG_LOG||Log.DEBUG||error != null) {  // 日志仅存服务器,所以不太敏感,而且这些日志虽然量大但非常重要,对排查 bug 很关键
			Log.fd(TAG,requestMethod + "/parseResponse return response = \n" + JSON.toJSONString(requestObject) + "\n\n");
		}
		if (IS_PRINT_REQUEST_ENDTIME_LOG||Log.DEBUG||error != null) {
			Log.fd(TAG , requestMethod + "/parseResponse  endTime = " + endTime + ";  duration = " + duration);
			Log.sl("",'>',"\n\n\n");
		}
		return res;
	}

首先就是在进行解析前对用户的身份进行一个验证,并且获取得到当前的用户身份。
在此中将JSON对象类型的request类型再次去调用了JSON的toJSONString方法,而这个最终调用了fastjson包的下列方法,将request再次解析成为更适合进行SQL处理的String类型的JSON数据,而且这次解析后request就已经变化了,下面是在拓展库fastjson中的最终调用方法,在这里主要是进行了一个格式的过滤。:

    /**
     * @since 1.2.9
     * @return
     */
    public static String toJSONString(Object object, // 
                                      SerializeConfig config, // 
                                      SerializeFilter[] filters, // 
                                      String dateFormat, //
                                      int defaultFeatures, // 
                                      SerializerFeature... features) {
        SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);

        try {
            JSONSerializer serializer = new JSONSerializer(out, config);
            
            if (dateFormat != null && dateFormat.length() != 0) {
                serializer.setDateFormat(dateFormat);
                serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
            }

            if (filters != null) {
                for (SerializeFilter filter : filters) {
                    serializer.addFilter(filter);
                }
            }

            serializer.write(object);

            return out.toString();
        } finally {
            out.close();
        }
    }

我么你可以看到,在这里面其实有一个序列化的对象,关于序列化与反序列化的概念,我们后续的分析中会再进行,所以这里先不赘述,我们在这里主要分析它的作用,也就是我们制作了一个配置,然后我们根据我们制定的配置进行序列化和反序列化,以便于达到信息的传输的功能。这个怎么理解呢?比如说我现在要送一个有着复杂结构的椅子从A城市到B城市,我要把它们拆开打包再送出去,然后我们怎么进行的拆解,回来组装的时候便怎么进行安装,这个组装规则就相当于是我们的配置规则。

身份验证及获取

在解析类中,还有比较重要的点就是身份角色的验证以及获取,以便于得到相应的角色作用

/**校验角色及对应操作的权限
	 * @param config
	 * @return
	 * @throws Exception
	 */
	@Override
	public void onVerifyRole(@NotNull SQLConfig config) throws Exception {
		if (Log.DEBUG) {
			Log.i(TAG, "onVerifyRole  config = " + JSON.toJSONString(config));
		}

		if (isNeedVerifyRole()) {
			if (config.getRole() == null) {
				if (globleRole != null) {
					config.setRole(globleRole);
				} else {
					config.setRole(getVisitor().getId() == null ? RequestRole.UNKNOWN : RequestRole.LOGIN);
				}
			}
			getVerifier().verifyAccess(config);
		}

	}

首先在这个config里面是有role角色的相关属性,同时我们的很多需要验证的方法都会去进行一个验证,当不存在时,那便进行设置,同时更新config的相关信息,否则就是直接获取。这个就是进行设置的具体,首先就是判断一下,当前是否需要用户权限,如果是游客是不需要的。如果需要那便查看config对象的role是否为null,如果为null则我们需要进行设置,那么就把当前的visitor设置为role,如果visitor也为null,按照理论来说不会这样,但如果发生了就把role设置为未知角色

正确的JSON结构

下面是当获得前端request时的格式正误判断以及一些请求的要求格式的检查,必须获取到指定的JSON结构:

/**解析请求JSONObject
	 * @param request => URLDecoder.decode(request, UTF_8);
	 * @return
	 * @throws Exception 
	 */
	@NotNull
	public static JSONObject parseRequest(String request) throws Exception {
		JSONObject obj = JSON.parseObject(request);
		if (obj == null) {
			throw new UnsupportedEncodingException("JSON格式不合法!");
		}
		return obj;
	}

	@Override
	public JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, String name, @NotNull JSONObject request
			, int maxUpdateCount, SQLCreator creator) throws Exception {

		if (RequestMethod.isPublicMethod(method)) {
			return request;
		}

		if (StringUtil.isEmpty(tag, true)) {
			throw new IllegalArgumentException("请在最外层传 tag !一般是 Table 名,例如 \"tag\": \"User\" ");
		}

		//代码在此处获取指定的JSON结构 <<<<<<<<<<<<
		JSONObject object = null;
		String error = "";
		try {
			object = getStructure("Request", method.name(), tag, version);
		} catch (Exception e) {
			error = e.getMessage();
		}
		if (object == null) { //empty表示随意操作  || object.isEmpty()) {
			throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.name() + ", tag: " + tag + " 对应的 structure !"
					+ "非开放请求必须是后端 Request 表中校验规则允许的操作!\n " + error + "\n如果需要则在 Request 表中新增配置!");
		}

		JSONObject target = object;
		if (object.containsKey(tag) == false) { //tag 是 Table 名或 Table[]

			boolean isArrayKey = tag.endsWith(":[]");  //  JSONRequest.isArrayKey(tag);
			String key = isArrayKey ? tag.substring(0, tag.length() - 3) : tag;

			if (apijson.JSONObject.isTableKey(key)) {
				if (isArrayKey) { //自动为 tag = Comment:[] 的 { ... } 新增键值对 "Comment[]":[] 为 { "Comment[]":[], ... }
					target.put(key + "[]", new JSONArray()); 
				}
				else { //自动为 tag = Comment 的 { ... } 包一层为 { "Comment": { ... } }
					target = new JSONObject(true);
					target.put(tag, object);
				}
			}
		}
		//JSONObject clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {}
		return getVerifier().verifyRequest(method, name, target, request, maxUpdateCount, getGlobleDatabase(), getGlobleSchema(), creator);
	}

在这里面的这一段代码

if (RequestMethod.isPublicMethod(method)) {
		return request;
	}

我们都知道POST方法的安全性一般是要高于GET方法,我们这一段就是需要指定JSON结构的GET请求可以改为POST请求。但这个一般只有对安全性要求高的才会指定,因为在这种情况下用明文的GET方式几乎肯定不安全。

这个核实的过程,首先就是通过getStructure获取到相应属性值,然后对这个JSONObject对象进行初始判断,检查是否存在相关的表名以及是否请求的是数组对象,然后通过verifier对象去进行核实且获取到返回的request。

接下来对刚才的操作进行了性能优化:

 /**
     * 获取正确的返回结果
     *
     * @param method
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    public JSONObject parseCorrectResponse(String table, JSONObject response) throws Exception {
        		Log.d(TAG, "getCorrectResponse  method = " + method + "; table = " + table);
        		if (response == null || response.isEmpty()) {//避免无效空result:{}添加内容后变有效
        			Log.e(TAG, "getCorrectResponse  response == null || response.isEmpty() >> return response;");
        return response;
        		}

        		JSONObject target = apijson.JSONObject.isTableKey(table) == false
        				? new JSONObject() : getStructure(method, "Response", "model", table);

        				return MethodStructure.parseResponse(method, table, target, response, new OnParseCallback() {

        					@Override
        					protected JSONObject onParseJSONObject(String key, JSONObject tobj, JSONObject robj) throws Exception {
        						return getCorrectResponse(method, key, robj);
        					}
        				});
    }

这个其实相当于一个动态规划,也就是我们所说的查表法,当检测到reaponse为null或者为空时,那么便直接return,避免了接下来的一些操作,同时也进行了一个避免,就是我们的原本无效的空result可能在进行了后续的操作便会变得有效,但这是不期望发生的,所以在这里进行一个判断就避免掉了这个情况。


与小组其他成员讨论这次主要是分析parseCorrectRequest方法的时候,在最后它是进行了一个返回:

return getVerifier().verifyRequest(method, name, target, request, maxUpdateCount, getGlobleDatabase(), getGlobleSchema(), creator);

而这个是属于队友的分析范围,秉持着可以多不可以缺的原则,我先是自己查看了相关的代码,由于代码量相当庞大,短时间觉得不甚理解,便与负责这一部分代码的队友进行了交流,然后便大致理解了这一行代码的作用,也对上下文更加的了解,这一行便是去先进行角色的核验,然后核验该角色的对应权限,然后根据request与response的不同情况,来解析得到不同的返回结果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值