add api context and logout

This commit is contained in:
dirgantarasiahaan 2023-05-25 15:39:36 +07:00
parent f0dfef725a
commit c4700af832
8 changed files with 377 additions and 1 deletions

View File

@ -1,10 +1,13 @@
package com.iconplus.smartproc.controller; package com.iconplus.smartproc.controller;
import com.iconplus.smartproc.helper.model.EmptyRequest;
import com.iconplus.smartproc.helper.model.EmptyResponse;
import com.iconplus.smartproc.model.request.LoginRequest; import com.iconplus.smartproc.model.request.LoginRequest;
import com.iconplus.smartproc.model.request.RefreshTokenRequest; import com.iconplus.smartproc.model.request.RefreshTokenRequest;
import com.iconplus.smartproc.model.response.LoginResponse; import com.iconplus.smartproc.model.response.LoginResponse;
import com.iconplus.smartproc.model.response.RefreshTokenResponse; import com.iconplus.smartproc.model.response.RefreshTokenResponse;
import com.iconplus.smartproc.service.authentication.LoginService; import com.iconplus.smartproc.service.authentication.LoginService;
import com.iconplus.smartproc.service.authentication.LogoutService;
import com.iconplus.smartproc.service.authentication.TokenManagementService; import com.iconplus.smartproc.service.authentication.TokenManagementService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -15,11 +18,14 @@ public class AuthenticationController {
private LoginService loginService; private LoginService loginService;
private TokenManagementService tokenManagementService; private TokenManagementService tokenManagementService;
private LogoutService logoutService;
public AuthenticationController(LoginService loginService, public AuthenticationController(LoginService loginService,
TokenManagementService tokenManagementService) { TokenManagementService tokenManagementService,
LogoutService logoutService) {
this.loginService = loginService; this.loginService = loginService;
this.tokenManagementService = tokenManagementService; this.tokenManagementService = tokenManagementService;
this.logoutService = logoutService;
} }
@PostMapping("/login") @PostMapping("/login")
@ -32,4 +38,10 @@ public class AuthenticationController {
return tokenManagementService.execute(refreshTokenRequest); return tokenManagementService.execute(refreshTokenRequest);
} }
@PostMapping("/logout")
public EmptyResponse logoutUser(EmptyRequest request) {
return logoutService.execute(request);
}
} }

View File

@ -0,0 +1,50 @@
package com.iconplus.smartproc.helper.context;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpHeaders;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiContext {
private HttpHeaders httpHeaders;
private String userRefId;
private String customerId;
private String otpMobileNo;
private String cid;
private String passportNo;
private String tokenScope;
private String deviceId;
private String boUserFullName;
private String boUserId;
private String authorization;
private String language;
private String requestId;
private String correlationId;
private String forwardedFor;
private String userAgent;
private String platform;
private String clientVersion;
private String channelId;
private String apiKey;
private String sleuthId;
private String userName;
private String userId;
private String smUniversalId;
private String clientId;
private String clientSecret;
private String secretKey;
private String timestamp;
private String mandiriKey;
private String signature;
private String clientIp;
private String releaseId;
private List<String> stackTrace;
private String snapshot;
}

View File

