Commit 60915af5 by 袁伟铭

修改api服务

parent 23fdb0fc
......@@ -32,6 +32,8 @@ public enum ApiCodeEnum {
LOGIN_VALID_ERROR("401", "登录验证失败"),
APPID_VALID_ERROR("501", "appId或appSecret错误"),
SERVER_ERROR("500", "服务器繁忙"),
;
......
......@@ -34,7 +34,7 @@ public class ApiController {
* 2016年10月3日 下午1:38:27
*/
@RequestMapping("/action")
public ApiResp action(ApiForm form, @RequestHeader(required = false) String appId, @RequestHeader(required = false) String appSecret, HttpServletRequest request) {
public ApiResp action(ApiForm form, @RequestHeader(required = false) String appId, @RequestHeader(required = false) String appSecret, @RequestHeader(required = false) String Authorization, HttpServletRequest request) {
long start = System.currentTimeMillis();
// 不处理Request Method:OPTIONS的请求
......@@ -57,7 +57,7 @@ public class ApiController {
ApiResp resp;
try {
// 身份验证
resp = apiService.auth(form, appId, appSecret);
resp = apiService.auth(form, appId, appSecret, Authorization);
if (resp.isSuccess()) {
// 调用接口方法
resp = apiService.action(form);
......
package com.zq.api.controller;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.zq.api.constant.ApiCodeEnum;
import com.zq.api.form.ApiForm;
import com.zq.api.form.ApiResp;
import com.zq.api.service.AppService;
import com.zq.api.utils.ApiUtils;
import com.zq.common.utils.ThrowableUtil;
import com.zq.common.vo.ResultVo;
import feign.FeignException;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Api(tags = "API接口")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/app")
public class AppController {
private final AppService appService;
/**
* 获取信息入口
* <p>
* 2016年10月3日 下午1:38:27
*/
@RequestMapping("/action")
public ApiResp action(ApiForm form, @RequestHeader(required = false) String appId, @RequestHeader(required = false) String appSecret, HttpServletRequest request) {
long start = System.currentTimeMillis();
// 不处理Request Method:OPTIONS的请求
if (request.getMethod().equals("OPTIONS")) {
return ApiUtils.getSuccessResp(form);
}
//解析业务参数
if (!form.parseBizContent()) {
return ApiUtils.getParamError(form);
}
String method = form.getMethod();
if (StrUtil.isBlank(method)) {
method = request.getParameter("method");
form.setMethod(method);
}
String stackTrace = "";
ApiResp resp;
try {
// 身份验证
resp = appService.auth(form, appId, appSecret);
if (resp.isSuccess()) {
// 调用接口方法
resp = appService.action(form);
}
} catch (Exception e) {
stackTrace = ThrowableUtil.getStackTrace(e);
// 判断指定异常是否来自或者包含指定异常
if (ExceptionUtil.isFromOrSuppressedThrowable(e, FeignException.Unauthorized.class)) {
resp = ApiUtils.toApiResp(form, ResultVo.fail(401, "Unauthorized"));
} else if (stackTrace.contains("Load balancer does not have available server for client")) {
resp = ApiUtils.getServiceNotAvailableError(form);
} else if (stackTrace.contains("Connection refused: connect executing")) {
resp = ApiUtils.getServiceNotAvailableError(form);
} else {
resp = ApiUtils.getMethodHandlerError(form);
}
}
// 没有数据输出空
resp = resp == null ? new ApiResp(form) : resp;
String logType = resp.isSuccess() ? "INFO" : "ERROR";
// 如果是500错误, 服务会返回错误的堆栈信息
if (resp.getCode().equals(ApiCodeEnum.SERVER_ERROR.code())) {
stackTrace = resp.getMsg();
resp.setMsg(ApiCodeEnum.SERVER_ERROR.msg());
}
// 调试日志
if (ApiUtils.DEBUG) {
System.out.println("API DEBUG ACTION \n[from=" + form + "]"
+ "\n[resp=" + JSON.toJSONString(resp) + "]"
+ "\n[time=" + (System.currentTimeMillis() - start) + "ms]");
}
String clientIP = ServletUtil.getClientIP(request);
appService.addLog(form, clientIP, logType, resp.getMsg(), stackTrace, System.currentTimeMillis() - start);
return resp;
}
/**
* 开关调试日志
* <p>
* 2016年10月3日 下午5:47:46
*/
@RequestMapping("/debug")
public ApiResp debug(HttpServletRequest request) {
ApiForm from = ServletUtil.toBean(request, ApiForm.class, true);
ApiUtils.DEBUG = !ApiUtils.DEBUG;
return new ApiResp(from).setData(ApiUtils.DEBUG);
}
}
......@@ -43,8 +43,8 @@ public class ApiForm {
public boolean parseBizContent() {
try {
boolean flag = ConfigCache.getValueToBoolean("API.PARAM.ENCRYPT"); // API参数是否加密
if (StrUtil.isNotBlank(bizContent) && flag) {
bizContent = ApiUtils.decode(bizContent);
if (StrUtil.isNotBlank(bizContent) && flag && StrUtil.isNotBlank(sign)) {
bizContent = ApiUtils.decode(bizContent, "BASE64");
}
bizContentJson = JSON.parseObject(bizContent);
if (bizContentJson == null) {
......@@ -201,15 +201,23 @@ public class ApiForm {
return innerMap;
}
public TreeMap<String, String> getSignTreeMap() {
public String getSignStr(String key) {
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put("timestamp", this.timestamp);
treeMap.put("nonce", this.nonce);
// treeMap.put("nonce", this.nonce);
treeMap.put("method", this.method);
treeMap.put("version", this.version);
String bizContent = StrUtil.isBlank(this.bizContent) ? "" : this.bizContent;
treeMap.put("bizContent", bizContent);
return treeMap;
// 原始请求串
StringBuilder src = new StringBuilder();
for (Map.Entry<String, String> entry : treeMap.entrySet()) {
src.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
src.append("key=").append(key);
return src.toString();
}
}
......@@ -3,8 +3,6 @@ package com.zq.api.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.zq.api.config.ConfigCache;
import com.zq.api.constant.ApiCodeEnum;
import com.zq.api.dao.ApiLogDao;
import com.zq.api.entity.ApiUser;
import com.zq.api.feign.SysFeign;
......@@ -12,7 +10,7 @@ import com.zq.api.form.ApiForm;
import com.zq.api.form.ApiResp;
import com.zq.api.utils.ApiUtils;
import com.zq.api.utils.ReflectionUtils;
import com.zq.common.config.security.ApiTokenUtils;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.entity.ApiLog;
import com.zq.common.vo.ApiTokenVo;
import com.zq.common.vo.ResultVo;
......@@ -37,6 +35,7 @@ public class ApiService {
private final ApiLogDao apiLogDao;
private final SysFeign sysFeign;
private final RedisUtils redisUtils;
/**
* 允许用户未登录状态下执行的方法名
......@@ -70,18 +69,9 @@ public class ApiService {
if (!METHOD_LIST.contains(form.getMethod())) {
return ApiUtils.getMethodError(form);
}
// 登陆验证标识
boolean validFlag = ConfigCache.getValueToBoolean("API.LOGIN.VALID");
IApiLogic apiLogic = getApiLogic(form);
if (validFlag) {
// 先进行登陆验证。如果验证失败,直接返回错误
ApiResp validResp = apiLogic.valid(form);
if (!validResp.getCode().equals(ApiCodeEnum.SUCCESS.code())) {
return validResp;
}
}
// 调用接口方法,利用反射更简洁
IApiLogic apiLogic = getApiLogic(form);
return (ApiResp) ReflectionUtils.invokeMethod(apiLogic, form.getMethod(), new Class<?>[]{ApiForm.class}, new Object[]{form});
}
......@@ -93,41 +83,37 @@ public class ApiService {
* @param appSecret
* @return
*/
public ApiResp auth(ApiForm form, String appId, String appSecret) {
public ApiResp auth(ApiForm form, String appId, String appSecret, String token) {
boolean contains = Arrays.asList(allowMethod).contains(form.getMethod());
if (contains) {
return ApiUtils.getSuccessResp(form);
} else if (StrUtil.isBlank(token)) {
return ApiUtils.getLoginValidError(form);
}
if (StrUtil.isNotBlank(appId) && StrUtil.isNotBlank(appSecret)) {
ResultVo resultVo = sysFeign.getApiUserByAppId(appId, appSecret);
if (!resultVo.isSuccess() || resultVo.getData() == null) {
return ApiUtils.getLoginValidError(form);
}
ApiUser apiUser = BeanUtil.copyProperties(resultVo.getData(), ApiUser.class);
ApiTokenVo apiTokenVo = ApiTokenVo.builder()
.userId(apiUser.getId())
.name(apiUser.getName())
.roleLevel(apiUser.getRoleLevel())
.build();
form.setUserId(apiUser.getId() == null ? null : apiUser.getId().toString());
form.setAppId(apiUser.getAppId());
form.setApiTokenVo(apiTokenVo);
} else {
if (StrUtil.isBlank(form.getToken())) {
return ApiUtils.getLoginValidError(form);
}
ResultVo resultVo = sysFeign.getApiUserByAppId(appId, appSecret);
if (!resultVo.isSuccess() || resultVo.getData() == null) {
return ApiUtils.getAppIdValidError(form);
}
ApiTokenVo tokenVo = ApiTokenUtils.getAppTokenVo(form.getToken());
if (tokenVo == null) {
return ApiUtils.getLoginValidError(form);
} else {
form.setUserId(String.valueOf(tokenVo.getUserId()));
form.setApiTokenVo(tokenVo);
}
ApiUser apiUser = BeanUtil.copyProperties(resultVo.getData(), ApiUser.class);
ApiTokenVo apiTokenVo = ApiTokenVo.builder()
.userId(apiUser.getId())
.name(apiUser.getName())
.roleLevel(apiUser.getRoleLevel())
.build();
form.setUserId(apiUser.getId() == null ? null : apiUser.getId().toString());
form.setAppId(apiUser.getAppId());
form.setApiTokenVo(apiTokenVo);
// 认证签名
String key = redisUtils.getStr(form.getToken());
String serverSign = ApiUtils.getSign(form.getSignStr(key == null ? "" : key));
if (!serverSign.equals(form.getSign())) {
return ApiUtils.getCheckSignValidError(form);
}
return ApiUtils.getSuccessResp(form);
}
......
package com.zq.api.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.zq.api.config.ConfigCache;
import com.zq.api.constant.ApiCodeEnum;
import com.zq.api.dao.ApiLogDao;
import com.zq.api.entity.ApiUser;
import com.zq.api.feign.SysFeign;
import com.zq.api.form.ApiForm;
import com.zq.api.form.ApiResp;
import com.zq.api.utils.ApiUtils;
import com.zq.api.utils.ReflectionUtils;
import com.zq.common.config.security.ApiTokenUtils;
import com.zq.common.entity.ApiLog;
import com.zq.common.vo.ApiTokenVo;
import com.zq.common.vo.ResultVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author wilmiam
* @since 2021-09-28 15:44
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AppService {
private final ApiLogDao apiLogDao;
private final SysFeign sysFeign;
/**
* 允许用户未登录状态下执行的方法名
*/
private final String[] allowMethod = {"sendCode", "resetPassword", "phoneLogin", "passwdLogin", "getAppVersion"};
private static final List<String> METHOD_LIST;
static {
METHOD_LIST = methodList();
}
public IApiLogic getApiLogic(ApiForm form) {
IApiLogic apiLogic = ApiUtils.getApiLogic(form);
return apiLogic;
}
public static List<String> methodList() {
List<String> methodList = new ArrayList<>();
Method[] methods = IApiLogic.class.getMethods();
for (Method method : methods) {
Class<?>[] params = method.getParameterTypes();
if (params.length == 1 && (params[0] == ApiForm.class)) {
methodList.add(method.getName());
}
}
return methodList;
}
public ApiResp action(ApiForm form) throws Exception {
if (!METHOD_LIST.contains(form.getMethod())) {
return ApiUtils.getMethodError(form);
}
// 登陆验证标识
boolean validFlag = ConfigCache.getValueToBoolean("API.SIGN.VALID");
IApiLogic apiLogic = getApiLogic(form);
if (validFlag) {
// 先进行登陆验证。如果验证失败,直接返回错误
ApiResp validResp = apiLogic.valid(form);
if (!validResp.getCode().equals(ApiCodeEnum.SUCCESS.code())) {
return validResp;
}
}
// 调用接口方法,利用反射更简洁
return (ApiResp) ReflectionUtils.invokeMethod(apiLogic, form.getMethod(), new Class<?>[]{ApiForm.class}, new Object[]{form});
}
/**
* 身份验证
*
* @param form
* @param appId
* @param appSecret
* @return
*/
public ApiResp auth(ApiForm form, String appId, String appSecret) {
boolean contains = Arrays.asList(allowMethod).contains(form.getMethod());
if (contains) {
return ApiUtils.getSuccessResp(form);
}
if (StrUtil.isNotBlank(appId) && StrUtil.isNotBlank(appSecret)) {
ResultVo resultVo = sysFeign.getApiUserByAppId(appId, appSecret);
if (!resultVo.isSuccess() || resultVo.getData() == null) {
return ApiUtils.getAppIdValidError(form);
}
ApiUser apiUser = BeanUtil.copyProperties(resultVo.getData(), ApiUser.class);
ApiTokenVo apiTokenVo = ApiTokenVo.builder()
.userId(apiUser.getId())
.name(apiUser.getName())
.roleLevel(apiUser.getRoleLevel())
.build();
form.setUserId(apiUser.getId() == null ? null : apiUser.getId().toString());
form.setAppId(apiUser.getAppId());
form.setApiTokenVo(apiTokenVo);
} else {
if (StrUtil.isBlank(form.getToken())) {
return ApiUtils.getLoginValidError(form);
}
ApiTokenVo tokenVo = ApiTokenUtils.getAppTokenVo(form.getToken());
if (tokenVo == null) {
return ApiUtils.getLoginValidError(form);
} else {
form.setUserId(String.valueOf(tokenVo.getUserId()));
form.setApiTokenVo(tokenVo);
}
}
return ApiUtils.getSuccessResp(form);
}
@Async
public void addLog(ApiForm form, String ip, String logType, String respMsg, String errorInfo, Long timeCost) {
apiLogDao.insert(ApiLog.builder()
.appId(form.getAppId())
.userId(form.getUserId())
.method(form.getMethod())
.version(form.getVersion())
.bizContent(form.getBizContent())
.ip(ip)
.logType(logType)
.respMsg(respMsg)
.stackTrace(errorInfo)
.timeCost(timeCost)
.createTime(DateUtil.date().toJdkDate())
.build());
}
}
......@@ -5,8 +5,6 @@ import com.zq.api.form.ApiResp;
import com.zq.api.service.IApiLogic;
import com.zq.api.utils.ApiUtils;
import java.util.TreeMap;
/**
* API基础类
* <p>
......@@ -32,7 +30,7 @@ public abstract class BaseApiLogic implements IApiLogic {
return ApiUtils.getCheckSignValidError(form);
}
String serverSign = ApiUtils.getSign(form.getSignTreeMap());
String serverSign = ApiUtils.getSign(form.getSignStr(""));
if (!serverSign.equals(form.getSign())) {
return ApiUtils.getCheckSignValidError(form);
}
......
package com.zq.api.utils;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zq.api.constant.ApiCodeEnum;
import com.zq.api.form.ApiForm;
import com.zq.api.form.ApiResp;
......@@ -12,17 +10,11 @@ import com.zq.api.service.impl.ApiV101Logic;
import com.zq.common.encrypt.EncryptUtils;
import com.zq.common.encrypt.RsaUtils;
import com.zq.common.vo.ResultVo;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
@Component
public class ApiUtils {
......@@ -68,7 +60,7 @@ public class ApiUtils {
}
/**
* 获取登录严重异常
* 获取登录验证异常
* <p>
* 2016年9月29日 上午11:44:38
*
......@@ -79,6 +71,17 @@ public class ApiUtils {
}
/**
* 获取AppID验证异常
* <p>
* 2016年9月29日 上午11:44:38
*
* @return
*/
public static ApiResp getAppIdValidError(ApiForm form) {
return new ApiResp(form, ApiCodeEnum.APPID_VALID_ERROR);
}
/**
* 版本错误返回resp
* <p>
* 2016年9月29日 上午11:44:38
......@@ -171,12 +174,19 @@ public class ApiUtils {
* 2017年3月15日 下午1:49:09
*
* @param params
* @param encryptType
* @return
* @throws UnsupportedEncodingException
*/
public static String decode(String params) throws UnsupportedEncodingException {
params = URLDecoder.decode(params, "utf-8");
params = EncryptUtils.rsaDecodeByPrivateKey(params, RsaUtils.privateKey);
public static String decode(String params, String encryptType) {
if (StringUtils.isBlank(params)) {
return "";
}
params = EncryptUtils.urlDecode(params, "UTF-8");
if ("RSA".equals(encryptType)) {
params = EncryptUtils.rsaDecodeByPrivateKey(params, RsaUtils.privateKey);
} else {
params = EncryptUtils.base64Decode(params);
}
return params;
}
......@@ -186,15 +196,19 @@ public class ApiUtils {
* 2017年3月15日 下午1:49:09
*
* @param params
* @param encryptType
* @return
* @throws UnsupportedEncodingException
*/
public static String encode(String params) throws UnsupportedEncodingException {
params = EncryptUtils.rsaDecodeByPrivateKey(params, RsaUtils.publicKey);
public static String encode(String params, String encryptType) {
if (StringUtils.isBlank(params)) {
return "";
}
params = URLEncoder.encode(params, "utf-8");
if ("RSA".equals(encryptType)) {
params = EncryptUtils.rsaDecodeByPrivateKey(params, RsaUtils.publicKey);
} else {
params = EncryptUtils.base64Decode(params);
}
params = EncryptUtils.urlEncode(params, "UTF-8");
return params;
}
......@@ -203,16 +217,11 @@ public class ApiUtils {
* <p>
* 2017年3月15日 下午3:14:27
*
* @param paramMaps
* @param content
* @return
*/
public static String getSign(TreeMap<String, String> paramMaps) {
// 原始请求串
StringBuilder src = new StringBuilder();
for (Map.Entry<String, String> entry : paramMaps.entrySet()) {
src.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
return MD5.create().digestHex(src.toString());
public static String getSign(String content) {
return MD5.create().digestHex(content).toUpperCase();
}
}
package com.zq.system.modules.system.service;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
......@@ -141,10 +142,10 @@ public class SingleService {
String serviceUrl = request.getParameter("service");
AssertUtils.hasText(serviceUrl, "跳转地址为空");
String passwd = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, vo.getPasswd());
// String passwd = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, vo.getPasswd());
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(vo.getUsername(), passwd);
new UsernamePasswordAuthenticationToken(vo.getUsername(), vo.getPasswd());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成令牌
......@@ -159,7 +160,8 @@ public class SingleService {
response.setHeader(properties.getHeader(), properties.getTokenStartWith() + token);
response.sendRedirect(serviceUrl + "?" + properties.getHeader() + "=" + properties.getTokenStartWith() + token + "&key=" + key);
} catch (Exception e) {
e.printStackTrace();
log.error("【{}】单点登录错误 => {}", vo.getUsername(), e.getMessage());
ServletUtil.write(response, e.getMessage(), "application/json;charset=utf-8");
}
}
......@@ -170,7 +172,7 @@ public class SingleService {
SysInfo sysInfo = sysInfoDao.selectOne(Wrappers.lambdaQuery(SysInfo.builder().appId(appId).build()));
BindUserInfo bindUserInfo = blindUserInfoDao.selectOne(Wrappers.lambdaQuery(BindUserInfo.builder().sysId(sysInfo.getId()).userId(adminContext.getUserId()).build()));
AssertUtils.notNull(bindUserInfo, 501, "用户未绑定系统");
AssertUtils.notNull(bindUserInfo, 501, "用户未绑定系统");
Map<String, Object> data = new HashMap<>();
data.put("userId", bindUserInfo.getSystemUserId());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment