test master 변경
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
+45
@@ -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:/";
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
}
|
||||
+56
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user