package com.eactive.eai.authserver.custom; import java.security.Principal; import java.time.LocalDateTime; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; 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.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; 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.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.eactive.eai.adapter.http.dynamic.UnkownMessageLogUtils; import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException; 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.MessageUtil; import com.eactive.eai.common.util.UUID; import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog; import com.fasterxml.jackson.databind.ObjectMapper; @Controller public class DJBOAuth2Controller { 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 = { "/*/oauth/token", "/*/oauth2/token" }, method = RequestMethod.POST, consumes = "application/json", produces = "application/json; charset=UTF-8") @ResponseBody public ResponseEntity tokenJson(@RequestBody DJBOAuth2AccessTokenRequest tokenRequest, HttpServletRequest request, HttpServletResponse response) { return issueToken(tokenRequest, request, response); } @RequestMapping(value = { "/*/oauth/token", "/*/oauth2/token" }, method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded", produces = "application/json; charset=UTF-8") @ResponseBody public ResponseEntity tokenForm(@RequestParam Map params, HttpServletRequest request, HttpServletResponse response) { DJBOAuth2AccessTokenRequest tokenRequest = new DJBOAuth2AccessTokenRequest(); tokenRequest.setGrantType(params.get("grant_type")); tokenRequest.setClientId(params.get("client_id")); tokenRequest.setClientSecret(params.get("client_secret")); tokenRequest.setScope(params.get("scope")); return issueToken(tokenRequest, request, response); } @GetMapping(value = { "/*/oauth/token", "/*/oauth2/token" }, produces = "application/json; charset=UTF-8") @ResponseBody public ResponseEntity tokenGet(@RequestParam Map params, HttpServletRequest request, HttpServletResponse response) { DJBOAuth2AccessTokenRequest tokenRequest = new DJBOAuth2AccessTokenRequest(); tokenRequest.setGrantType(params.get("grant_type")); tokenRequest.setClientId(params.get("client_id")); tokenRequest.setClientSecret(params.get("client_secret")); tokenRequest.setScope(params.get("scope")); return issueToken(tokenRequest, request, response); } private ResponseEntity issueToken(DJBOAuth2AccessTokenRequest tokenRequest, HttpServletRequest request, HttpServletResponse response) { if (logger.isDebug()) { logger.debug(tokenRequest.toString()); } RequestContextData data = new RequestContextData(); data.setClientId(tokenRequest.getClientId()); data.setIpAddress(extractIpAddress(request)); data.setGrantType(tokenRequest.getGrantType()); data.setScope(tokenRequest.getScope()); data.setUsername("none"); data.setResource("none"); RequestContextData.ThreadLocalRequestContext.set(data); String grantType = tokenRequest.getGrantType(); String clientId = tokenRequest.getClientId(); String clientSecret = tokenRequest.getClientSecret(); String scopes = tokenRequest.getScope(); try { if (!StringUtils.equals(grantType, "client_credentials")) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"), "Invalid Mandatory Field {grant_type}"); } String[] scopeArr = org.springframework.util.StringUtils.tokenizeToStringArray(scopes, " "); Set scopeSet = new HashSet<>(); for (String scope : scopeArr) { scopeSet.add(scope); } ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId); verifyClient(clientDetails, clientId, clientSecret, scopeSet); String traceId = UUID.randomUUID().toString().replace("-", ""); response.setHeader(HEADER_TRACEID, traceId); HashMap authorizationParameters = new HashMap<>(); authorizationParameters.put(OAuth2Utils.GRANT_TYPE, grantType); authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); authorizationParameters.put("client_secret", clientSecret); Set responseType = new HashSet<>(); responseType.add(grantType); OAuth2Request authorizationRequest = new OAuth2Request(authorizationParameters, clientId, null, true, scopeSet, null, "", responseType, null); Principal principal = new OAuth2Authentication(authorizationRequest, null); ResponseEntity result = tokenEndpoint().postAccessToken(principal, authorizationParameters); OAuth2AccessToken token = result.getBody(); DJBOAuth2AccessTokenResponse responseToken = new DJBOAuth2AccessTokenResponse(); responseToken.setAccessToken(token.getValue()); responseToken.setTokenType(token.getTokenType()); responseToken.setExpiresIn(Long.valueOf(token.getExpiresIn())); responseToken.setScope(StringUtils.join(token.getScope(), " ")); responseToken.setJti((String) token.getAdditionalInformation().get("jti")); responseToken.setClientId((String) token.getAdditionalInformation().get("client_id")); //logTokenIssuance(true, false, "Token issued successfully", token.getValue()); ObjectMapper mapper = new ObjectMapper(); return ResponseEntity.ok(mapper.writeValueAsString(responseToken)); } catch (JwtAuthException e) { logger.info("Token request[/dj/oauth2/token] => clientId: {}, grantType: {}, scope: {}", clientId, grantType, scopes); logger.error(e.getMessage()); logTokenIssuance(false, false, e.getMessage(), null); Properties logProp = new Properties(); logProp.put("clientId", clientId); UnkownMessageLogUtils.logUnkownMessage(this.getClass().getSimpleName(), this.getClass().getSimpleName(), tokenRequest.toString(), logProp, request, response, "RECEAIIRP301", e); String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, e.getMessage()); int statusCode = NumberUtils.toInt(StringUtils.left(e.getCode(), 3), HttpStatus.UNAUTHORIZED.value()); return ResponseEntity.status(statusCode).body(errorJson); } catch (Exception e) { logger.info("Token request[/dj/oauth2/token] => clientId: {}, grantType: {}, scope: {}", clientId, grantType, scopes); logger.error(e.getMessage()); logTokenIssuance(false, false, e.getMessage(), null); Properties logProp = new Properties(); logProp.put("clientId", clientId); UnkownMessageLogUtils.logUnkownMessage(this.getClass().getSimpleName(), this.getClass().getSimpleName(), tokenRequest.toString(), logProp, request, response, "RECEAIIRP301", e); String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, "Unauthorized. [Unknown]"); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorJson); } finally { RequestContextData.ThreadLocalRequestContext.clear(); } } private void verifyClient(ClientDetails clientDetails, String clientId, String clientSecret, Set 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 (StringUtils.isBlank(clientSecret)) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"), "Invalid Mandatory Field {client_secret}"); } if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) { throw new JwtAuthException( String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"), "Unauthorized. [Bad client credentials(Client Secret is not match)]"); } 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 String extractIpAddress(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) ip = request.getHeader("Proxy-Client-IP"); if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) ip = request.getHeader("WL-Proxy-Client-IP"); if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) ip = request.getHeader("HTTP_CLIENT_IP"); if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) ip = request.getHeader("HTTP_X_FORWARDED_FOR"); if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) ip = request.getRemoteAddr(); return ip; } private TokenEndpoint tokenEndpoint() { return BeanUtils.getBean("tokenEndpoint", TokenEndpoint.class); } 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(); TokenIssuanceLog log = new TokenIssuanceLog(); String clientId = data.getClientId(); log.setClientId(clientId); if (StringUtils.isNotBlank(clientId)) { try { ClientVO clientVO = OAuth2Manager.getInstance().getClientInfo(clientId); log.setAppName(clientVO.getClientName()); log.setOrgId(clientVO.getOrgId()); log.setOrgName(clientVO.getOrgName()); } catch (DAOException e) { logger.warn("cannot find clientVO - " + clientId); } } 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); tokenIssuanceLogDAO.saveTokenIssuanceLog(log); } catch (Throwable th) { logger.error("Error while logging token issuance: " + th.getMessage(), th); } } }