This commit is contained in:
Rinjae
2025-09-05 19:06:22 +09:00
commit a1d74cf004
333 changed files with 47708 additions and 0 deletions
@@ -0,0 +1,250 @@
package com.eactive.eai.authserver.config;
import java.security.KeyPair;
import javax.sql.DataSource;
import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.logger.EAIDBLogControl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import com.eactive.eai.authserver.provider.ElinkJdbcTokenStore;
import com.eactive.eai.common.dao.Keys;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.ServiceLocator;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Configuration
@EnableAuthorizationServer
@Order(6)
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
public static final String PROP_GROUP_AUTH_SERVER = "OAuthServer";
public static final String PROP_KEYSTORE_PATH = "certification.keystorePath";
public static final String PROP_KEYSTORE_PW = "certification.keystorePassword";
public static final String PROP_KEY_ALIAS = "certification.keyAlias";
public static final String PROP_KEY_PW = "certification.keyPassword";
public static final String PROP_TOKEN_TYPE = "oauth2.tokenType";
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private TokenIssuanceLogDAO tokenIssuanceLogDAO;
@Autowired
@Qualifier("eLinkClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
@Qualifier("eLinkUserDetailsService")
private UserDetailsService userDetailsService;
@Override
public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()") // /oauth/token_key
.checkTokenAccess("permitAll()") // /oauth/check_token
.allowFormAuthenticationForClients();
security.addTokenEndpointAuthenticationFilter(new OAuthRequestLoggingFilter());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
PropGroupVO vo = PropManager.getInstance().getPropGroupVO(PROP_GROUP_AUTH_SERVER);
String tokenType = "JWT";
if (vo != null) {
tokenType = vo.getProperty(PROP_TOKEN_TYPE, "JWT");
}
endpoints.pathMapping("/oauth/token", "/api/v1/oauth/token");
endpoints.authenticationManager(authenticationManager);
endpoints.userDetailsService(userDetailsService);
// JWT 토큰을 사용하는 경우
JwtAccessTokenConverter converter = jwtAccessTokenConverter();
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
// JWT 변환 후에 로깅하도록 순서 변경
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(converter, new TokenIssuanceLogEnhancer())
);
endpoints
.tokenStore(new JwtTokenStore(converter))
.accessTokenConverter(converter)
.tokenEnhancer(tokenEnhancerChain);
endpoints.exceptionTranslator(new CustomWebResponseExceptionTranslator());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
PropGroupVO vo = PropManager.getInstance().getPropGroupVO(PROP_GROUP_AUTH_SERVER);
String keystorePath = "/certificate/elink-oauth-dev.jks";
String keystorePassword = "elink1234";
String keyAlias = "elink-oauth";
String keyPassword = "elink1234";
if (vo != null) {
keystorePath = vo.getProperty(PROP_KEYSTORE_PATH);
keystorePassword = vo.getProperty(PROP_KEYSTORE_PW);
keyAlias = vo.getProperty(PROP_KEY_ALIAS);
keyPassword = vo.getProperty(PROP_KEY_PW);
} else {
logger.warn("The properties has not been set.[" + PROP_GROUP_AUTH_SERVER + "]");
}
Resource keystore = new ClassPathResource(keystorePath);
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keystore, keystorePassword.toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(keyAlias, keyPassword.toCharArray());
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair);
return converter;
}
private DataSource getDataSource() {
try {
ServiceLocator sl = ServiceLocator.getInstance();
DataSource dataSource = sl.getDataSource(Keys.EAI_DATASOURCE);
return dataSource;
} catch (Exception e) {
logger.error("Failed get [" + Keys.EAI_DATASOURCE + "]");
logger.error(e.getMessage());
return null;
}
}
private class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
OAuth2Exception oAuth2Exception;
boolean isRestrictionExempt = false;
if (e instanceof OAuth2Exception) {
oAuth2Exception = (OAuth2Exception) e;
if(e instanceof IssueLimitOAuth2Exception){
isRestrictionExempt = true;
}
} else {
oAuth2Exception = new OAuth2Exception("Server error", e);
}
// 토큰 발급 실패 로깅
logTokenIssuance(false, isRestrictionExempt, oAuth2Exception.getMessage(), null);
return ResponseEntity
.status(oAuth2Exception.getHttpErrorCode())
.body(oAuth2Exception);
}
}
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);
}
}
}
@@ -0,0 +1,13 @@
package com.eactive.eai.authserver.config;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
public class IssueLimitOAuth2Exception extends OAuth2Exception {
public IssueLimitOAuth2Exception(String msg, Throwable t) {
super(msg, t);
}
public IssueLimitOAuth2Exception(String msg) {
super(msg);
}
}
@@ -0,0 +1,54 @@
package com.eactive.eai.authserver.config;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
public class OAuthRequestLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
RequestContextData data = new RequestContextData();
data.setClientId(request.getParameter("client_id"));
data.setIpAddress(extractIpAddress(request));
data.setGrantType(request.getParameter("grant_type"));
data.setScope(request.getParameter("scope"));
data.setUsername(request.getParameter("username"));
RequestContextData.ThreadLocalRequestContext.set(data);
filterChain.doFilter(request, response);
} finally {
RequestContextData.ThreadLocalRequestContext.clear();
}
}
private String extractIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
}
}
@@ -0,0 +1,30 @@
package com.eactive.eai.authserver.config;
import lombok.Data;
@Data
public class RequestContextData {
private String clientId;
private String ipAddress;
private String grantType;
private String scope;
private String username;
// Getters and setters
public static class ThreadLocalRequestContext {
private static final ThreadLocal<RequestContextData> contextHolder = new ThreadLocal<>();
public static void set(RequestContextData data) {
contextHolder.set(data);
}
public static RequestContextData get() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
}
@@ -0,0 +1,54 @@
package com.eactive.eai.authserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("eLinkUserDetailsService")
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.headers().frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.and()
.formLogin();
// .and()
// .httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@@ -0,0 +1,49 @@
package com.eactive.eai.authserver.custom;
import java.io.Serializable;
@SuppressWarnings("serial")
public class SnapOAuth2AccessTokenRequest implements Serializable {
private String grantType;
private String authCode;
private String refreshToken;
private Object additionalInfo;
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getAuthCode() {
return authCode;
}
public void setAuthCode(String authCode) {
this.authCode = authCode;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public Object getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(Object additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return "SnapOAuth2AccessTokenRequest [grantType=" + grantType + ", authCode=" + authCode + ", refreshToken="
+ refreshToken + ", additionalInfo=" + additionalInfo + "]";
}
}
@@ -0,0 +1,72 @@
package com.eactive.eai.authserver.custom;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@SuppressWarnings("serial")
@JsonInclude(Include.NON_NULL)
public class SnapOAuth2AccessTokenResponse implements Serializable {
private String responseCode;
private String responseMessage;
private String accessToken;
private String tokenType;
private String expiresIn;
private Object additionalInfo;
public String getResponseCode() {
return responseCode;
}
public void setResponseCode(String responseCode) {
this.responseCode = responseCode;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
public Object getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(Object additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return "SnapOAuth2AccessTokenResponse [responseCode=" + responseCode + ", responseMessage=" + responseMessage
+ ", accessToken=" + accessToken + ", tokenType=" + tokenType + ", expiresIn=" + expiresIn
+ ", additionalInfo=" + additionalInfo + "]";
}
}
@@ -0,0 +1,222 @@
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.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.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.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.service.OAuth2Manager;
import com.eactive.eai.authserver.util.BeanUtils;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.util.Logger;
@Controller
public class SnapOAuth2Controller {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static final String HEADER_TIMESTAMP = "X-TIMESTAMP";
private static final String HEADER_CLIENT_ID = "X-CLIENT-KEY";
private static final String HEADER_SIGNATURE = "X-SIGNATURE";
private static final String SERVICE_CODE_ACCESS_TOKEN = "00"; // 임시설정
@RequestMapping(value = "/bukopin_snap_api/v1.0/access-token/b2b", method = RequestMethod.POST, produces = "application/json; charset=\"UTF-8\"")
@ResponseBody
public SnapOAuth2AccessTokenResponse token(@RequestBody SnapOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
if (logger.isDebug()) {
logger.debug(tokenRequest.toString());
}
SnapOAuth2AccessTokenResponse responseToken = new SnapOAuth2AccessTokenResponse();
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 timeStamp = request.getHeader(HEADER_TIMESTAMP);
String clientId = request.getHeader(HEADER_CLIENT_ID);
String signature = request.getHeader(HEADER_SIGNATURE);
ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
verifyClient(clientDetails, timeStamp, clientId, signature);
response.setHeader(HEADER_TIMESTAMP, timeStamp);
response.setHeader(HEADER_CLIENT_ID, clientId);
HashMap<String, String> authorizationParameters = new HashMap<String, String>();
authorizationParameters.put(OAuth2Utils.GRANT_TYPE, tokenRequest.getGrantType());
authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId);
authorizationParameters.put("client_secret", clientDetails.getClientSecret());
Set<String> responseType = new HashSet<String>();
responseType.add(tokenRequest.getGrantType());
OAuth2Request authorizationRequest = new OAuth2Request(authorizationParameters, clientId, null, true, null,
null, "", responseType, null);
Principal principal = new OAuth2Authentication(authorizationRequest, null);
ResponseEntity<OAuth2AccessToken> result = tokenEndpoint().postAccessToken(principal,
authorizationParameters);
OAuth2AccessToken token = result.getBody();
responseToken.setAccessToken(token.getValue());
responseToken.setExpiresIn(String.valueOf(token.getExpiresIn()));
responseToken.setTokenType(token.getTokenType());
} 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 timeStamp, String clientId, String signatureStr)
throws JwtAuthException {
if (StringUtils.isBlank(timeStamp)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {" + HEADER_TIMESTAMP + "}");
}
if (StringUtils.isBlank(clientId)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {" + HEADER_CLIENT_ID + "}");
}
if (StringUtils.isBlank(signatureStr)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {" + HEADER_SIGNATURE + "}");
}
if (clientDetails == null) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [client not found]");
}
String publicKeyStr = ((ClientVO) clientDetails).getSecurityKey();
if (StringUtils.isBlank(publicKeyStr)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [public key not found]");
}
try {
String message = clientId + "|" + timeStamp;
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(message.getBytes(StandardCharsets.UTF_8));
if (!signature.verify(Base64.getDecoder().decode(signatureStr))) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [Signature not matched]");
}
} catch (JwtAuthException e) {
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [Signature fail]");
}
}
private TokenEndpoint tokenEndpoint() {
return BeanUtils.getBean("tokenEndpoint", TokenEndpoint.class);
}
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);
}
}
@@ -0,0 +1,77 @@
package com.eactive.eai.authserver.provider;
import javax.sql.DataSource;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
/**
* ** Oracle DDL
* @formatter:off
* CRATE TABLE tseaiau05 (
* TOKEN_ID VARCHAR(255),
* TOKEN BLOB,
* AUTHENTICATION_ID VARCHAR(255) PRIMARY KEY,
* USER_NAME VARCHAR(255),
* CLIENT_ID VARCHAR(255),
* AUTHENTICATION BLOB,
* REFRESH_TOKEN VARCHAR(255),
* CREATE_TIME timestamp default systimestamp
* );
*
* CREATE TABLE tseaiau06 (
* TOKEN_ID VARCHAR(255),
* TOKEN BLOB,
* AUTHENTICATION BLOB,
* CREATE_TIME timestamp default systimestamp
* );
*
* CREATE INDEX IX_TSEAIAU05_01 ON TSEAIAU05 (CREATE_TIME);
* CREATE INDEX IX_TSEAIAU06_01 ON TSEAIAU06 (CREATE_TIME);
* @formatter:on
*/
public class ElinkJdbcTokenStore extends JdbcTokenStore {
private static final String ELINK_ACCESS_TOKEN_INSERT_STATEMENT = "insert into tseaiau05 (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)";
private static final String ELINK_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from tseaiau05 where token_id = ?";
private static final String ELINK_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from tseaiau05 where token_id = ?";
private static final String ELINK_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT = "select token_id, token from tseaiau05 where authentication_id = ?";
private static final String ELINK_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT = "select token_id, token from tseaiau05 where user_name = ? and client_id = ?";
private static final String ELINK_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT = "select token_id, token from tseaiau05 where user_name = ?";
private static final String ELINK_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT = "select token_id, token from tseaiau05 where client_id = ?";
private static final String ELINK_ACCESS_TOKEN_DELETE_STATEMENT = "delete from tseaiau05 where token_id = ?";
private static final String ELINK_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT = "delete from tseaiau05 where refresh_token = ?";
private static final String ELINK_REFRESH_TOKEN_INSERT_STATEMENT = "insert into tseaiau06 (token_id, token, authentication) values (?, ?, ?)";
private static final String ELINK_REFRESH_TOKEN_SELECT_STATEMENT = "select token_id, token from tseaiau06 where token_id = ?";
private static final String ELINK_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from tseaiau06 where token_id = ?";
private static final String ELINK_REFRESH_TOKEN_DELETE_STATEMENT = "delete from tseaiau06 where token_id = ?";
private String insertAccessTokenSql = ELINK_ACCESS_TOKEN_INSERT_STATEMENT;
private String selectAccessTokenSql = ELINK_ACCESS_TOKEN_SELECT_STATEMENT;
private String selectAccessTokenAuthenticationSql = ELINK_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
private String selectAccessTokenFromAuthenticationSql = ELINK_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT;
private String selectAccessTokensFromUserNameAndClientIdSql = ELINK_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT;
private String selectAccessTokensFromUserNameSql = ELINK_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT;
private String selectAccessTokensFromClientIdSql = ELINK_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT;
private String deleteAccessTokenSql = ELINK_ACCESS_TOKEN_DELETE_STATEMENT;
private String insertRefreshTokenSql = ELINK_REFRESH_TOKEN_INSERT_STATEMENT;
private String selectRefreshTokenSql = ELINK_REFRESH_TOKEN_SELECT_STATEMENT;
private String selectRefreshTokenAuthenticationSql = ELINK_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
private String deleteRefreshTokenSql = ELINK_REFRESH_TOKEN_DELETE_STATEMENT;
private String deleteAccessTokenFromRefreshTokenSql = ELINK_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT;
public ElinkJdbcTokenStore(DataSource dataSource) {
super(dataSource);
super.setInsertAccessTokenSql(insertAccessTokenSql);
super.setSelectAccessTokenSql(selectAccessTokenSql);
super.setDeleteAccessTokenSql(deleteAccessTokenSql);
super.setInsertRefreshTokenSql(insertRefreshTokenSql);
super.setSelectRefreshTokenSql(selectRefreshTokenSql);
super.setDeleteRefreshTokenSql(deleteRefreshTokenSql);
super.setSelectAccessTokenAuthenticationSql(selectAccessTokenAuthenticationSql);
super.setSelectRefreshTokenAuthenticationSql(selectRefreshTokenAuthenticationSql);
super.setSelectAccessTokenFromAuthenticationSql(selectAccessTokenFromAuthenticationSql);
super.setDeleteAccessTokenFromRefreshTokenSql(deleteAccessTokenFromRefreshTokenSql);
super.setSelectAccessTokensFromUserNameSql(selectAccessTokensFromUserNameSql);
super.setSelectAccessTokensFromUserNameAndClientIdSql(selectAccessTokensFromUserNameAndClientIdSql);
super.setSelectAccessTokensFromClientIdSql(selectAccessTokensFromClientIdSql);
}
}