|
|
@@ -0,0 +1,215 @@
|
|
|
+package com.fs.app.controller;
|
|
|
+
|
|
|
+import com.fs.baidu.domain.BdApi;
|
|
|
+import com.fs.baidu.service.IBdApiService;
|
|
|
+import com.fs.baidu.utils.SignService;
|
|
|
+import com.fs.huifuPay.sdk.opps.core.exception.BasePayException;
|
|
|
+import com.fs.huifuPay.sdk.opps.core.utils.HttpClientUtils;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.json.JSONObject;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.web.bind.annotation.GetMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.TreeMap;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@RestController
|
|
|
+@RequestMapping("/baiduBack")
|
|
|
+public class MockAppController {
|
|
|
+ private static final String APPID = "appId";
|
|
|
+ private static final String AUTH_CODE = "authCode";
|
|
|
+ private static final String USERID = "userId";
|
|
|
+ private static final String TIMESTAMP = "timestamp";
|
|
|
+ private static final String SIGNATURE = "signature";
|
|
|
+ private static final String STATE = "state";
|
|
|
+ private static final String ACCESSTOKEN_URL = "https://u.baidu.com/oauth/accessToken";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IBdApiService bdApiService; // 开发者自定义
|
|
|
+ @Autowired
|
|
|
+ private SignService signService; // 开发者可自定义
|
|
|
+
|
|
|
+ @GetMapping("/callback")
|
|
|
+ public String mockApp(HttpServletRequest request) throws BasePayException {
|
|
|
+
|
|
|
+ // 获取请求参数
|
|
|
+ Map<String, String> params = new HashMap<>();
|
|
|
+ this.fillParams(params, request);
|
|
|
+ log.info("callback: params = {}", JSONObject.valueToString(params));
|
|
|
+ int userIdInt = 0;
|
|
|
+ try {
|
|
|
+ userIdInt = Integer.parseInt(params.get(USERID));
|
|
|
+ } catch (Exception e) {
|
|
|
+ return this.getResponseJson(600011, "参数错误", new Object());
|
|
|
+ }
|
|
|
+ // 对签名内容进行判空
|
|
|
+ if (StringUtils.isBlank(params.get(SIGNATURE))) {
|
|
|
+ return this.getResponseJson(600011, "参数错误", new Object());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取应用对应的密钥
|
|
|
+ BdApi app = bdApiService.getAppByAppId(params.get(APPID));
|
|
|
+ String sk = app.getAppSecretKey();
|
|
|
+ // 开发者进行验签
|
|
|
+ // 检查状态码
|
|
|
+// if (!this.checkState(params.get(APPID), app.getAppUserId(), params.get(STATE))) {
|
|
|
+// log.info("callback: state check fail");
|
|
|
+// return this.getResponseJson(600011, "状态码错误", new Object());
|
|
|
+// }
|
|
|
+
|
|
|
+ // 签名验证
|
|
|
+ if (!this.checkSignature(params, sk)) {
|
|
|
+ log.info("callback: signature check fail");
|
|
|
+ return this.getResponseJson(600011, "签名错误", new Object());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用接口换取授权令牌
|
|
|
+ String response = this.getAccessToken(params, sk, userIdInt);
|
|
|
+ log.info("callback:getAccesstoken, response={}", response);
|
|
|
+ String accessToken = null;
|
|
|
+ String refreshToken = null;
|
|
|
+ int uid;
|
|
|
+
|
|
|
+ if (response.length() > 0) {
|
|
|
+ JSONObject res = new JSONObject(response);
|
|
|
+ int code = (int) res.get("code");
|
|
|
+ if (code == 0) {
|
|
|
+ JSONObject data = res.getJSONObject("data");
|
|
|
+ accessToken = (String) data.get("accessToken");
|
|
|
+ // 注释部分为 accessToken 其他信息,各应用开发者酌情使用即可
|
|
|
+ refreshToken = (String) data.get("refreshToken");
|
|
|
+ String openId = data.getString("openId");
|
|
|
+ int expiresIn = data.getInt("expiresIn");
|
|
|
+ int refreshExpiresIn = data.getInt("refreshExpiresIn");
|
|
|
+ app.setAccessToken(accessToken);
|
|
|
+ app.setRefreshToken(refreshToken);
|
|
|
+ app.setExpiresIn(expiresIn);
|
|
|
+ app.setOpenId(openId);
|
|
|
+ app.setRefreshExpiresIn(refreshExpiresIn);
|
|
|
+ app.setAuthCode(params.get(AUTH_CODE));
|
|
|
+ bdApiService.updateById(app);
|
|
|
+
|
|
|
+ // TODO 相关信息落库处理
|
|
|
+ } else {
|
|
|
+ return this.getResponseJson(600011, "未获取到 access_token", new Object());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Map<String, String> data = new HashMap<>();
|
|
|
+ data.put("accessToken", accessToken);
|
|
|
+ return this.getResponseJson(0, "success", data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 填充请求参数,开发者可用实体代替map
|
|
|
+ *
|
|
|
+ * @param params
|
|
|
+ * @param request
|
|
|
+ */
|
|
|
+ private void fillParams(Map<String, String> params, HttpServletRequest request) {
|
|
|
+ params.put(APPID, request.getParameter(APPID));
|
|
|
+ params.put(AUTH_CODE, request.getParameter(AUTH_CODE));
|
|
|
+ params.put(USERID, request.getParameter(USERID));
|
|
|
+ params.put(TIMESTAMP, request.getParameter(TIMESTAMP));
|
|
|
+ params.put(SIGNATURE, request.getParameter(SIGNATURE));
|
|
|
+ params.put(STATE, request.getParameter(STATE));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验状态码是否符合预期
|
|
|
+ *
|
|
|
+ * @param appId 应用ID
|
|
|
+ * @param userId 创建应用的ID
|
|
|
+ * @param state 请求参数中的状态码
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private boolean checkState(String appId, Long userId, String state) {
|
|
|
+ // md5util 开发者自行开发即可
|
|
|
+ String newState = md5(appId.concat("_").concat(String.valueOf(userId)));
|
|
|
+ return newState.equals(state);
|
|
|
+ }
|
|
|
+ public static String md5(String input) {
|
|
|
+ try {
|
|
|
+ MessageDigest md = MessageDigest.getInstance("MD5");
|
|
|
+ byte[] messageDigest = md.digest(input.getBytes());
|
|
|
+ StringBuilder hexString = new StringBuilder();
|
|
|
+ for (byte b : messageDigest) {
|
|
|
+ String hex = Integer.toHexString(0xff & b);
|
|
|
+ if (hex.length() == 1) {
|
|
|
+ hexString.append('0');
|
|
|
+ }
|
|
|
+ hexString.append(hex);
|
|
|
+ }
|
|
|
+ return hexString.toString();
|
|
|
+ } catch (NoSuchAlgorithmException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 签名验证方法
|
|
|
+ *
|
|
|
+ * @param params
|
|
|
+ * @param sk
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private boolean checkSignature(Map<String, String> params, String sk) {
|
|
|
+ // 获取验签字符串:按key自然排序,拼接成 json 字符串,
|
|
|
+ // 示例:str1 = {"appId": xxxxx, "authCode": xxx, "state": xxx,"timestamp": xxx}
|
|
|
+ TreeMap<String, Object> map = new TreeMap<>();
|
|
|
+ map.put(APPID, params.get(APPID));
|
|
|
+ map.put(AUTH_CODE, params.get(AUTH_CODE));
|
|
|
+ map.put(USERID, params.get(USERID));
|
|
|
+ map.put(STATE, params.get(STATE));
|
|
|
+ map.put(TIMESTAMP, params.get(TIMESTAMP));
|
|
|
+ // 根据上述签名算法对接收到的参数签名
|
|
|
+ String sign = signService.paramsSign(sk, map);
|
|
|
+ log.info("callback: signature = {}", sign);
|
|
|
+ // 判断签名和URL传参签名是否一致
|
|
|
+ return params.get(SIGNATURE).equals(sign);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 换取授权令牌
|
|
|
+ *
|
|
|
+ * @param params
|
|
|
+ * @param sk
|
|
|
+ * @param userIdInt 授权账户ID
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private String getAccessToken(Map<String, String> params, String sk, int userIdInt) throws BasePayException {
|
|
|
+ Map<String, Object> requestMap = new HashMap<>();
|
|
|
+ requestMap.put(APPID, params.get(APPID));
|
|
|
+ requestMap.put("secretKey", sk);
|
|
|
+ requestMap.put(AUTH_CODE, params.get(AUTH_CODE));
|
|
|
+ requestMap.put("grantType", "access_token");
|
|
|
+ requestMap.put("userId", userIdInt);
|
|
|
+
|
|
|
+ String paramsJson = JSONObject.valueToString(requestMap);
|
|
|
+ // 开发者自行增加异常判断
|
|
|
+ return HttpClientUtils.httpPostJson(ACCESSTOKEN_URL, new HashMap<>(), paramsJson);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 封装返回json串
|
|
|
+ *
|
|
|
+ * @param code
|
|
|
+ * @param message
|
|
|
+ * @param data
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private String getResponseJson(int code, String message, Object data) {
|
|
|
+ // 封装返回字段的map
|
|
|
+ Map<String, Object> responseMap = new HashMap<>();
|
|
|
+ responseMap.put("code", code);
|
|
|
+ responseMap.put("message", message);
|
|
|
+ responseMap.put("data", data);
|
|
|
+ return JSONObject.valueToString(responseMap);
|
|
|
+ }
|
|
|
+}
|