All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.buession.security.geetest.GeetestClient Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
 * See the NOTICE file distributed with this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 *
 * =========================================================================================================
 *
 * This software consists of voluntary contributions made by many individuals on behalf of the
 * Apache Software Foundation. For more information on the Apache Software Foundation, please see
 * .
 *
 * +-------------------------------------------------------------------------------------------------------+
 * | License: http://www.apache.org/licenses/LICENSE-2.0.txt 										       |
 * | Author: Yong.Teng  													       |
 * | Copyright @ 2013-2020 Buession.com Inc.														       |
 * +-------------------------------------------------------------------------------------------------------+
 */
package com.buession.security.geetest;

import com.buession.core.utils.StatusUtils;
import com.buession.core.validator.Validate;
import com.buession.httpclient.HttpClient;
import com.buession.httpclient.core.Response;
import com.buession.lang.Status;
import com.buession.security.geetest.core.ClientType;
import com.buession.security.geetest.core.EnhencedResult;
import com.buession.security.mcrypt.MD5Mcrypt;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.buession.security.geetest.core.ProcessResult;
import com.buession.security.geetest.core.RequestData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Yong.Teng
 */
public class GeetestClient {

	public final static String GT_SERVER_STATUS_SESSION_KEY = "gt_server_status";

	/**
	 * 极验验证二次验证表单数据 chllenge
	 */
	public final static String GEETEST_CHALLENGE = "geetest_challenge";

	/**
	 * 极验验证二次验证表单数据 validate
	 */
	public final static String GEETEST_VALIDATE = "geetest_validate";

	/**
	 * 极验验证二次验证表单数据 seccode
	 */
	public final static String GEETEST_SECCODE = "geetest_seccode";

	public final static String PARAMETER_SESSION_ID = "session_id";

	public final static String PARAMETER_CHALLENGE = "verifyChallenge";

	public final static String PARAMETER_VALIDATE = "verifyValidate";

	public final static String PARAMETER_SECCODE = "verifySeccode";

	public final static int CHALLENGE_LENGTH = 32;

	private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();

	/**
	 * 公钥
	 */
	private String geetestId;

	/**
	 * 私钥
	 */
	private String geetestKey;

	/**
	 * 是否开启新的 failback
	 */
	private boolean newFailback = false;

	private HttpClient httpClient;

	/**
	 * 返回字符串
	 */
	private ProcessResult response;

	private final static Logger logger = LoggerFactory.getLogger(GeetestClient.class);

	static{
		OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	}

	/**
	 * 构造函数
	 *
	 * @param geetestId
	 * 		公钥
	 * @param geetestKey
	 * 		私钥
	 * @param newFailback
	 * 		是否开启新的 failback
	 */
	public GeetestClient(String geetestId, String geetestKey, boolean newFailback){
		this.geetestId = geetestId;
		this.geetestKey = geetestKey;
		this.newFailback = newFailback;
	}

	/**
	 * 构造函数
	 *
	 * @param geetestId
	 * 		公钥
	 * @param geetestKey
	 * 		私钥
	 * @param newFailback
	 * 		是否开启新的 failback
	 * @param httpClient
	 * 		Http Client
	 */
	public GeetestClient(String geetestId, String geetestKey, boolean newFailback, HttpClient httpClient){
		this(geetestId, geetestKey, newFailback);
		this.httpClient = httpClient;
	}

	/**
	 * 获取 Http Client
	 *
	 * @return Http Client
	 */
	public HttpClient getHttpClient(){
		return httpClient;
	}

	/**
	 * 设置 Http Client
	 *
	 * @param httpClient
	 * 		Http Client
	 */
	public void setHttpClient(HttpClient httpClient){
		this.httpClient = httpClient;
	}

	/**
	 * 验证初始化预处理
	 *
	 * @param requestData
	 * 		请求数据
	 *
	 * @return 初始化预处理结果
	 */
	public Status preProcess(RequestData requestData){
		if(registerChallenge(requestData) == Status.FAILURE){
			response = getFailPreProcessResult();
			return Status.FAILURE;
		}

		return Status.SUCCESS;
	}

	/**
	 * 服务正常的情况下使用的验证方式,向gt-server进行二次验证,获取验证结果
	 *
	 * @param challenge
	 * 		challenge
	 * @param validate
	 * 		validate
	 * @param seccode
	 * 		seccode
	 * @param requestData
	 * 		请求数据
	 *
	 * @return 验证结果
	 */
	public Status enhencedValidateRequest(String challenge, String validate, String seccode, RequestData requestData){
		if(requestIsLegal(challenge, validate, seccode) == false){
			return Status.FAILURE;
		}

		logger.debug("request legitimate");

		Map formData = new HashMap<>(7);

		formData.put("challenge", challenge);
		formData.put("validate", validate);
		formData.put("seccode", seccode);
		formData.put("json_format", "1");

		if(requestData.getUserId() != null){
			formData.put("user_id", requestData.getUserId());
		}

		if(requestData.getClientType() != null){
			formData.put("client_type", requestData.getClientType().getValue());
		}

		if(requestData.getIpAddress() != null){
			formData.put("ip_address", requestData.getIpAddress());
		}

		logger.debug("param: {}", formData.toString());

		if(validate.length() <= 0){
			return Status.FAILURE;
		}

		if(checkResultByPrivate(geetestKey, challenge, validate) == false){
			return Status.FAILURE;
		}

		logger.debug("checkResultByPrivate");

		Response response;
		try{
			response = httpClient.post(Geetest.VALIDATE_URL, formData);

			logger.debug("Enhenced Validate response: {}", response);

			EnhencedResult returnMap = OBJECT_MAPPER.readValue(response.getBody(), EnhencedResult.class);

			final MD5Mcrypt md5Mcrypt = new MD5Mcrypt();
			return StatusUtils.valueOf(md5Mcrypt.encode(seccode).equals(returnMap.getSeccode()));
		}catch(Exception e){
			logger.error("Enhenced Validate failure: {}", e);
		}

		return Status.FAILURE;
	}