@ -0,0 +1,184 @@
package com.iconplus.smartproc.helper.context;
import brave.internal.Platform;
import com.iconplus.smartproc.helper.service.TokenUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Log4j2
public class ApiContextFactory {
private ApiContextFactory() {
}
private static final String DEFAULT_LANGUAGE = "id-id";
private static final String HEADER_AUTHORIZATION = "Authorization";
private static final String HEADER_ACCEPT_LANGUAGE = HttpHeaders.ACCEPT_LANGUAGE;
private static final String HEADER_REQUEST_ID = "X-Request-ID";
private static final String HEADER_CORRELATION_ID = "X-Correlation-ID";
private static final String HEADER_FORWARDED_FOR = "X-Forwarded-For";
private static final String HEADER_USER_AGENT = "User-Agent";
private static final String HEADER_PLATFORM = "X-Platform";
private static final String HEADER_CLIENT_VERSION = "X-Client-Version";
private static final String HEADER_CHANNEL_ID = "X-Channel-ID";
private static final String HEADER_API_KEY = "X-API-Key";
private static final String HEADER_SLEUTH_ID = "X-Sleuth-ID";
private static final String HEADER_USER_NAME = "X-User-Name";
private static final String HEADER_USER_ID = "X-User-ID";
private static final String HEADER_SM_UNIVERSAL_ID = "sm_universalId";
private static final String HEADER_CUSTOMER_ID = "X-Customer-ID";
private static final String HEADER_CLIENT_ID = "X-Client-ID";
private static final String HEADER_CLIENT_SECRET = "X-Client-Secret";
private static final String HEADER_SECRET_KEY = "X-Secret-Key";
private static final String HEADER_TIMESTAMP = "X-TIMESTAMP";
private static final String HEADER_MANDIRI_KEY = "X-MANDIRI-KEY";
private static final String HEADER_SIGNATURE = "X-SIGNATURE";
private static final String HEADER_RELEASE_ID = "X-Release-ID";
private static final String JWT_USER_ID = "user_id";
private static final String JWT_CUSTOMER_ID = "customer_id";
private static final String JWT_OTP_MOBILE_NO = "otpMobileNo";
private static final String JWT_CID = "cid";
private static final String JWT_PASSPORT_NO = "passportNo";
private static final String JWT_LOGIN_SCOPE = "scope";
private static final String JWT_DEVICE_ID = "device_id";
private static final String JWT_CLIENT_ID = "clientId";
private static final String JWT_BO_USER_FULL_NAME = "boUserFullName";
private static final String JWT_BO_USER_ID = "boUserId";
private static final String X_FORWARDED_FOR = "x-forwarded-for";
private static final String PROXY_CLIENT_IP = "proxy-client-ip";
private static final String WL_PROXY_CLIENT_IP = "wl-proxy-client-ip";
private static final String HTTP_CLIENT_IP = "http_client_ip";
private static final String HTTP_X_FORWARDED_FOR = "http_x_forwarded_for";
private static final String UNKNOWN = "unknown";
private static final String CLIENT_IP = "clientIp";
private static final List<String> ALLOW_HEADER_LIST = List
.of(HEADER_AUTHORIZATION, HEADER_ACCEPT_LANGUAGE, HEADER_REQUEST_ID,
HEADER_CORRELATION_ID, HEADER_FORWARDED_FOR, HEADER_USER_AGENT, HEADER_PLATFORM,
HEADER_CLIENT_VERSION, HEADER_CHANNEL_ID, HEADER_API_KEY, JWT_DEVICE_ID, HEADER_SLEUTH_ID, HEADER_USER_NAME,
HEADER_USER_ID, HEADER_SM_UNIVERSAL_ID, HEADER_CUSTOMER_ID, HEADER_CLIENT_ID, HEADER_CLIENT_SECRET, HEADER_SECRET_KEY,
HEADER_TIMESTAMP, HEADER_MANDIRI_KEY, HEADER_SIGNATURE, HEADER_RELEASE_ID).stream()
.map(String::toLowerCase).collect(Collectors.toList());
public static ApiContext generateApiContext() throws IOException {
ApiContext apiContext = new ApiContext();
HttpHeaders httpHeaders = constructHttpHeaders();
String platform = httpHeaders.getFirst(HEADER_PLATFORM);
String authentication = httpHeaders.getFirst(HEADER_AUTHORIZATION);
if (authentication != null) {
authentication = authentication.replace("Bearer", "");
authentication = authentication.trim();
}
if (authentication != null) {
Map<String, String> jwtBodyMap = TokenUtils.decodeToken(authentication);
apiContext.setUserId(jwtBodyMap.getOrDefault(JWT_USER_ID, ""));
apiContext.setCustomerId(jwtBodyMap.getOrDefault(JWT_CUSTOMER_ID, ""));
apiContext.setDeviceId(jwtBodyMap.getOrDefault(JWT_DEVICE_ID, ""));
apiContext.setTokenScope(jwtBodyMap.getOrDefault(JWT_LOGIN_SCOPE, ""));
apiContext.setClientId(jwtBodyMap.getOrDefault(JWT_CLIENT_ID, ""));
}
if(StringUtils.isEmpty(apiContext.getClientId())) {
apiContext.setClientId(httpHeaders.getFirst(HEADER_CLIENT_ID));
}
apiContext.setHttpHeaders(httpHeaders);
apiContext.setAuthorization(authentication);
apiContext.setLanguage(httpHeaders.getFirst(HEADER_ACCEPT_LANGUAGE));
apiContext.setRequestId(httpHeaders.getFirst(HEADER_REQUEST_ID));
apiContext.setCorrelationId(httpHeaders.getFirst(HEADER_CORRELATION_ID));
apiContext.setForwardedFor(httpHeaders.getFirst(HEADER_FORWARDED_FOR));
apiContext.setUserAgent(httpHeaders.getFirst(HEADER_USER_AGENT));
apiContext.setPlatform(httpHeaders.getFirst(HEADER_PLATFORM));
apiContext.setClientVersion(httpHeaders.getFirst(HEADER_CLIENT_VERSION));
apiContext.setChannelId(httpHeaders.getFirst(HEADER_CHANNEL_ID));
apiContext.setSleuthId(httpHeaders.getFirst(HEADER_SLEUTH_ID));
apiContext.setApiKey(httpHeaders.getFirst(HEADER_API_KEY));
apiContext.setClientSecret(httpHeaders.getFirst(HEADER_CLIENT_SECRET));
apiContext.setSecretKey(httpHeaders.getFirst(HEADER_SECRET_KEY));
apiContext.setTimestamp(httpHeaders.getFirst(HEADER_TIMESTAMP));
apiContext.setMandiriKey(httpHeaders.getFirst(HEADER_MANDIRI_KEY));
apiContext.setSignature(httpHeaders.getFirst(HEADER_SIGNATURE));
apiContext.setClientIp(httpHeaders.getFirst(CLIENT_IP));
apiContext.setReleaseId(httpHeaders.getFirst(HEADER_RELEASE_ID));
//fill the username with user id if its empty, because AD changed the design and they remove the x-user-name
String userName = httpHeaders.getFirst(HEADER_USER_NAME);
if (userName == null || userName.equals("")) {
userName = httpHeaders.getFirst(HEADER_USER_ID);
}
apiContext.setUserName(userName);
return apiContext;
}
public static HttpHeaders constructHttpHeaders() throws IOException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpServletRequest curRequest = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
Enumeration<String> headerNames = curRequest.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
String value = curRequest.getHeader(header);
if (ALLOW_HEADER_LIST.contains(header)) {
log.debug("Adding header {} with value {}", header, value);
httpHeaders.add(header, value);
} else {
log.debug("Header {} with value {} is not required to be copied", header,
value);
}
}
}
if (!httpHeaders.containsKey(HEADER_ACCEPT_LANGUAGE) || StringUtils
.isEmpty(httpHeaders.getFirst(
HEADER_ACCEPT_LANGUAGE))) {
httpHeaders.set(HEADER_ACCEPT_LANGUAGE, DEFAULT_LANGUAGE);
}
httpHeaders.add(CLIENT_IP, getClientIp(httpHeaders, curRequest.getRemoteAddr()));
return httpHeaders;
}
public static String getClientIp(HttpHeaders httpHeaders, String remoteAddr) {
String ip = httpHeaders.getFirst(X_FORWARDED_FOR);
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = httpHeaders.getFirst(PROXY_CLIENT_IP);
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = httpHeaders.getFirst(WL_PROXY_CLIENT_IP);
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = httpHeaders.getFirst(HTTP_CLIENT_IP);
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = httpHeaders.getFirst(HTTP_X_FORWARDED_FOR);
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = remoteAddr;
}
return ip;
}
}

View File

@ -0,0 +1,21 @@
package com.iconplus.smartproc.helper.context;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.annotation.RequestScope;
import java.io.IOException;
@Configuration
public class AutoConfiguration {
@RequestScope
@Bean
@ConditionalOnMissingBean(ApiContext.class)
public ApiContext apiContext() throws IOException {
return ApiContextFactory.generateApiContext();
}
}

View File

@ -0,0 +1,30 @@
package com.iconplus.smartproc.helper.context;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ContextProvider implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CONTEXT = applicationContext;
}
/**
* Get a Spring bean by type.
**/
public static <T> T getBean(Class<T> beanClass) {
return CONTEXT.getBean(beanClass);
}
/**
* Get a Spring bean by name.
**/
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
}

View File

@ -0,0 +1,33 @@
package com.iconplus.smartproc.helper.context;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Log4j2
@Component
public class HttpHeadersInterceptor implements ClientHttpRequestInterceptor {
@Autowired
private ApiContext apiContext;
public HttpHeadersInterceptor(ApiContext apiContext) {
this.apiContext = apiContext;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
apiContext.getHttpHeaders().entrySet().stream()
.filter(x -> x.getKey() != null)
.forEach(x -> request.getHeaders().add(x.getKey(), x.getValue().get(0)));
return execution.execute(request, body);
}
}

View File

@ -23,5 +23,7 @@ public interface TokenManagementRepository extends JpaRepository<TokenManagement
"and tm.isDelete = false") "and tm.isDelete = false")
Optional<TokenManagement> findByRefreshToken(String refreshToken); Optional<TokenManagement> findByRefreshToken(String refreshToken);
Optional<TokenManagement> findByAccessTokenAndIsDeleteFalse(String accessToken);
Optional<TokenManagement> findByUserId(Long id); Optional<TokenManagement> findByUserId(Long id);
} }

View File

@ -0,0 +1,44 @@
package com.iconplus.smartproc.service.authentication;
import com.iconplus.smartproc.exception.BusinessException;
import com.iconplus.smartproc.helper.context.ApiContext;
import com.iconplus.smartproc.helper.model.EmptyRequest;
import com.iconplus.smartproc.helper.model.EmptyResponse;
import com.iconplus.smartproc.helper.service.BaseService;
import com.iconplus.smartproc.model.entity.TokenManagement;
import com.iconplus.smartproc.repository.TokenManagementRepository;
import com.iconplus.smartproc.repository.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class LogoutService implements BaseService<EmptyRequest, EmptyResponse> {
private ApiContext apiContext;
private TokenManagementRepository tokenManagementRepository;
private LogoutService(ApiContext apiContext,
TokenManagementRepository tokenManagementRepository) {
this.apiContext = apiContext;
this.tokenManagementRepository = tokenManagementRepository;
}
@Override
public EmptyResponse execute(EmptyRequest input) {
String accessToken = apiContext.getAuthorization();
TokenManagement tokenManagement = getTokenManagement(accessToken);
tokenManagement.setIsDelete(true);
tokenManagementRepository.save(tokenManagement);
return new EmptyResponse();
}
private TokenManagement getTokenManagement(String accessToken) {
var tokenManagement = tokenManagementRepository.findByAccessTokenAndIsDeleteFalse(accessToken);
if (tokenManagement.isEmpty()) {
throw new BusinessException("err", "err", "err");
}
return tokenManagement.get();
}
}