test master 변경

This commit is contained in:
현성필
2024-01-23 11:24:09 +09:00
parent 75a695897d
commit 91e902af25
121 changed files with 329 additions and 338 deletions
@@ -0,0 +1,83 @@
package com.eactive.testmaster.common.entity;
import org.hibernate.annotations.Type;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Auditable {
/**
* 최초등록시점
*/
@Column(name = "REGISTERED_ON")
@Type(type = "org.hibernate.type.LocalDateTimeType")
@CreatedDate
private LocalDateTime createdAt;
/**
* 최초등록자ID
*/
@Column(name = "REGISTERED_BY")
@CreatedBy
private String createdBy;
/**
* 최종수정시점
*/
@Column(name = "LAST_MODIFIED_ON")
@Type(type = "org.hibernate.type.LocalDateTimeType")
@LastModifiedDate
private LocalDateTime updatedAt;
/**
* 최종수정자ID
*/
@Column(name = "LAST_MODIFIED_BY")
@LastModifiedBy
private String updatedBy;
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
}
@@ -0,0 +1,12 @@
package com.eactive.testmaster.common.entity;
public enum EnabledStatus {
Y("사용"),
N("미사용");
private String description;
EnabledStatus(String description) {
this.description = description;
}
}
@@ -0,0 +1,34 @@
package com.eactive.testmaster.common.exception;
import java.time.LocalDateTime;
public class CustomErrorResponse {
private LocalDateTime timestamp;
private String error;
private int status;
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
@@ -0,0 +1,45 @@
package com.eactive.testmaster.common.exception;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.time.LocalDateTime;
@Order(1)
@RestControllerAdvice(annotations = RestController.class)
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<CustomErrorResponse> customHandleNotFound(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<CustomErrorResponse> customHandleRuntime(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(UserNotLoginException.class)
public ResponseEntity<CustomErrorResponse> customHandleUserNotLogin(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.UNAUTHORIZED.value());
return new ResponseEntity<>(errors, HttpStatus.UNAUTHORIZED);
}
}
@@ -0,0 +1,7 @@
package com.eactive.testmaster.common.exception;
public class NotFoundException extends IllegalArgumentException {
public NotFoundException(String message) {
super(message);
}
}
@@ -0,0 +1,39 @@
package com.eactive.testmaster.common.exception;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
@Controller
public class PortalErrorController implements ErrorController {
@GetMapping("/error")
public String handleError(HttpServletRequest request, ModelMap model) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
model.addAttribute("status", status);
return "error";
}
return "redirect:/";
}
@PostMapping("/error")
public String handleErrorPost(HttpServletRequest request, ModelMap model) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
model.addAttribute("status", status);
return "error";
}
return "redirect:/";
}
}
@@ -0,0 +1,34 @@
package com.eactive.testmaster.common.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice(annotations = Controller.class)
@Order(2)
public class PortalGlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
@ExceptionHandler(value = NotFoundException.class)
public ModelAndView handleIllegalArgumentException(HttpServletRequest request, NotFoundException ex) {
log.error(ex.getMessage());
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("exception", ex);
return modelAndView;
}
@ExceptionHandler(value = UserNotLoginException.class)
public ModelAndView handleIllegalArgumentException(HttpServletRequest request, UserNotLoginException ex) {
return new ModelAndView("redirect:/login");
}
}
@@ -0,0 +1,8 @@
package com.eactive.testmaster.common.exception;
public class ServerNotFoundException extends RuntimeException {
public ServerNotFoundException(String message) {
super(message);
}
}
@@ -0,0 +1,7 @@
package com.eactive.testmaster.common.exception;
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
@@ -0,0 +1,8 @@
package com.eactive.testmaster.common.exception;
public class UserNotLoginException extends RuntimeException {
public UserNotLoginException() {
super("User not login");
}
}
@@ -0,0 +1,8 @@
package com.eactive.testmaster.common.exception;
public class UserPasswordResetException extends RuntimeException {
public UserPasswordResetException(String message) {
super(message);
}
}
@@ -0,0 +1,75 @@
package com.eactive.testmaster.common.interceptor;
import com.eactive.testmaster.common.exception.UserNotLoginException;
import com.eactive.testmaster.common.util.SecurityUtil;
import com.eactive.testmaster.user.entity.AnonymousUser;
import com.eactive.testmaster.user.entity.User;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 인증여부 체크 인터셉터
*
* @author
* @version 1.0
*/
public class AuthenticInterceptor implements AsyncHandlerInterceptor {
/**
* 관리자 접근 권한 패턴 목록
*/
private List<String> adminAuthPatternList;
private static AntPathMatcher pathMatcher = new AntPathMatcher();
public List<String> getAdminAuthPatternList() {
return adminAuthPatternList;
}
public void setAdminAuthPatternList(List<String> adminAuthPatternList) {
this.adminAuthPatternList = adminAuthPatternList;
}
/**
* 인증된 사용자 여부로 인증 여부를 체크한다.
* 관리자 권한에 따라 접근 페이지 권한을 체크한다.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//인증된사용자 여부
User currentUser = SecurityUtil.getCurrentUser();
if (currentUser == null || currentUser instanceof AnonymousUser) {
throw new UserNotLoginException();
}
boolean found = findPattern(request.getRequestURI());
if (found) {
if (currentUser.getUserSecurity() != null && "ROLE_ADMIN".equals(currentUser.getUserSecurity())) {
return true;
} else {
throw new UserNotLoginException();
}
}
return true;
}
boolean findPattern(String path) {
for (String pattern : adminAuthPatternList) {
if (pathMatcher.match(pattern, path)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,81 @@
package com.eactive.testmaster.common.mapper;
import com.eactive.testmaster.client.dto.ApiRequestKeyValueDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.mapstruct.ReportingPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Mapper(componentModel = "spring",
unmappedTargetPolicy = ReportingPolicy.IGNORE)
public class CommonMapper {
public String map(boolean value) {
return value ? "Y" : "N";
}
public boolean map(String value) {
if (value == null) return false;
return value.equals("Y");
}
public String map(List<String> values) {
if (values == null) return "";
return String.join(",", values);
}
public List<String> toList(String value) {
if (StringUtils.hasLength(value)) return new ArrayList<>();
return Arrays.asList(value.split(","));
}
@Named("authorities")
public List<String> authorities(Collection<? extends GrantedAuthority> grantedAuthorities) {
return grantedAuthorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
}
public String mapKeyValue(List<ApiRequestKeyValueDTO> list) {
if (list == null) return "";
StringBuilder result = new StringBuilder();
for (ApiRequestKeyValueDTO dto : list) {
result.append(dto.toString()).append("||");
}
if (result.length() > 0) {
result.setLength(result.length() - 2); // Remove the last "||"
}
return result.toString();
}
public List<ApiRequestKeyValueDTO> keyValueToList(String keyValue) {
List<ApiRequestKeyValueDTO> list = new ArrayList<>();
if (!StringUtils.hasLength(keyValue)) return list;
String[] pairs = keyValue.split("\\|\\|");
for (String pair : pairs) {
ApiRequestKeyValueDTO dto = new ApiRequestKeyValueDTO();
if (pair.startsWith("//")) {
dto.setEnabled(false);
pair = pair.substring(2);
} else {
dto.setEnabled(true);
}
String[] keyValueArray = pair.split("::");
if (keyValueArray.length == 2) {
dto.setKey(keyValueArray[0]);
dto.setValue(keyValueArray[1]);
list.add(dto);
}
}
return list;
}
}
@@ -0,0 +1,56 @@
package com.eactive.testmaster.common.message;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PortalReloadableResourceBundleMessageSource extends org.springframework.context.support.ReloadableResourceBundleMessageSource {
private static final String CLASSPATH_PREFIX = "classpath:";
private static final String PROPERTIES_EXTENSION = ".properties";
private final ResourcePatternResolver resourcePatternResolver;
public PortalReloadableResourceBundleMessageSource() {
this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
}
public void setEgovBaseNames(String... basenames) {
List<String> resolvedBasenames = new ArrayList<>();
for (String basename : basenames) {
String trimmedBasename = StringUtils.trimToEmpty(basename);
if (trimmedBasename.startsWith(CLASSPATH_PREFIX)) {
resolvedBasenames.add(trimmedBasename);
} else if (StringUtils.isNotBlank(trimmedBasename)) {
try {
Resource[] resources = resourcePatternResolver.getResources(trimmedBasename);
for (Resource resource : resources) {
String uri = resource.getURI().toString();
if (!uri.contains(PROPERTIES_EXTENSION)) {
continue;
}
String baseName = processBasename(uri);
resolvedBasenames.add(baseName);
}
} catch (IOException e) {
logger.debug("No message source files found for basename " + trimmedBasename + ".");
}
}
}
setBasenames(resolvedBasenames.toArray(new String[0]));
}
private String processBasename(String uri) {
String baseName = StringUtils.substringBeforeLast(uri, PROPERTIES_EXTENSION);
String prefix = StringUtils.substringBeforeLast(baseName, "/");
String name = StringUtils.substringAfterLast(baseName, "/");
while (name.contains("_")) {
name = StringUtils.substringBeforeLast(name, "_");
}
return prefix + "/" + name;
}
}
@@ -0,0 +1,90 @@
package com.eactive.testmaster.common.search;
import org.springframework.data.jpa.domain.Specification;
import java.util.List;
/**
* Overview:
*
* BaseSearch 클래스는 Spring Data JPA 프레임워크를 사용하여 복잡한 검색 쿼리를 편리하게 작성할 수 있는 방법을 제공하는 추상 클래스입니다.
* 이 클래스를 사용하면 SearchModel 객체 목록을 사용하여 검색 쿼리를 생성할 수 있으며, 이를 결합하여 보다 복잡하고 구체적인 검색 조건을 만들 수 있습니다.
*
* How to Use:
*
* BaseSearch 클래스를 사용하려면 검색하려는 엔티티에 특정한 BaseSearch의 하위 클래스를 만들어야 합니다.
* 예를 들어 User 개체를 검색하려는 경우 BaseSearch<User>를 확장하는 UserSearch라는 클래스를 만들 수 있습니다.
*
* 하위 클래스에서 buildSearchCondition() 메서드를 구현해야 합니다.
* 이 메서드는 사용하려는 검색 조건을 나타내는 SearchModel 객체 목록을 반환해야 합니다.
*
* 예를 들어, 다음은 이름에 특정 문자열이 포함된 사용자를 검색하기 위해 buildSearchCondition() 메서드를 구현하는 방법입니다:
*
* public List<SearchModel> buildSearchCondition() {
* List<SearchModel> models = new ArrayList<>();
* models.add(new ColumnSearchModel("firstName", SearchCondition.LIKE, "John"));
* models.add(new ColumnSearchModel("lastName", SearchCondition.LIKE, "Doe"));
* return models;
* }
*
* 이 예제에서는 이름과 성에 각각 문자열 "John"과 "Doe"가 포함된 사용자를 검색하는 두 개의 ColumnSearchModel 객체를 만들고 있습니다.
*
* 검색 쿼리를 빌드하려면 UserSearch 클래스의 인스턴스를 생성하고 그 인스턴스에서 buildSpecification() 메서드를 호출하면 됩니다.
* 이 메서드는 Spring 데이터 JPA를 사용하여 데이터베이스를 쿼리하는 데 사용할 수 있는 Specification 객체를 반환합니다.
*
* UserSearch search = new UserSearch();
* Specification<User> spec = search.buildSpecification();
* List<User> users = userRepository.findAll(spec);
*
* 이 예제에서는 UserSearch 인스턴스를 만들고 buildSpecification() 메서드를 호출하여 사양 객체를 빌드한 다음 이 사양 객체를 사용하여 데이터베이스에서 검색 조건과 일치하는 모든 사용자 객체를 쿼리합니다.
* 결과는 User 객체 목록으로 반환됩니다.
*
* 추가 활용 방법
*
* BaseSearch 클래스는 유연하고 확장 가능하도록 설계되어 필요에 따라 더 복잡한 검색 쿼리를 사용할 수 있습니다.
* getSpecification() 메서드를 구현하는 SearchModel의 새로운 서브클래스를 생성하여 검색 조건을 추가할 수 있습니다.
* 필요에 따라 추가 검색 조건을 포함하도록 SpecificationUtil 클래스를 수정할 수도 있습니다.
*
* 또한 BaseSearch 클래스를 확장하여 정렬 및 페이징과 같은 추가 기능을 제공할 수 있습니다.
* 하위 클래스에 메서드와 속성을 추가하여 애플리케이션의 특정 요구 사항을 충족하는 검색 쿼리를 만들 수 있습니다.
*
* ColumnSearchModel은 검색조건이 컬럼명과 비교 연산자, 값으로 구성된다. SearchCondition 참조
* SpecificationSearchModel은 검색조건이 Specification을 리턴하는 형태로 구성된다. 검색조건을 복잡하게 구성해야 할 때 사용한다.
*
* public static Specification current(String user, ApproverStatus approverStatus) {
* return (root, criteriaQuery, criteriaBuilder) -> {
* ListJoin join = root.joinList("approvers");
* return criteriaBuilder.and(
* criteriaBuilder.equal(join.get("user"), user),
* criteriaBuilder.equal(join.get("status"), approverStatus));
* };
* }
*/
public interface BaseSearch<T> {
List<SearchModel> buildSearchCondition();
default Specification<T> buildSpecification() {
List<SearchModel> models = buildSearchCondition();
Specification<T> specifications = null;
for (SearchModel model : models) {
Specification spec = getSpecification(model);
if (spec != null) {
if (specifications == null) {
specifications = Specification.where(spec);
} else {
specifications = specifications.and(spec);
}
}
}
return specifications;
}
default Specification<T> getSpecification(SearchModel model) {
return model.getSpecification();
}
}
@@ -0,0 +1,65 @@
package com.eactive.testmaster.common.search;
import com.eactive.testmaster.common.util.SpecificationUtil;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ObjectUtils;
import java.util.Collection;
public class ColumnSearchModel implements SearchModel {
private String column;
private Object value; //값의 형태는 String, Collection, Object 등이 될 수 있다.
private SearchCondition condition;
public ColumnSearchModel(String column, SearchCondition condition, Object value) {
this.column = column;
this.condition = condition;
this.value = value;
}
public String getColumn() {
return column;
}
public void setColumn(String column) {
this.column = column;
}
public SearchCondition getCondition() {
return condition;
}
public void setCondition(SearchCondition condition) {
this.condition = condition;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public Specification getSpecification() {
if (condition == null) return null;
if (column == null) return null;
if (ObjectUtils.isEmpty(value)) return null;
if (condition.equals(SearchCondition.EQUAL)) {
return SpecificationUtil.equalTo(column, value);
} else if (condition.equals(SearchCondition.LIKE)) {
return SpecificationUtil.like(column, value.toString());
} else if (condition.equals(SearchCondition.IN)) {
return SpecificationUtil.in(column, (Collection) value);
} else if (condition.equals(SearchCondition.NOT_EQUAL)) {
return SpecificationUtil.notEqualTo(column, value);
}
return null;
}
}
@@ -0,0 +1,8 @@
package com.eactive.testmaster.common.search;
public enum SearchCondition {
EQUAL,
LIKE,
IN,
NOT_EQUAL
}
@@ -0,0 +1,10 @@
package com.eactive.testmaster.common.search;
import org.springframework.data.jpa.domain.Specification;
import java.io.Serializable;
public interface SearchModel extends Serializable {
Specification getSpecification();
}
@@ -0,0 +1,17 @@
package com.eactive.testmaster.common.search;
import org.springframework.data.jpa.domain.Specification;
public class SpecificationSearchModel implements SearchModel {
private final Specification specification;
public SpecificationSearchModel(Specification specification) {
this.specification = specification;
}
public Specification getSpecification() {
return specification;
}
}
@@ -0,0 +1,36 @@
package com.eactive.testmaster.common.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* ApplicationContextUtil 클래스
*
* @author
* @version 1.0
*/
@Component("ApplicationContextUtil")
public class ApplicationContextUtil implements ApplicationContextAware {
public static ApplicationContext getContext() {
return context;
}
public static void setContext(ApplicationContext context) {
ApplicationContextUtil.context = context;
}
private static ApplicationContext context;
@SuppressWarnings("static-access")
public void setApplicationContext(ApplicationContext context)
throws BeansException {
this.context = context;
}
}
@@ -0,0 +1,62 @@
package com.eactive.testmaster.common.util;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author
* @version 1.0
* @Class Name : EncryptionUtil.java
* @Description : 문자열 암호화 처리하는 유틸리티 클래스
* @since 2009.08.26
*/
@Component
public class EncryptionUtil {
private static final String SHA_256 = "SHA-256";
MessageDigest md;
public EncryptionUtil() throws NoSuchAlgorithmException {
md = MessageDigest.getInstance(SHA_256);
}
public String generateNewPassword() {
// 2. 임시 비밀번호를 생성한다.(영+영+숫+영+영+숫+영+영=8자리)
StringBuilder newpassword = new StringBuilder();
for (int i = 1; i <= 8; i++) {
// 영자
if (i % 3 != 0) {
newpassword.append(RandomUtil.getRandomStr('a', 'z'));
// 숫자
} else {
newpassword.append(RandomUtil.getRandomNum(0, 9));
}
}
return newpassword.toString();
}
/**
* 비밀번호를 암호화하는 기능(복호화가 되면 안되므로 SHA-256 인코딩 방식 적용)
*
* @param password 암호화될 패스워드
* @param id salt로 사용될 사용자 ID 지정
* @return 암호화된 패스워드
*/
public synchronized String encryptPassword(String password, String id) {
if (StringUtils.isEmpty(password)) return "";
if (StringUtils.isEmpty(id)) return "";
byte[] hashValue = null; // 해쉬값
md.reset();
md.update(id.getBytes());
hashValue = md.digest(password.getBytes());
return new String(Base64.encodeBase64(hashValue));
}
}
@@ -0,0 +1,58 @@
package com.eactive.testmaster.common.util;
import java.security.SecureRandom;
public class RandomUtil {
private RandomUtil() {
throw new IllegalStateException("Utility class");
}
public static int getRandomNum(int startNum, int endNum) {
int randomNum = 0;
// 랜덤 객체 생성
SecureRandom rnd = new SecureRandom();
do {
// 종료숫자내에서 랜덤 숫자를 발생시킨다.
randomNum = rnd.nextInt(endNum + 1);
} while (randomNum < startNum); // 랜덤 숫자가 시작숫자보다 작을경우 다시 랜덤숫자를 발생시킨다.
return randomNum;
}
/**
* 문자열 A에서 Z사이의 랜덤 문자열을 구하는 기능을 제공 시작문자열과 종료문자열 사이의 랜덤 문자열을 구하는 기능
*
* @param startChr - 첫 문자
* @param endChr - 마지막문자
* @return 랜덤문자
* @see
*/
public static String getRandomStr(char startChr, char endChr) {
int randomInt;
String randomStr = null;
// 시작문자열이 종료문자열보가 클경우
if (startChr > endChr) {
throw new IllegalArgumentException("Start String: " + startChr + " End String: " + endChr);
}
// 랜덤 객체 생성
SecureRandom rnd = new SecureRandom();
do {
// 시작문자 및 종료문자 중에서 랜덤 숫자를 발생시킨다.
randomInt = rnd.nextInt(endChr + 1);
} while (randomInt < startChr); // 입력받은 문자 'A'(65)보다 작으면 다시 랜덤 숫자 발생.
// 랜덤 숫자를 문자로 변환 후 스트링으로 다시 변환
randomStr = (char) randomInt + "";
// 랜덤문자열를 리턴
return randomStr;
}
}
@@ -0,0 +1,33 @@
package com.eactive.testmaster.common.util;
import com.eactive.testmaster.user.entity.AnonymousUser;
import com.eactive.testmaster.user.entity.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityUtil {
private SecurityUtil() {
// Utility class
throw new IllegalStateException("Utility class");
}
public static final String ANONYMOUS_USER = "anonymousUser";
public static User getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !authentication.getPrincipal().equals(ANONYMOUS_USER)) {
return ((User) authentication.getDetails());
}
return new AnonymousUser();
}
public static String getCurrentUserEsntlId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !authentication.getPrincipal().equals(ANONYMOUS_USER)) {
return ((User) authentication.getDetails()).getEsntlId();
}
return "ANONYMOUS";
}
}
@@ -0,0 +1,101 @@
package com.eactive.testmaster.common.util;
import org.hibernate.query.criteria.internal.path.PluralAttributePath;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;
import java.util.Collection;
public class SpecificationUtil {
private SpecificationUtil() {
throw new IllegalStateException("Utility class");
}
public static <T> Path getPath(Root<T> root, String column) {
Path p = root;
if (column.contains(".")) {
String[] columns = column.split("\\.");
for (String s : columns) {
Path<Object> c = p.get(s);
if (c instanceof PluralAttributePath) {
p = root.joinList(s);
} else {
p = p.get(s);
}
}
return p;
} else {
return root.get(column);
}
}
public static <T> Specification<T> notNull(String column) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.isNotNull(root.get(column));
}
public static <T> Specification<T> isNull(String column) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.isNull(root.get(column));
}
public static <T> Specification<T> equalTo(String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(getPath(root, column), value);
}
public static <T> Specification<T> equalToJoin(String table, String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> {
Join join = root.join(table, JoinType.INNER);
return criteriaBuilder.equal(join.get(column), value);
};
}
public static <T> Specification<T> equalToLeftJoin(String table, String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> {
Join join = root.join(table, JoinType.LEFT);
return criteriaBuilder.equal(join.get(column), value);
};
}
public static <T> Specification<T> notEqualTo(String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.notEqual(getPath(root, column), value);
}
public static <T> Specification<T> like(String column, String value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.like(criteriaBuilder.lower(getPath(root, column)), "%" + value.toLowerCase() + "%");
}
public static <T> Specification<T> in(String column, Collection<Object> value) {
return (root, criteriaQuery, criteriaBuilder) -> {
CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get(column));
value.forEach(in::value);
return in;
};
}
public static <T> Specification<T> greater(String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.greaterThan(getPath(root, column), (Comparable) value);
}
public static <T> Specification<T> greaterAndEqual(String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(getPath(root, column), (Comparable) value);
}
public static <T> Specification<T> less(String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.lessThan(getPath(root, column), (Comparable) value);
}
public static <T> Specification<T> lessAndEqual(String column, Object value) {
return (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.lessThanOrEqualTo(getPath(root, column), (Comparable) value);
}
}
@@ -0,0 +1,21 @@
package com.eactive.testmaster.common.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Constraint(validatedBy = PasswordMatchValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PasswordMatch {
String message() default "{password.not.match}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String input();
String confirm();
}
@@ -0,0 +1,51 @@
package com.eactive.testmaster.common.validator;
import com.eactive.testmaster.common.util.ApplicationContextUtil;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Objects;
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, Object> {
private String field1;
private String field2;
@Override
public void initialize(PasswordMatch constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
this.field1 = constraintAnnotation.input();
this.field2 = constraintAnnotation.confirm();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
Object passwordValue = null;
Object password2Value = null;
try {
passwordValue = PropertyUtils.getProperty(value, field1);
password2Value = PropertyUtils.getProperty(value, field2);
} catch (Exception e) {
return false;
}
if (Objects.isNull(passwordValue) || Objects.isNull(password2Value)) {
return false;
}
MessageSource messageSource = (MessageSource) ApplicationContextUtil.getContext().getBean("messageSource");
String message = messageSource.getMessage("password.not.match", null, LocaleContextHolder.getLocale());
if (!passwordValue.equals(password2Value)) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(field2)
.addConstraintViolation();
return false;
}
return true;
}
}
@@ -0,0 +1,17 @@
package com.eactive.testmaster.common.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Constraint(validatedBy = UniqueUserIdValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UniqueId {
String message() default "아이디가 사용중입니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,22 @@
package com.eactive.testmaster.common.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Constraint(validatedBy = UniqueRouteValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UniqueRoute {
String message() default "등록된 API가 존재합니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String path();
String method();
}
@@ -0,0 +1,49 @@
package com.eactive.testmaster.common.validator;
import com.eactive.testmaster.api.repository.MockRouteRepository;
import com.eactive.testmaster.common.util.ApplicationContextUtil;
import org.apache.commons.beanutils.PropertyUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class UniqueRouteValidator implements ConstraintValidator<UniqueRoute, Object> {
private String method;
private String path;
@Override
public void initialize(UniqueRoute constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
this.method = constraintAnnotation.method();
this.path = constraintAnnotation.path();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
String methodValue;
String pathValue;
try {
methodValue = (String) PropertyUtils.getProperty(value, this.method);
pathValue = (String) PropertyUtils.getProperty(value, this.path);
} catch (Exception e) {
return false;
}
MockRouteRepository mockRouteRepository = ApplicationContextUtil.getContext().getBean(MockRouteRepository.class);
long found = mockRouteRepository.countByMethodAndPathPattern(methodValue, pathValue);
String message = "등록된 API가 존재합니다.";
if (found > 0L){
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(this.path)
.addConstraintViolation();
return false;
}
return true;
}
}
@@ -0,0 +1,22 @@
package com.eactive.testmaster.common.validator;
import com.eactive.testmaster.common.exception.UserNotFoundException;
import com.eactive.testmaster.common.util.ApplicationContextUtil;
import com.eactive.testmaster.user.service.UserService;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class UniqueUserIdValidator implements ConstraintValidator<UniqueId, String> {
@Override
public boolean isValid(String id, ConstraintValidatorContext constraintValidatorContext) {
UserService userService = ApplicationContextUtil.getContext().getBean(UserService.class);
try {
userService.findByUserId(id);
return false;
} catch (UserNotFoundException e) {
return true;
}
}
}