package tech.powerjob.server.openapi.security; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import tech.powerjob.client.module.AppAuthRequest; import tech.powerjob.client.module.AppAuthResult; import tech.powerjob.common.OpenAPIConstant; import tech.powerjob.common.enums.ErrorCodes; import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.server.auth.common.utils.HttpServletUtils; import tech.powerjob.server.auth.jwt.JwtService; import tech.powerjob.server.auth.jwt.ParseResult; import tech.powerjob.server.core.service.AppInfoService; import tech.powerjob.server.persistence.remote.model.AppInfoDO; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Map; import java.util.Optional; /** * OpenApiSecurityService * * @author tjq * @since 2024/2/19 */ @Slf4j @Service public class OpenApiSecurityServiceImpl implements OpenApiSecurityService { @Resource private JwtService jwtService; @Resource private AppInfoService appInfoService; private static final String JWT_KEY_APP_ID = "appId"; private static final String JWT_KEY_APP_PASSWORD = "password"; private static final String JWT_KEY_ENCRYPT_TYPE = "encryptType"; @Override public void authAppByToken(HttpServletRequest httpServletRequest) { String token = HttpServletUtils.fetchFromHeader(OpenAPIConstant.REQUEST_HEADER_ACCESS_TOKEN, httpServletRequest); String appIdFromHeader = HttpServletUtils.fetchFromHeader(OpenAPIConstant.REQUEST_HEADER_APP_ID, httpServletRequest); if (StringUtils.isEmpty(appIdFromHeader)) { throw new PowerJobException(ErrorCodes.INVALID_REQUEST, "lack_of_appId_in_header"); } if (StringUtils.isEmpty(token)) { throw new PowerJobException(ErrorCodes.OPEN_API_AUTH_FAILED, "token_is_empty"); } ParseResult parseResult = jwtService.parse(token, null); switch (parseResult.getStatus()) { case EXPIRED: throw new PowerJobException(ErrorCodes.TOKEN_EXPIRED, parseResult.getMsg()); case FAILED: throw new PowerJobException(ErrorCodes.INVALID_TOKEN, parseResult.getMsg()); } Map jwtResult = parseResult.getResult(); Long appIdFromJwt = MapUtils.getLong(jwtResult, JWT_KEY_APP_ID); String passwordFromJwt = MapUtils.getString(jwtResult, JWT_KEY_APP_PASSWORD); String encryptType = MapUtils.getString(jwtResult, JWT_KEY_ENCRYPT_TYPE); // 校验 appId 一致性 if (!StringUtils.equals(appIdFromHeader, String.valueOf(appIdFromJwt))) { throw new PowerJobException(ErrorCodes.INVALID_REQUEST, "Inconsistent_appId_from_token_and_header"); } // 此处不考虑改密码后的缓存时间,毕竟只要改了密码,一定会报错。换言之 OpenAPI 模式下,密码不可更改 Optional appInfoOpt = appInfoService.findById(appIdFromJwt, true); if (!appInfoOpt.isPresent()) { throw new PowerJobException(ErrorCodes.INVALID_APP, "can_not_find_app"); } appInfoService.assertApp(appInfoOpt.get(), passwordFromJwt, encryptType); } @Override public AppAuthResult authAppByParam(AppAuthRequest appAuthRequest) { String appName = appAuthRequest.getAppName(); String encryptedPassword = appAuthRequest.getEncryptedPassword(); Long appId = appInfoService.assertApp(appName, encryptedPassword, appAuthRequest.getEncryptType()); Map jwtBody = Maps.newHashMap(); jwtBody.put(JWT_KEY_APP_ID, appId); jwtBody.put(JWT_KEY_APP_PASSWORD, encryptedPassword); jwtBody.put(JWT_KEY_ENCRYPT_TYPE, appAuthRequest.getEncryptType()); AppAuthResult appAuthResult = new AppAuthResult(); appAuthResult.setAppId(appId); appAuthResult.setToken(jwtService.build(jwtBody, null)); return appAuthResult; } }