|
|
|
@@ -0,0 +1,288 @@
|
|
|
|
|
package com.eactive.eai.authserver.custom;
|
|
|
|
|
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
|
import java.security.KeyFactory;
|
|
|
|
|
import java.security.KeyPair;
|
|
|
|
|
import java.security.KeyPairGenerator;
|
|
|
|
|
import java.security.Principal;
|
|
|
|
|
import java.security.PublicKey;
|
|
|
|
|
import java.security.SecureRandom;
|
|
|
|
|
import java.security.Signature;
|
|
|
|
|
import java.security.spec.X509EncodedKeySpec;
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
import java.util.Base64;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
|
|
import javax.crypto.Cipher;
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
import org.apache.commons.lang3.math.NumberUtils;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
|
|
|
|
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
|
|
|
|
import org.springframework.security.oauth2.provider.ClientDetails;
|
|
|
|
|
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
|
|
|
|
import org.springframework.security.oauth2.provider.OAuth2Request;
|
|
|
|
|
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
|
|
|
|
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
|
|
|
|
import org.springframework.stereotype.Controller;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMethod;
|
|
|
|
|
import org.springframework.web.bind.annotation.ResponseBody;
|
|
|
|
|
|
|
|
|
|
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
|
|
|
|
|
import com.eactive.eai.authserver.config.IssueLimitOAuth2Exception;
|
|
|
|
|
import com.eactive.eai.authserver.config.AuthorizationServerConfig;
|
|
|
|
|
import com.eactive.eai.authserver.config.RequestContextData;
|
|
|
|
|
import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
|
|
|
|
|
import com.eactive.eai.authserver.service.OAuth2Manager;
|
|
|
|
|
import com.eactive.eai.authserver.util.BeanUtils;
|
|
|
|
|
import com.eactive.eai.authserver.vo.ClientVO;
|
|
|
|
|
import com.eactive.eai.common.dao.DAOException;
|
|
|
|
|
import com.eactive.eai.common.logger.EAIDBLogControl;
|
|
|
|
|
import com.eactive.eai.common.util.Logger;
|
|
|
|
|
import com.eactive.eai.common.util.UUID;
|
|
|
|
|
import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
|
|
|
|
|
|
|
|
|
|
@Controller
|
|
|
|
|
public class KjbMGOAuth2Controller {
|
|
|
|
|
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
|
|
|
|
|
|
|
|
|
private static final String HEADER_TRACEID = "traceId";
|
|
|
|
|
|
|
|
|
|
private static final String SERVICE_CODE_ACCESS_TOKEN = "00"; // 임시설정
|
|
|
|
|
@Autowired
|
|
|
|
|
private TokenIssuanceLogDAO tokenIssuanceLogDAO;
|
|
|
|
|
|
|
|
|
|
@RequestMapping(value = "/mapi/oauth2/token2", method = RequestMethod.POST, produces = "application/json; charset=\"UTF-8\"")
|
|
|
|
|
@ResponseBody
|
|
|
|
|
public KjbMGOAuth2AccessTokenResponse token(@RequestBody KjbMGOAuth2AccessTokenRequest tokenRequest,
|
|
|
|
|
HttpServletRequest request, HttpServletResponse response) {
|
|
|
|
|
if (logger.isDebug()) {
|
|
|
|
|
logger.debug(tokenRequest.toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KjbMGOAuth2AccessTokenResponse responseToken = new KjbMGOAuth2AccessTokenResponse();
|
|
|
|
|
try {
|
|
|
|
|
if (!StringUtils.equals(tokenRequest.getGrantType(), "client_credentials")) {
|
|
|
|
|
throw new JwtAuthException(
|
|
|
|
|
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
|
|
|
|
|
"Invalid Mandatory Field {grantType}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String grantType = tokenRequest.getGrantType();
|
|
|
|
|
String clientId = tokenRequest.getClientId();
|
|
|
|
|
String clientSecret = tokenRequest.getClientSecret();
|
|
|
|
|
|
|
|
|
|
String[] scopeArr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenRequest.getScope(), " ");
|
|
|
|
|
Set<String> scopeSet = new HashSet<String>();
|
|
|
|
|
for (String scope : scopeArr) {
|
|
|
|
|
scopeSet.add(scope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String[] resourceArr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenRequest.getResource(), ",");
|
|
|
|
|
Set<String> resourceSet = new HashSet<String>();
|
|
|
|
|
for (String resource : resourceArr) {
|
|
|
|
|
resourceSet.add(resource);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
|
|
|
|
|
verifyClient(clientDetails, clientId, scopeSet);
|
|
|
|
|
|
|
|
|
|
String traceId = UUID.randomUUID().toString().replace("-", "");
|
|
|
|
|
response.setHeader(HEADER_TRACEID, traceId);
|
|
|
|
|
|
|
|
|
|
HashMap<String, String> authorizationParameters = new HashMap<String, String>();
|
|
|
|
|
authorizationParameters.put(OAuth2Utils.GRANT_TYPE, grantType);
|
|
|
|
|
authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId);
|
|
|
|
|
authorizationParameters.put("client_secret", clientSecret);
|
|
|
|
|
|
|
|
|
|
Set<String> responseType = new HashSet<String>();
|
|
|
|
|
responseType.add(tokenRequest.getGrantType());
|
|
|
|
|
|
|
|
|
|
OAuth2Request authorizationRequest = new OAuth2Request(authorizationParameters, clientId, null, true, scopeSet,
|
|
|
|
|
resourceSet, "", responseType, null);
|
|
|
|
|
|
|
|
|
|
Principal principal = new OAuth2Authentication(authorizationRequest, null);
|
|
|
|
|
ResponseEntity<OAuth2AccessToken> result = tokenEndpoint().postAccessToken(principal,
|
|
|
|
|
authorizationParameters);
|
|
|
|
|
OAuth2AccessToken token = result.getBody();
|
|
|
|
|
if(StringUtils.isNotBlank(token.getTokenType()) && StringUtils.equalsIgnoreCase(token.getTokenType(), "bearer")) {
|
|
|
|
|
responseToken.setTokenType("Bearer");
|
|
|
|
|
} else { responseToken.setTokenType(token.getTokenType()); }
|
|
|
|
|
responseToken.setAccessToken(token.getValue());
|
|
|
|
|
responseToken.setExpiresIn(Long.valueOf(token.getExpiresIn()));
|
|
|
|
|
responseToken.setrefreshToken("");
|
|
|
|
|
responseToken.setExpiresOn(System.currentTimeMillis() + (token.getExpiresIn() * 1000));
|
|
|
|
|
responseToken.setScope(tokenRequest.getScope());
|
|
|
|
|
responseToken.setResource(tokenRequest.getResource());
|
|
|
|
|
} catch (JwtAuthException e) {
|
|
|
|
|
response.setStatus(NumberUtils.toInt(StringUtils.left(e.getCode(), 3), HttpStatus.UNAUTHORIZED.value()));
|
|
|
|
|
responseToken.setResponseCode(e.getCode());
|
|
|
|
|
responseToken.setResponseMessage(e.getMessage());
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
logger.error(e.getMessage());
|
|
|
|
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
|
|
|
|
responseToken.setResponseCode(
|
|
|
|
|
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"));
|
|
|
|
|
responseToken.setResponseMessage("Unauthorized. [Unknown]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return responseToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void verifyClient(ClientDetails clientDetails, String clientId, Set<String> scopeSet)
|
|
|
|
|
throws JwtAuthException {
|
|
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(clientId)) {
|
|
|
|
|
throw new JwtAuthException(
|
|
|
|
|
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
|
|
|
|
|
"Invalid Mandatory Field {client_id}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (clientDetails == null) {
|
|
|
|
|
throw new JwtAuthException(
|
|
|
|
|
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
|
|
|
|
|
"Unauthorized. [client not found]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scopeSet.isEmpty()) {
|
|
|
|
|
throw new JwtAuthException(
|
|
|
|
|
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
|
|
|
|
|
"Invalid Mandatory Field {scope}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!clientDetails.getScope().containsAll(scopeSet)) {
|
|
|
|
|
throw new JwtAuthException(
|
|
|
|
|
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
|
|
|
|
|
"Unauthorized. [Bad client credentials(Include unacceptable scope)]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TokenEndpoint tokenEndpoint() {
|
|
|
|
|
return BeanUtils.getBean("tokenEndpoint", TokenEndpoint.class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class TokenIssuanceLogEnhancer implements TokenEnhancer {
|
|
|
|
|
@Override
|
|
|
|
|
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
|
|
|
|
|
if (!EAIDBLogControl.isEnable()) {
|
|
|
|
|
logger.warn("DB logging is disabled. Skipping token issuance logging.");
|
|
|
|
|
return accessToken;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String clientId = authentication.getOAuth2Request().getClientId();
|
|
|
|
|
LocalDateTime startDateTime = LocalDateTime.now().minusHours(24);
|
|
|
|
|
|
|
|
|
|
int recentTokenCount = tokenIssuanceLogDAO.findRecentLogsForRestrictionCount(clientId, startDateTime);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
ClientVO clientVO = OAuth2Manager.getInstance().getClientInfo(clientId);
|
|
|
|
|
int dailyTokenLimit = clientVO.getDailyTokenLimit();
|
|
|
|
|
if (dailyTokenLimit > 0 && recentTokenCount >= dailyTokenLimit) {
|
|
|
|
|
throw new IssueLimitOAuth2Exception("Token issuance limit exceeded for this client");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JWT 변환 이후의 최종 토큰 값 로깅
|
|
|
|
|
logTokenIssuance(true, false, "Token issued successfully", accessToken.getValue());
|
|
|
|
|
} catch (DAOException e) {
|
|
|
|
|
logger.warn("cannot find clientVo - " + clientId);
|
|
|
|
|
}
|
|
|
|
|
return accessToken;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void logTokenIssuance(boolean isSuccess, boolean isRestrictionExempt, String resultMessage, String accessToken) {
|
|
|
|
|
try {
|
|
|
|
|
if (!EAIDBLogControl.isEnable()) {
|
|
|
|
|
logger.warn("DB logging is disabled. Skipping token issuance logging.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
RequestContextData data = RequestContextData.ThreadLocalRequestContext.get();
|
|
|
|
|
OAuth2Manager.getInstance();
|
|
|
|
|
|
|
|
|
|
TokenIssuanceLog log = new TokenIssuanceLog();
|
|
|
|
|
String clientId = data.getClientId();
|
|
|
|
|
log.setClientId(clientId);
|
|
|
|
|
|
|
|
|
|
if(StringUtils.isNotBlank(clientId)) {
|
|
|
|
|
ClientVO clientVO = OAuth2Manager.getInstance().getClientInfo(clientId);
|
|
|
|
|
log.setAppName(clientVO.getClientName());
|
|
|
|
|
log.setOrgId(clientVO.getOrgId());
|
|
|
|
|
log.setOrgName(clientVO.getOrgName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.setGrantType(data.getGrantType());
|
|
|
|
|
log.setScope(data.getScope());
|
|
|
|
|
log.setIpAddress(data.getIpAddress());
|
|
|
|
|
log.setSuccessYn(isSuccess ? "Y" : "N");
|
|
|
|
|
log.setIssuanceDateTime(LocalDateTime.now());
|
|
|
|
|
log.setRestrictionExemptYn(isRestrictionExempt ? "Y" : "N");
|
|
|
|
|
log.setResultMessage(resultMessage);
|
|
|
|
|
log.setAccessToken(accessToken); // Add access token value to the log
|
|
|
|
|
|
|
|
|
|
tokenIssuanceLogDAO.saveTokenIssuanceLog(log);
|
|
|
|
|
} catch (Throwable th) {
|
|
|
|
|
logger.error("Error while logging token issuance: " + th.getMessage(), th);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
|
// First generate a public/private key pair
|
|
|
|
|
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
|
|
|
|
|
// generator.initialize(512, new SecureRandom());
|
|
|
|
|
//generator.initialize(1024, new SecureRandom());
|
|
|
|
|
generator.initialize(2048, new SecureRandom());
|
|
|
|
|
KeyPair pair = generator.generateKeyPair();
|
|
|
|
|
|
|
|
|
|
// The private key can be used to sign (not encrypt!) a message. The public key
|
|
|
|
|
// holder can then verify the message.
|
|
|
|
|
|
|
|
|
|
String message = "sSGJDwlJsel5WeXodzd3J3MQ20KYCZpL|2022-12-21T14:36:19+07:00";
|
|
|
|
|
|
|
|
|
|
// Let's sign our message
|
|
|
|
|
Signature privateSignature = Signature.getInstance("SHA256withRSA");
|
|
|
|
|
privateSignature.initSign(pair.getPrivate());
|
|
|
|
|
privateSignature.update(message.getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
System.out.println(
|
|
|
|
|
"private key=" + Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()));
|
|
|
|
|
|
|
|
|
|
byte[] signature = privateSignature.sign();
|
|
|
|
|
//System.out.println("signature=" + new String(signature, StandardCharsets.UTF_8));
|
|
|
|
|
System.out.println("signature=" + Base64.getEncoder().encodeToString(signature));
|
|
|
|
|
|
|
|
|
|
// Let's check the signature
|
|
|
|
|
Signature publicSignature = Signature.getInstance("SHA256withRSA");
|
|
|
|
|
publicSignature.initVerify(pair.getPublic());
|
|
|
|
|
publicSignature.update(message.getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
boolean isCorrect = publicSignature.verify(signature);
|
|
|
|
|
System.out.println("public key=" + Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()));
|
|
|
|
|
|
|
|
|
|
System.out.println("Signature correct: " + isCorrect);
|
|
|
|
|
|
|
|
|
|
// The public key can be used to encrypt a message, the private key can be used
|
|
|
|
|
// to decrypt it.
|
|
|
|
|
// Encrypt the message
|
|
|
|
|
Cipher encryptCipher = Cipher.getInstance("RSA");
|
|
|
|
|
encryptCipher.init(Cipher.ENCRYPT_MODE, pair.getPublic());
|
|
|
|
|
|
|
|
|
|
byte[] cipherText = encryptCipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
|
|
|
|
|
|
|
|
|
|
// Now decrypt it
|
|
|
|
|
Cipher decriptCipher = Cipher.getInstance("RSA");
|
|
|
|
|
decriptCipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
|
|
|
|
|
|
|
|
|
|
String decipheredMessage = new String(decriptCipher.doFinal(cipherText), StandardCharsets.UTF_8);
|
|
|
|
|
|
|
|
|
|
System.out.println(decipheredMessage);
|
|
|
|
|
}
|
|
|
|
|
}
|