	/**
	 * failback 使用的验证方式
	 *
	 * @param challenge
	 * 		challenge
	 * @param validate
	 * 		validate
	 * @param seccode
	 * 		seccode
	 *
	 * @return 验证结果
	 */
	public Status failbackValidateRequest(String challenge, String validate, String seccode){
		logger.debug("in failback validate");
		return StatusUtils.valueOf(requestIsLegal(challenge, validate, seccode));
	}

	/**
	 * 获取本次验证初始化返回结果
	 *
	 * @return 初始化结果
	 */
	public ProcessResult getResponse(){
		return response;
	}

	public String getVersion(){
		return Geetest.VERSION;
	}

	/**
	 * 用 captchaID 进行注册,更新challenge
	 *
	 * @param requestData
	 * 		请求数据
	 *
	 * @return 注册结果
	 */
	protected Status registerChallenge(RequestData requestData){
		String url = URLBuilder.buildRegisterUrl(geetestId, requestData.getClientType(), requestData.getUserId(),
				requestData.getIpAddress());

		logger.debug("GET_URL: {}", url);

		try{
			Response response = httpClient.get(url);
			if(response == null){
				logger.warn("gtServer register challenge failure");
				return Status.FAILURE;
			}

			logger.debug("gtServer register challenge success: {} ", response.getBody());

			ProcessResult processResult = OBJECT_MAPPER.readValue(response.getBody(), ProcessResult.class);

			logger.debug("gtServer register challenge failed: {}", processResult.getChallenge());

			if(processResult.getChallenge().length() == CHALLENGE_LENGTH){
				final MD5Mcrypt md5Mcrypt = new MD5Mcrypt();

				this.response =
						getSuccessPreProcessResult(md5Mcrypt.encode(processResult.getChallenge() + geetestKey));

				return Status.SUCCESS;
			}else{
				logger.error("gtServer register challenge error");
			}
		}catch(Exception e){
			logger.error("gtServer register challenge error: {}", e);
		}

		return Status.FAILURE;
	}

	/**
	 * 返回预处理成功结果
	 *
	 * @param challenge
	 * 		challenge
	 *
	 * @return 预处理成功结果
	 */
	protected ProcessResult getSuccessPreProcessResult(String challenge){
		logger.debug("challenge: {}", challenge);
		ProcessResult processResult = new ProcessResult();

		processResult.setSuccess(true);
		processResult.setGt(geetestId);
		processResult.setChallenge(challenge);
		processResult.setNewCaptcha(newFailback);

		return processResult;
	}

	/**
	 * 返回预处理失败结果
	 *
	 * @return 预处理失败结果
	 */
	protected ProcessResult getFailPreProcessResult(){
		final MD5Mcrypt md5Mcrypt = new MD5Mcrypt();
		StringBuilder challenge = new StringBuilder(36);
		String s1 = md5Mcrypt.encode(random(100));
		String s2 = md5Mcrypt.encode(random(100));

		challenge.append(s1).append(s2, 0, 2);

		ProcessResult processResult = new ProcessResult();

		processResult.setSuccess(false);
		processResult.setGt(geetestId);
		processResult.setChallenge(challenge.toString());
		processResult.setNewCaptcha(newFailback);

		return processResult;
	}

	/**
	 * 检查客户端的请求是否合法,三个只要有一个为空,则判断不合法
	 *
	 * @param challenge
	 * 		challenge
	 * @param validate
	 * 		validate
	 * @param seccode
	 * 		seccode
	 *
	 * @return 检测结果
	 */
	protected final static boolean requestIsLegal(String challenge, String validate, String seccode){
		return Validate.hasText(challenge) && Validate.hasText(validate) && Validate.hasText(seccode);
	}

	protected final static boolean checkResultByPrivate(final String geetestKey, final String challenge,
			final String validate){
		final MD5Mcrypt md5Mcrypt = new MD5Mcrypt();
		final StringBuilder sb = new StringBuilder(geetestKey.length() + Geetest.NAME.length() + challenge.length());

		sb.append(geetestKey).append(Geetest.NAME).append(challenge);

		return validate.equals(md5Mcrypt.encode(sb.toString()));
	}

	protected final static long random(final int divisor){
		return Math.round(Math.random() * divisor);
	}

	protected final static class URLBuilder {

		public final static String buildRegisterUrl(final String geetestId, final ClientType clientType,
				final String userId, final String ip){
			final StringBuilder sb = new StringBuilder(Geetest.REGISTER_URL.length() + geetestId.length() + 24);

			sb.append(Geetest.REGISTER_URL).append('?');
			sb.append("gt=").append(geetestId);
			sb.append("&json_format=1");

			if(userId != null){
				sb.append("&user_id=").append(userId);
			}

			if(clientType != null){
				sb.append("&client_type=").append(clientType.getValue());
			}

			if(ip != null){
				sb.append("&ip_address=").append(ip);
			}

			return sb.toString();
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy