초기소스
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AnonymousUser{
|
||||
|
||||
private String id;
|
||||
private String mbrId;
|
||||
private String sopmalCd;
|
||||
private String mbrDvCd;
|
||||
private int userInnb = 0;
|
||||
private String mbrNm;
|
||||
private String mbtlnb;
|
||||
private String accessUrl;
|
||||
private String clientId;
|
||||
private String originalHost;
|
||||
private String sysDvCd;
|
||||
private String mngrDv;
|
||||
|
||||
// 인트라넷
|
||||
private String tentGroup; // 입점 그룹
|
||||
private String reprsntBrand; // 대표브랜드
|
||||
private String strOperDv; // 대표브랜드
|
||||
private String tentcpnNm; // 입점사명
|
||||
private String itrntDv; //인트라넷 구분
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CustomAccessDeniedEntryPoint implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException) throws IOException {
|
||||
|
||||
// 인증된 사용자가 접근했을 때 루트 페이지로 리다이렉트
|
||||
|
||||
response.sendRedirect("/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
import com.nimbusds.jose.util.StandardCharset;
|
||||
import com.wb.domainserver.realm.RealmContextHolder;
|
||||
import com.wb.domainserver.realm.util.UrlExtractor;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException{
|
||||
|
||||
|
||||
log.debug("authException.getMessage() {}", authException.getMessage());
|
||||
log.debug("request.getRequestURI() {}", request.getRequestURI());
|
||||
log.debug("getAuthentication() {}", SecurityContextHolder.getContext().getAuthentication());
|
||||
|
||||
String realm = RealmContextHolder.getRealm().toLowerCase();
|
||||
|
||||
//엑세스토큰 만료시에 refresh 토큰 검증 요청
|
||||
if(!request.getRequestURI().contains("/tokens/refresh")){
|
||||
|
||||
response.sendRedirect("/"+realm+"/tokens/refresh?redirect_uri=%s".formatted(URLEncoder.encode(UrlExtractor.getOriginalURL(request), StandardCharset.UTF_8)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
//@Component
|
||||
//public class MemberJwtAuthenticationConverter
|
||||
// extends SimpleAuthenticationConverter
|
||||
//{
|
||||
|
||||
// @Override
|
||||
// public AbstractAuthenticationToken convert(Jwt jwt, Collection<GrantedAuthority> authorities) {
|
||||
// return new MemberJwtToken(jwt, authorities);
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
import com.wb.domainserver.realm.RealmContextHolder;
|
||||
import com.wb.domainserver.security.user.CustomAuthenticationToken;
|
||||
import com.wb.domainserver.security.user.SecurityUser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
@Slf4j
|
||||
public class MemberJwtContextHolder {
|
||||
|
||||
|
||||
public static AnonymousUser getMemberInfo() {
|
||||
|
||||
AnonymousUser vo = new AnonymousUser();
|
||||
var authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication instanceof CustomAuthenticationToken customAuthenticationToken) {
|
||||
|
||||
SecurityUser users = (SecurityUser) customAuthenticationToken.getPrincipal();
|
||||
|
||||
vo.setId(users.getId());
|
||||
vo.setMbrId(users.getMbrId());
|
||||
vo.setSopmalCd(users.getSopmalCd());
|
||||
vo.setMbrDvCd(users.getMbrDvCd());
|
||||
vo.setUserInnb((int) users.getUserInnb());
|
||||
vo.setMbrNm(users.getUsername());
|
||||
vo.setMbtlnb(users.getMbtlnb());
|
||||
vo.setAccessUrl(users.getAccessUrl());
|
||||
vo.setClientId(users.getClientId());
|
||||
vo.setOriginalHost(RealmContextHolder.getOriginalHostRealm());
|
||||
vo.setSysDvCd(RealmContextHolder.getSysDvCdRealm());
|
||||
vo.setMngrDv(users.getMngrDv());
|
||||
|
||||
// 인트라넷
|
||||
vo.setTentGroup(users.getTentGroup());
|
||||
vo.setReprsntBrand(users.getReprsntBrand());
|
||||
vo.setStrOperDv(users.getStrOperDv());
|
||||
vo.setTentcpnNm(users.getTentcpnNm());
|
||||
vo.setItrntDv(users.getItrntDv());
|
||||
|
||||
|
||||
}else {
|
||||
|
||||
vo.setSopmalCd(RealmContextHolder.getSopmalCdRealm());
|
||||
vo.setOriginalHost(RealmContextHolder.getOriginalHostRealm());
|
||||
vo.setSysDvCd(RealmContextHolder.getSysDvCdRealm());
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
public class MemberJwtToken {
|
||||
private final String id; //f:300c3290-6f54-418d-b35e-3ad39b674937:2041:02:it.gcone.co.kr
|
||||
private final String mbrId;
|
||||
private final String sopmalCd;
|
||||
private final String mbrDvCd;
|
||||
private final int userInnb;
|
||||
private final String mbrNm;
|
||||
private final String mbtlnb;
|
||||
private final String accessUrl; //it.gcone.co.kr
|
||||
private final String clientId; //account
|
||||
private final String sysDvCd;
|
||||
private final String mngrDv;
|
||||
private final List<String> roleType;
|
||||
|
||||
//인트라넷 정보
|
||||
private final String tentGroup;
|
||||
private final String reprsntBrand;
|
||||
private final String strOperDv;
|
||||
private final String tentcpnNm;
|
||||
private final String itrntDv;
|
||||
|
||||
public MemberJwtToken(Claims jwt) {
|
||||
|
||||
this.id = jwt.get("id", String.class);
|
||||
this.mbrId = jwt.get("mbrId", String.class);
|
||||
this.sopmalCd = jwt.get("sopmalCd", String.class);
|
||||
this.mbrDvCd = jwt.get("mbrDvCd", String.class);
|
||||
this.userInnb = Integer.parseInt(jwt.get("userInnb", String.class));
|
||||
this.mbrNm = jwt.get("mbrNm", String.class);
|
||||
this.mbtlnb = jwt.get("mbtlnb", String.class);
|
||||
this.accessUrl = jwt.get("accessUrl", String.class);
|
||||
this.clientId = jwt.get("clientId", String.class);
|
||||
this.sysDvCd = jwt.get("sysDvCd", String.class);
|
||||
this.roleType = jwt.get("roleType", List.class);
|
||||
this.mngrDv = jwt.get("mngrDv", String.class);
|
||||
|
||||
// 인트라넷 정보
|
||||
this.tentGroup = jwt.get("tentGroup", String.class);
|
||||
this.reprsntBrand = jwt.get("reprsntBrand", String.class);
|
||||
this.strOperDv = jwt.get("strOperDv", String.class);
|
||||
this.tentcpnNm = jwt.get("tentcpnNm", String.class);
|
||||
this.itrntDv = jwt.get("itrntDv", String.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.wb.domainserver.security.authentication;
|
||||
|
||||
//import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
|
||||
|
||||
//@Slf4j
|
||||
//public abstract class SimpleAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
||||
// private final JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
|
||||
//
|
||||
// @Override
|
||||
// public AbstractAuthenticationToken convert(@NonNull Jwt jwt) {
|
||||
// AbstractAuthenticationToken token = jwtAuthenticationConverter.convert(jwt);
|
||||
// Collection<GrantedAuthority> authorities = token.getAuthorities();
|
||||
//
|
||||
// return convert(jwt, authorities);
|
||||
// }
|
||||
//
|
||||
// public abstract AbstractAuthenticationToken convert(Jwt jwt, Collection<GrantedAuthority> authorities);
|
||||
//}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.wb.domainserver.security.authorization;
|
||||
|
||||
import com.wb.domainserver.security.authentication.AnonymousUser;
|
||||
import com.wb.domainserver.security.authentication.MemberJwtContextHolder;
|
||||
import com.wb.domainserver.security.authorization.annotation.AuthPolicy;
|
||||
import com.wb.domainserver.security.authorization.annotation.Policy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class WnbtAuthorizationManager<T> implements AuthorizationManager<MethodInvocation> {
|
||||
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation object) {
|
||||
boolean grantedFlag = true;
|
||||
if (hasAuthentication(authentication.get())) {
|
||||
Policy policyAnnotation = AnnotationUtils.findAnnotation(object.getMethod(), Policy.class);
|
||||
grantedFlag = Optional.ofNullable(policyAnnotation)
|
||||
.map(Policy::value).filter(
|
||||
policy -> (policy == AuthPolicy.PUBLIC) || (policy == AuthPolicy.RESTRICTED && hasAccessToRestrictedPolicy(MemberJwtContextHolder.getMemberInfo(), object))
|
||||
).isPresent();
|
||||
}
|
||||
|
||||
log.debug("# [WnbtAuthorizationManager] AuthorizationDecision check ::: {}", grantedFlag);
|
||||
|
||||
return new AuthorizationDecision(grantedFlag);
|
||||
}
|
||||
|
||||
private boolean hasAuthentication(Authentication authentication) {
|
||||
return authentication != null && isNotAnonymous(authentication) && authentication.isAuthenticated();
|
||||
}
|
||||
|
||||
private boolean isNotAnonymous(Authentication authentication) {
|
||||
return !this.trustResolver.isAnonymous(authentication);
|
||||
}
|
||||
|
||||
private boolean hasAccessToRestrictedPolicy(AnonymousUser tokenClaim, MethodInvocation object){
|
||||
RequestMapping classRequestMapping = object.getMethod().getDeclaringClass().getAnnotation(RequestMapping.class);
|
||||
var classPath = Optional.ofNullable(classRequestMapping).map(RequestMapping::value).orElseGet(()->new String[0]);
|
||||
|
||||
GetMapping getMapping = object.getMethod().getAnnotation(GetMapping.class);
|
||||
PostMapping postMapping = object.getMethod().getAnnotation(PostMapping.class);
|
||||
RequestMapping requestMapping = object.getMethod().getAnnotation(RequestMapping.class);
|
||||
|
||||
var path = Optional.ofNullable(getMapping).map(GetMapping::value).orElse(
|
||||
Optional.ofNullable(postMapping).map(PostMapping::value).orElse(
|
||||
Optional.ofNullable(requestMapping).map(RequestMapping::value).orElseGet(()->new String[0])));
|
||||
|
||||
log.debug("###################################################################################");
|
||||
log.debug("# [hasAccessToRestrictedPolicy] user's Authority ::: {} ", tokenClaim.getUserInnb());
|
||||
log.debug("# [hasAccessToRestrictedPolicy] user's Authority ::: {} ", tokenClaim.getMbrDvCd());
|
||||
log.debug("# [hasAccessToRestrictedPolicy] user's Authority ::: {} ", tokenClaim.getMbrId());
|
||||
log.debug("# [hasAccessToRestrictedPolicy] user's Authority ::: {} ", tokenClaim.getMbrNm());
|
||||
log.debug("# [hasAccessToRestrictedPolicy] requestMapping path ::: {} ", (Object) path);
|
||||
log.debug("# [hasAccessToRestrictedPolicy] classpath ::: {} ", (Object) classPath);
|
||||
log.debug("###################################################################################");
|
||||
|
||||
// List<String> pathList = securityUserDetailService.getAvailablePathList(user);
|
||||
// log.debug("##########pathList######## = {}",pathList);
|
||||
|
||||
// if(!pathList.isEmpty()) {
|
||||
// for (Object pa : pathList) {
|
||||
// String test = "/mng/goods/realgoods/goodsList34.do";
|
||||
// // test -> path 로 변경
|
||||
// if(pa.equals(test)){
|
||||
// log.debug("test 통과@#@@#!#@!#@!#!@##@!");
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.wb.domainserver.security.authorization.annotation;
|
||||
|
||||
public enum AuthPolicy {
|
||||
RESTRICTED, PUBLIC
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.wb.domainserver.security.authorization.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
|
||||
@Target(METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Policy {
|
||||
AuthPolicy value();
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
|
||||
import com.nhncorp.psinfra.toolkit.SimpleCryptLib;
|
||||
import com.wb.domainserver.interceptor.mngr.MngrAuthInterceptor;
|
||||
import com.wb.domainserver.interceptor.mngr.MngrLoggingInterceptor;
|
||||
import com.wb.domainserver.interceptor.mngr.MngrMenuInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@ComponentScan(basePackages = {
|
||||
"com.wb.domainserver.domain.mngr"
|
||||
, "com.wb.domainserver.domain.admin"
|
||||
, "com.wb.domainserver.interceptor.mngr"
|
||||
, "com.wb.domainserver.util.file"
|
||||
// , "com.wb.domainserver.domain.mq.controller" /*ERP batch Controller 위치*/
|
||||
// , "com.wb.domainserver.batch.controller" /*batch Controller*/
|
||||
})
|
||||
public class AdminWebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
private static final String[] ADMIN_INTERCEPTOR_PATH = {"/mngr/**", "/admin/**","/mngr","/admin"};
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
if(!registry.hasMappingForPattern("/webjars/**")){
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("/webjars/").resourceChain(false);
|
||||
}
|
||||
if(!registry.hasMappingForPattern("/**")){
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations(
|
||||
"classpath:/static",
|
||||
"classpath:/static/admin",
|
||||
"classpath:/static/assets",
|
||||
"classpath:/static/ckeditor",
|
||||
"classpath:/static/crm",
|
||||
"classpath:/static/daumeditor",
|
||||
"classpath:/static/intranet",
|
||||
"classpath:/static/itrnt",
|
||||
"classpath:/static/jchartfx",
|
||||
"classpath:/static/js",
|
||||
"classpath:/static/login",
|
||||
"classpath:/static/mngr",
|
||||
"classpath:/static/css"
|
||||
).resourceChain(false);
|
||||
}
|
||||
if(!registry.hasMappingForPattern("/**.html")){
|
||||
registry.addResourceHandler("/**.html")
|
||||
.addResourceLocations(
|
||||
"classpath:/templates/"
|
||||
).resourceChain(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public SimpleCryptLib simpleCryptLib() {
|
||||
return new SimpleCryptLib();
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 처리 bean 선언
|
||||
*/
|
||||
@Bean
|
||||
public MngrMenuInterceptor mngrMenuInterceptor() {
|
||||
return new MngrMenuInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* logging 처리 bean 선언
|
||||
*/
|
||||
@Bean
|
||||
public MngrLoggingInterceptor mngrLoggingInterceptor() {
|
||||
return new MngrLoggingInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* auth 처리 bean 선언
|
||||
*/
|
||||
@Bean
|
||||
public MngrAuthInterceptor mngrAuthInterceptor() {
|
||||
return new MngrAuthInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
MngrLoggingInterceptor loggingInterceptor = mngrLoggingInterceptor();
|
||||
registry.addInterceptor(loggingInterceptor).addPathPatterns(ADMIN_INTERCEPTOR_PATH)
|
||||
.order(1)
|
||||
.excludePathPatterns("/itrnt/mbr/selectMbrJoinForm")
|
||||
.excludePathPatterns("/itrnt/ntcn/mssage/insertPosMbrSend")
|
||||
.excludePathPatterns("/order/tm_settle")
|
||||
.excludePathPatterns("/order/insertTmOrder")
|
||||
.excludePathPatterns("/order/selectTmOrdrInfo")
|
||||
.excludePathPatterns("/crm/ordr/insertTossKeyInSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertKcpSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertKcpKeyInSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertTossSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertSetleTm")
|
||||
.excludePathPatterns("/comm/itrnt/include", "/WEB-INF/views/**", "/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif", "/favicon.ico");
|
||||
MngrAuthInterceptor authInterceptor = mngrAuthInterceptor();
|
||||
registry.addInterceptor(authInterceptor).addPathPatterns(ADMIN_INTERCEPTOR_PATH)
|
||||
.order(2)
|
||||
.excludePathPatterns("/itrnt/mbr/selectMbrJoinForm")
|
||||
.excludePathPatterns("/itrnt/ntcn/mssage/insertPosMbrSend")
|
||||
.excludePathPatterns("/order/tm_settle")
|
||||
.excludePathPatterns("/order/insertTmOrder")
|
||||
.excludePathPatterns("/order/selectTmOrdrInfo")
|
||||
.excludePathPatterns("/crm/ordr/insertTossKeyInSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertKcpSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertKcpKeyInSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertTossSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertSetleTm")
|
||||
.excludePathPatterns("/mngr", "/mngr/login", "/mngr/tokens/refresh", "/mngr/login/page/mngr","/mngr/login/loginCheck","/mngr/login/mngr","/mngr/login/insertPhoneAuth" , "/mngr/login/selectPhoneAuth", "/mngr/login/loginCheck")
|
||||
.excludePathPatterns("/admin", "/admin/login", "/admin/tokens/refresh", "/admin/login/page/admin","/admin/login/loginCheck","/admin/login/admin","/admin/login/insertPhoneAuth" , "/admin/login/selectPhoneAuth", "/admin/login/loginCheck")
|
||||
.excludePathPatterns("/comm/itrnt/include", "/WEB-INF/views/**", "/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif", "/favicon.ico", "/mngr/vendor/chart.js/chart.umd.js.map", "/ckeditor/ckeditor.js.map");
|
||||
MngrMenuInterceptor menuInterceptor = mngrMenuInterceptor();
|
||||
registry.addInterceptor(menuInterceptor).addPathPatterns(ADMIN_INTERCEPTOR_PATH)
|
||||
.order(3)
|
||||
.excludePathPatterns("/itrnt/mbr/selectMbrJoinForm")
|
||||
.excludePathPatterns("/itrnt/ntcn/mssage/insertPosMbrSend")
|
||||
.excludePathPatterns("/order/tm_settle")
|
||||
.excludePathPatterns("/order/insertTmOrder")
|
||||
.excludePathPatterns("/order/selectTmOrdrInfo")
|
||||
.excludePathPatterns("/crm/ordr/insertTossKeyInSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertKcpSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertKcpKeyInSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertTossSetleTm")
|
||||
.excludePathPatterns("/crm/ordr/insertSetleTm")
|
||||
.excludePathPatterns("/comm/itrnt/include", "/WEB-INF/views/**", "/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif", "/favicon.ico");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.wb.domainserver.realm.RealmResolver;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class CacheConfig {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public CacheConfig(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CaffeineCacheManager cacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||
cacheManager.setCaffeine(caffeineCacheBuilder());
|
||||
cacheManager.setCacheLoader(cacheLoader());
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Caffeine<Object, Object> caffeineCacheBuilder() {
|
||||
return Caffeine.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.maximumSize(100);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheLoader<Object, Object> cacheLoader() {
|
||||
return new CacheLoader<>() {
|
||||
@Override
|
||||
public Object load(Object key) {
|
||||
return loadAsync(key).join();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> asyncReload(Object key, Object oldValue, Executor executor) {
|
||||
return loadAsync(key);
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> loadAsync(Object key) {
|
||||
String hostUrl = (String) key;
|
||||
RealmResolver realmResolver = applicationContext.getBean(RealmResolver.class);
|
||||
return realmResolver.resolveRealmInternalDirect(hostUrl);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import com.wb.domainserver.common.msg.GconeMessageSource;
|
||||
import com.wb.domainserver.common.ui.pagination.DefaultPaginationManager;
|
||||
import com.wb.domainserver.common.ui.pagination.ImagePaginationRenderer;
|
||||
import com.wb.domainserver.common.ui.pagination.PaginationRenderer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
import org.springframework.web.servlet.view.JstlView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class CommWebMvcConfig {
|
||||
|
||||
|
||||
/**
|
||||
* image pagination 처리를 위한 renderer set
|
||||
* @return ImagePaginationRenderer image pagination renderer
|
||||
*/
|
||||
@Bean
|
||||
public ImagePaginationRenderer imageRenderer() {
|
||||
return new ImagePaginationRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 기본 페이징 처리 bean 선언
|
||||
* @return DefaultPaginationManager 기본 페이징 처리 bean
|
||||
*/
|
||||
@Bean
|
||||
public DefaultPaginationManager paginationManager() {
|
||||
DefaultPaginationManager defaultPaginationManager = new DefaultPaginationManager();
|
||||
|
||||
Map<String, PaginationRenderer> rendererType = new HashMap<>();
|
||||
rendererType.put("image", imageRenderer());
|
||||
defaultPaginationManager.setRendererType(rendererType);
|
||||
|
||||
return defaultPaginationManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolver getViewResolver() {
|
||||
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
|
||||
resolver.setPrefix("/WEB-INF/views/");
|
||||
resolver.setSuffix(".jsp");
|
||||
resolver.setViewClass(JstlView.class);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReloadableResourceBundleMessageSource messageSource() {
|
||||
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
|
||||
|
||||
reloadableResourceBundleMessageSource.setBasenames(
|
||||
"classpath:/message/messages");
|
||||
reloadableResourceBundleMessageSource.setCacheSeconds(60);
|
||||
return reloadableResourceBundleMessageSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GconeMessageSource gconeMessageSource() {
|
||||
GconeMessageSource gconeMessageSource = new GconeMessageSource();
|
||||
gconeMessageSource.setReloadableResourceBundleMessageSource(messageSource());
|
||||
return gconeMessageSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import com.wb.domainserver.realm.RealmResolver;
|
||||
import com.wb.domainserver.realm.filter.RealmFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
@Configuration
|
||||
public class RealmFilterConfig {
|
||||
|
||||
@Autowired
|
||||
RealmResolver realmResolver;
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<RealmFilter> realmFilter() {
|
||||
FilterRegistrationBean<RealmFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new RealmFilter(realmResolver));
|
||||
registrationBean.addUrlPatterns("/*");
|
||||
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import com.wb.domainserver.domain.sopmal.oauth.service.CustomOAuth2UserService;
|
||||
import com.wb.domainserver.exception.response.CustomOauthFailureException;
|
||||
import com.wb.domainserver.realm.RealmResolver;
|
||||
import com.wb.domainserver.realm.filter.RealmFilter;
|
||||
import com.wb.domainserver.response.CustomAuthenticationSuccessHandler;
|
||||
import com.wb.domainserver.security.authentication.CustomAccessDeniedEntryPoint;
|
||||
import com.wb.domainserver.security.authentication.CustomAuthenticationEntryPoint;
|
||||
import com.wb.domainserver.security.authorization.WnbtAuthorizationManager;
|
||||
import com.wb.domainserver.security.filter.CustomXssFilter;
|
||||
import com.wb.domainserver.security.filter.JwtAuthenticationFilter;
|
||||
import com.wb.domainserver.security.handler.CustomLogoutSuccessHandler;
|
||||
import com.wb.domainserver.security.user.SecurityUserDetailService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;
|
||||
|
||||
@EnableWebSecurity(debug = true)
|
||||
@Configuration
|
||||
//@EnableMethodSecurity
|
||||
public class ResourceServerConfig {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ResourceServerConfig.class);
|
||||
|
||||
@Autowired
|
||||
private RealmResolver realmResolver;
|
||||
|
||||
@Autowired
|
||||
private SecurityUserDetailService userDetailService;
|
||||
|
||||
@Autowired
|
||||
private CustomAuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
@Autowired
|
||||
private CustomAccessDeniedEntryPoint accessDeniedEntryPoint;
|
||||
|
||||
@Autowired
|
||||
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
|
||||
|
||||
@Autowired
|
||||
private CustomOAuth2UserService customOAuth2UserService;
|
||||
|
||||
@Bean
|
||||
@Role(ROLE_INFRASTRUCTURE)
|
||||
public Advisor authorizationManagerBeforeMethodInterception(WnbtAuthorizationManager<?> authorizationManager) {
|
||||
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
|
||||
pattern.setPattern("com.wb.domainserver.*.controller.*");
|
||||
return new AuthorizationManagerBeforeMethodInterceptor(pattern, authorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
SecurityFilterChain sopmalSecurityFilterChain(HttpSecurity http, SecurityPathProperties securityProperties) throws Exception {
|
||||
|
||||
RequestMatcher logoutRequestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher("/sopmal/logout")
|
||||
);
|
||||
|
||||
//사용자 권한
|
||||
List<RequestMatcher> anonymousPath = securityProperties.getSolmal().getAnonmymous().stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RequestMatcher> authPath = securityProperties.getSolmal().getAuthenticated().stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RequestMatcher> resourcePath = securityProperties.getCommon().getResources().stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
OrRequestMatcher anonymoushMatchers = new OrRequestMatcher(anonymousPath);
|
||||
OrRequestMatcher authMatchers = new OrRequestMatcher(authPath);
|
||||
OrRequestMatcher resourceMatchers = new OrRequestMatcher(resourcePath);
|
||||
|
||||
return http
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.securityMatcher("/sopmal/**", "/oauth2/**", "/login/oauth2/**")
|
||||
.authorizeHttpRequests(authorize ->
|
||||
authorize
|
||||
.requestMatchers(authMatchers).authenticated()
|
||||
.requestMatchers(anonymoushMatchers).anonymous()
|
||||
.requestMatchers(resourceMatchers).permitAll()
|
||||
.requestMatchers(
|
||||
new AntPathRequestMatcher("/oauth2/**"),
|
||||
new AntPathRequestMatcher("/login/oauth2/**")
|
||||
).permitAll()
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.oauth2Login(oauth2 -> oauth2 //sns oauth
|
||||
.loginPage("/sopmal/login")
|
||||
.successHandler(customAuthenticationSuccessHandler) //인증성공시 로그인 혹은 연동처리
|
||||
.userInfoEndpoint(userInfo -> userInfo //인증처리
|
||||
.userService(customOAuth2UserService)
|
||||
)
|
||||
.failureHandler(new CustomOauthFailureException()) //인증 실패시
|
||||
)
|
||||
.addFilterAfter(new CustomXssFilter(), RequestCacheAwareFilter.class)
|
||||
.addFilterBefore(new JwtAuthenticationFilter(userDetailService), LogoutFilter.class)
|
||||
.addFilterAfter(new RealmFilter(realmResolver), HeaderWriterFilter.class)
|
||||
.exceptionHandling(exceptions -> exceptions
|
||||
.authenticationEntryPoint(authenticationEntryPoint)
|
||||
.accessDeniedHandler(accessDeniedEntryPoint)
|
||||
).logout(logout
|
||||
-> logout.logoutRequestMatcher(logoutRequestMatcher)
|
||||
.deleteCookies("JSESSIONID")
|
||||
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
|
||||
)
|
||||
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(2)
|
||||
SecurityFilterChain adminSecurityFilterChain(HttpSecurity http, SecurityPathProperties securityProperties) throws Exception {
|
||||
|
||||
// 설정된 로그아웃 URL
|
||||
RequestMatcher logoutRequestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher("/mngr/logout"),
|
||||
new AntPathRequestMatcher("/admin/logout"),
|
||||
new AntPathRequestMatcher("/crm/logout")
|
||||
);
|
||||
|
||||
|
||||
List<RequestMatcher> anonmyPath = securityProperties.getAdmin().getAnonmymous().stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
List<RequestMatcher> authPath = securityProperties.getAdmin().getAuthenticated().stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
List<RequestMatcher> resourcePath = securityProperties.getCommon().getResources().stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
OrRequestMatcher authMatchers = new OrRequestMatcher(authPath);
|
||||
OrRequestMatcher resourceMatchers = new OrRequestMatcher(resourcePath);
|
||||
|
||||
return http
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.securityMatcher("/mngr/**","/mngr","/admin/**","/admin","/admin/**","/crm/**","/itrnt/**", logoutRequestMatcher.toString()) //"/*/mq/**", "/api/batch/schedulerloader/**", 제외
|
||||
.authorizeHttpRequests(authorize ->
|
||||
authorize
|
||||
// .requestMatchers(AntPathRequestMatcher.antMatcher("/*/mq/**")).anonymous() //ERP 호출 허용을 위해
|
||||
// .requestMatchers(AntPathRequestMatcher.antMatcher("/api/batch/schedulerloader/**")).anonymous() //batch 스케줄 동적기동
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/admin")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/mngr")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/mngr/login/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/admin/login/**")).permitAll()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/mngr/tokens/**")).anonymous()
|
||||
.requestMatchers(AntPathRequestMatcher.antMatcher("/admin/tokens/**")).anonymous()
|
||||
.requestMatchers(resourceMatchers).permitAll()
|
||||
.requestMatchers(authMatchers).authenticated()
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.addFilterBefore(new JwtAuthenticationFilter(userDetailService), LogoutFilter.class)
|
||||
.addFilterAfter(new RealmFilter(realmResolver), HeaderWriterFilter.class)
|
||||
.exceptionHandling(exceptions -> exceptions
|
||||
.authenticationEntryPoint(authenticationEntryPoint)
|
||||
.accessDeniedHandler(accessDeniedEntryPoint)
|
||||
).logout(logout
|
||||
-> logout.logoutRequestMatcher(logoutRequestMatcher)
|
||||
.deleteCookies("JSESSIONID")
|
||||
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
|
||||
)
|
||||
|
||||
.build();
|
||||
}
|
||||
|
||||
//
|
||||
// private String customBearerTokenResolver(HttpServletRequest request) {
|
||||
// String authorization = request.getHeader("Authorization");
|
||||
// if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer ")) {
|
||||
// return authorization.substring(7);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// private String determineIssuerUri(String token) {
|
||||
// return String.format(issuerUri+"/%s", RealmContextHolder.getRealm());
|
||||
// }
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Component
|
||||
@PropertySource(value = "classpath:/secured-config.yml", factory = SecurityPathPropertySourceFactory.class)
|
||||
@ConfigurationProperties(prefix = "secured.path")
|
||||
public class SecurityPathProperties {
|
||||
|
||||
private Solmal solmal;
|
||||
private Admin admin;
|
||||
private Common common;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Solmal {
|
||||
private List<String> anonmymous;
|
||||
private List<String> authenticated;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Admin {
|
||||
private List<String> anonmymous;
|
||||
private List<String> authenticated;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Common {
|
||||
private List<String> resources;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.core.io.support.PropertySourceFactory;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
public class SecurityPathPropertySourceFactory implements PropertySourceFactory {
|
||||
@Override
|
||||
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
|
||||
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
|
||||
String sourceName = name != null ? name : resource.getResource().getFilename();
|
||||
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
|
||||
}
|
||||
|
||||
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
|
||||
try {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(resource.getResource());
|
||||
factory.afterPropertiesSet();
|
||||
return factory.getObject();
|
||||
} catch (IllegalStateException e) {
|
||||
// for ignoreResourceNotFound
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof FileNotFoundException)
|
||||
throw (FileNotFoundException) e.getCause();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class TomcatWebCustomConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>{
|
||||
|
||||
@Override
|
||||
public void customize(TomcatServletWebServerFactory factory) {
|
||||
factory.addConnectorCustomizers(connector -> connector.setProperty("relaxedQueryChars", "[]"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
@Configuration
|
||||
public class WebClientConfig {
|
||||
|
||||
@Bean
|
||||
public WebClient webClient(WebClient.Builder builder) {
|
||||
builder.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1));
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("*")
|
||||
.allowedHeaders("*")
|
||||
.exposedHeaders("Authorization");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.wb.domainserver.security.config;
|
||||
|
||||
|
||||
import com.wb.domainserver.interceptor.sopmal.*;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@ComponentScan(basePackages = {
|
||||
"com.wb.domainserver.domain.sopmal"
|
||||
, "com.wb.domainserver.domain.log"
|
||||
, "com.wb.domainserver.interceptor.sopmal"})
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
private static final String[] SOPMAL_INTERCEPTOR_PATH = {"/","/index","/sopmal/**"};
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
if(!registry.hasMappingForPattern("/webjars/**")){
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("/webjars/").resourceChain(false);
|
||||
}
|
||||
if(!registry.hasMappingForPattern("/**")){
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations(
|
||||
"classpath:/static",
|
||||
"classpath:/static/js",
|
||||
"classpath:/static/img",
|
||||
"classpath:/static/login",
|
||||
"classpath:/static/css").resourceChain(false);
|
||||
}
|
||||
if(!registry.hasMappingForPattern("/**.html")){
|
||||
registry.addResourceHandler("/**.html")
|
||||
.addResourceLocations(
|
||||
"classpath:/templates/"
|
||||
).resourceChain(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
DeviceHandlerInterceptor deviceHandlerInterceptor = deviceHandlerInterceptor();
|
||||
registry.addInterceptor(deviceHandlerInterceptor).addPathPatterns();
|
||||
|
||||
LoggingInterceptor loggingInterceptor = loggingInterceptor();
|
||||
registry.addInterceptor(loggingInterceptor).addPathPatterns(SOPMAL_INTERCEPTOR_PATH)
|
||||
.order(1)
|
||||
.excludePathPatterns("/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif");
|
||||
OperInterceptor operInterceptor = operInterceptor();
|
||||
registry.addInterceptor(operInterceptor).addPathPatterns(SOPMAL_INTERCEPTOR_PATH)
|
||||
.order(2)
|
||||
.excludePathPatterns("/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif", "/oper");
|
||||
LoginChkInterceptor loginChkInterceptor = loginChkInterceptor();
|
||||
registry.addInterceptor(loginChkInterceptor).addPathPatterns(SOPMAL_INTERCEPTOR_PATH)
|
||||
.order(3)
|
||||
.excludePathPatterns("/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif");
|
||||
AuthInterceptor authInterceptor = authInterceptor();
|
||||
registry.addInterceptor(authInterceptor).addPathPatterns(SOPMAL_INTERCEPTOR_PATH)
|
||||
.order(4)
|
||||
.excludePathPatterns("/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif");
|
||||
MenuInterceptor menuInterceptor = menuInterceptor();
|
||||
registry.addInterceptor(menuInterceptor).addPathPatterns(SOPMAL_INTERCEPTOR_PATH)
|
||||
.order(5)
|
||||
.excludePathPatterns("/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif");
|
||||
FooterInterceptor footerInterceptor = footerInterceptor();
|
||||
registry.addInterceptor(footerInterceptor).addPathPatterns(SOPMAL_INTERCEPTOR_PATH)
|
||||
.order(6)
|
||||
.excludePathPatterns("/**/css/**", "/**/images/**", "/**/js/**", "/**/fonts/**", "/**/*.js", "/**/*.css", "/error", "/**/*.jpg", "/**/*.png", "/**/*.gif");
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DeviceHandlerInterceptor deviceHandlerInterceptor() {
|
||||
return new DeviceHandlerInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 처리 bean 선언
|
||||
*/
|
||||
@Bean
|
||||
public MenuInterceptor menuInterceptor() {
|
||||
return new MenuInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* footer 처리 bean 선언
|
||||
*/
|
||||
@Bean
|
||||
public FooterInterceptor footerInterceptor() {
|
||||
return new FooterInterceptor();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * logging 처리 bean 선언
|
||||
// */
|
||||
@Bean
|
||||
public LoggingInterceptor loggingInterceptor() {
|
||||
return new LoggingInterceptor();
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * 사용자 권한 처리 bean 선언
|
||||
// */
|
||||
@Bean
|
||||
public AuthInterceptor authInterceptor() {
|
||||
return new AuthInterceptor();
|
||||
}
|
||||
|
||||
// /**
|
||||
// *쇼핑몰 운영 처리 bean 선언
|
||||
// */
|
||||
@Bean
|
||||
public OperInterceptor operInterceptor() {
|
||||
return new OperInterceptor();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 로그인 확인 bean 선언
|
||||
// */
|
||||
@Bean
|
||||
public LoginChkInterceptor loginChkInterceptor() {
|
||||
return new LoginChkInterceptor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.wb.domainserver.security.filter;
|
||||
|
||||
import com.wb.domainserver.security.filter.wrapper.XssServletWrapper;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.util.ContentCachingResponseWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
public class CustomXssFilter implements Filter {
|
||||
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
//들어온 HttpRequest 분해하여 xss 필터링
|
||||
HttpServletRequestWrapper requestWrapper = new XssServletWrapper((HttpServletRequest) request);
|
||||
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);
|
||||
chain.doFilter(requestWrapper, responseWrapper);
|
||||
responseWrapper.copyBodyToResponse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.wb.domainserver.security.filter;
|
||||
|
||||
import com.nimbusds.jose.util.StandardCharset;
|
||||
import com.wb.domainserver.realm.RealmContextHolder;
|
||||
import com.wb.domainserver.realm.util.UrlExtractor;
|
||||
import com.wb.domainserver.security.user.CustomAuthenticationToken;
|
||||
import com.wb.domainserver.security.user.SecurityUserDetailService;
|
||||
import com.wb.domainserver.util.CookieUtils;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final SecurityUserDetailService customUserDetailService;
|
||||
|
||||
public JwtAuthenticationFilter(SecurityUserDetailService customUserDetailService) {
|
||||
this.customUserDetailService = customUserDetailService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, AuthenticationException {
|
||||
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
|
||||
String realm = RealmContextHolder.getRealm().toLowerCase();
|
||||
|
||||
try {
|
||||
|
||||
String requestUri = request.getRequestURI();
|
||||
log.debug("request uri: {}", requestUri);
|
||||
|
||||
|
||||
String accessToken = extractToken(request);
|
||||
|
||||
|
||||
String sysDvCd = RealmContextHolder.getSysDvCdRealm();
|
||||
String expireChk = request.getParameter("expire");
|
||||
String tokenExpireCookie = CookieUtils.getCookieValue("token_expire", request);
|
||||
|
||||
//사용자이면서 ACT가 없는데 tokenExpire는 있을 경우
|
||||
//로그인된 사용자지만 엑세스 토큰이 만료되어 다시 재발급 받아야 하는 대상
|
||||
//expire값은 루프 요청 방지
|
||||
if("01".equals(sysDvCd) && !StringUtils.hasText(accessToken) && StringUtils.hasText(tokenExpireCookie) && !"p".equals(expireChk)){
|
||||
response.sendRedirect("/"+realm+"/tokens/refresh?redirect_uri=%s&expire=%s".formatted(URLEncoder.encode(UrlExtractor.getOriginalURL(request), StandardCharset.UTF_8), "p"));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// accessToken이 있고 refresh Token 요청이 아닌경우
|
||||
if (StringUtils.hasText(accessToken) && !requestUri.contains("/tokens/refresh")) {
|
||||
UserDetails userDetails = customUserDetailService.loadUserByUsername(accessToken);
|
||||
if (userDetails != null) {
|
||||
log.debug("user details: {}", userDetails);
|
||||
log.debug("user details authorizeList: {}", userDetails.getAuthorities());
|
||||
setAuthentication(userDetails);
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
} catch (JwtException je){
|
||||
log.error("jwt access token error: {}", je.getMessage());
|
||||
|
||||
Map<String, String> loginCookiePath = CookieUtils.loginCookiePath();
|
||||
|
||||
//로그인 관련 쿠키 삭제
|
||||
CookieUtils.deleteCookie("ACT", loginCookiePath.get("ACTPath"), request, response);
|
||||
CookieUtils.deleteCookie("RFT", loginCookiePath.get("RFTPath"), request, response);
|
||||
CookieUtils.deleteCookie("token_expire", loginCookiePath.get("ACTPath"), request, response);
|
||||
|
||||
response.sendRedirect("/"+ realm + "/logout");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
|
||||
String accessToken = "";
|
||||
|
||||
if(request.getHeader(HttpHeaders.AUTHORIZATION) != null){
|
||||
accessToken = request.getHeader(HttpHeaders.AUTHORIZATION).replaceFirst("Bearer ", "");
|
||||
}
|
||||
else{
|
||||
|
||||
if(request.getCookies() != null){
|
||||
for(Cookie cookie : request.getCookies()){
|
||||
if("ACT".equals(cookie.getName()) && StringUtils.hasText(cookie.getValue())){
|
||||
accessToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private void setAuthentication(UserDetails userDetails) {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new CustomAuthenticationToken(userDetails, userDetails.getAuthorities())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.wb.domainserver.security.filter;
|
||||
|
||||
import com.wb.domainserver.interceptor.wrapper.MultipleReadableRequestWrapper;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.ContentCachingResponseWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class LoggingFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
MultipleReadableRequestWrapper requestWrapper = new MultipleReadableRequestWrapper(request);
|
||||
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
|
||||
filterChain.doFilter(requestWrapper, responseWrapper);
|
||||
responseWrapper.copyBodyToResponse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.wb.domainserver.security.filter.wrapper;
|
||||
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
public class XssServletWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private byte[] rawData;
|
||||
|
||||
public XssServletWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
// log.info("xssServletWrapper start");
|
||||
try {
|
||||
//reqeust를 inputStream으로 바이트 변환하여 XSS 필터링
|
||||
if(request.getMethod().equalsIgnoreCase("post")) {
|
||||
// log.info("xssServletWrapper ing");
|
||||
InputStream is = request.getInputStream();
|
||||
this.rawData = replaceXSS(IOUtils.toByteArray(is));
|
||||
}
|
||||
|
||||
// log.info("xssServletWrapper end");
|
||||
} catch (IOException e) {
|
||||
log.error("xssServletWrapper err {}",e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//바이트 단위 script 태그 치환
|
||||
private byte[] replaceXSS(byte[] data) {
|
||||
|
||||
if(data != null) {
|
||||
// log.info("xssFilter replace Byte IN ");
|
||||
|
||||
String strData = new String(data);
|
||||
data = replaceStrXss(strData).getBytes();
|
||||
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//문자열 단위 script 태그 치환
|
||||
private String replaceXSS(String value) {
|
||||
|
||||
if(value != null) {
|
||||
// log.info("xssFilter replace String IN ");
|
||||
value = replaceStrXss(value);
|
||||
|
||||
}
|
||||
|
||||
// log.debug("returnValue :: {}", value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
if(this.rawData == null) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.rawData);
|
||||
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return byteArrayInputStream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return replaceXSS(super.getQueryString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameter(String name) {
|
||||
return replaceXSS(super.getParameter(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getParameterMap() {
|
||||
Map<String, String[]> params = super.getParameterMap();
|
||||
if(params != null) {
|
||||
params.forEach((key, value) -> {
|
||||
for(int i=0; i<value.length; i++) {
|
||||
value[i] = replaceXSS(value[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParameterValues(String name) {
|
||||
String[] params = super.getParameterValues(name);
|
||||
if(params != null) {
|
||||
for(int i=0; i<params.length; i++) {
|
||||
params[i] = replaceXSS(params[i]);
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(this.getInputStream(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
|
||||
private String replaceStrXss(String value){
|
||||
|
||||
// Avoid anything between script tags
|
||||
Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid anything in a src='...' type of expression
|
||||
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Remove any lonesome </script> tag
|
||||
scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Remove any lonesome <script ...> tag
|
||||
scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid eval(...) expressions
|
||||
scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid expression(...) expressions
|
||||
scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid javascript:... expressions
|
||||
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid vbscript:... expressions
|
||||
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid onload= expressions
|
||||
scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.wb.domainserver.security.handler;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 1
|
||||
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
|
||||
response.getWriter().write("권한 불충분 %s".formatted(accessDeniedException.getMessage())); // 2
|
||||
response.getWriter().flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.wb.domainserver.security.handler;
|
||||
|
||||
import com.wb.domainserver.realm.RealmContextHolder;
|
||||
import com.wb.domainserver.realm.util.UrlExtractor;
|
||||
import com.wb.domainserver.util.CookieUtils;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
|
||||
|
||||
String userRole = "";
|
||||
|
||||
//엑세스토큰 유효한 상태에서의 로그아웃
|
||||
if(authentication != null){
|
||||
userRole = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).findFirst().orElse("");
|
||||
}
|
||||
//엑세스토큰 유효하지 않은 상태의 로그아웃
|
||||
else{
|
||||
|
||||
userRole = switch (RealmContextHolder.getSysDvCdRealm()) {
|
||||
case "02" -> "ROLE_ADMIN";
|
||||
case "03" -> "ROLE_MNGR";
|
||||
default -> "ROLE_USER";
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
String originUrlHost = UrlExtractor.getOriginalHost(RealmContextHolder.getOriginalHostRealm());
|
||||
|
||||
log.debug("logout success. userRole : {}", userRole);
|
||||
log.debug("logout success. userRole getRealm : {}", RealmContextHolder.getRealm());
|
||||
log.debug("logout success. userRole getSysDvCdRealm: {}", RealmContextHolder.getSysDvCdRealm());
|
||||
log.debug("logout success. userRole getOriginalHostRealm: {}", originUrlHost);
|
||||
|
||||
// 세션 만료 처리
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
|
||||
// SecurityContextHolder 클리어
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
//로그인 쿠키 경로
|
||||
Map<String, String> cookiePath = CookieUtils.loginCookiePath();
|
||||
|
||||
//관리자 일경우
|
||||
if("ROLE_MNGR".equals(userRole) || "ROLE_ADMIN".equals(userRole)){
|
||||
|
||||
// 쿠키 삭제
|
||||
CookieUtils.deleteCookie("ACT", cookiePath.get("ACTPath"),request, response);
|
||||
CookieUtils.deleteCookie("RFT", cookiePath.get("RFTPath"), request, response);
|
||||
CookieUtils.deleteCookie("token_expire", cookiePath.get("ACTPath"), request, response);
|
||||
response.sendRedirect( "/mngr");
|
||||
|
||||
}else{
|
||||
|
||||
// 쿠키 삭제
|
||||
CookieUtils.deleteCookie("ACT", cookiePath.get("ACTPath"), request, response);
|
||||
CookieUtils.deleteCookie("RFT", cookiePath.get("RFTPath"), request, response);
|
||||
CookieUtils.deleteCookie("token_expire", cookiePath.get("ACTPath"), request, response);
|
||||
|
||||
response.sendRedirect("/sopmal/main");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.wb.domainserver.security.jwt.config;
|
||||
|
||||
|
||||
import com.wb.domainserver.security.jwt.handler.JwtHandler;
|
||||
import com.wb.domainserver.security.jwt.handler.JwtHelper;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Configuration
|
||||
public class JwtConfig {
|
||||
|
||||
private final JwtHandler jwtHandler;
|
||||
|
||||
public JwtConfig(JwtHandler jwtHandler) {
|
||||
this.jwtHandler = jwtHandler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtHelper accessTokenHelper(
|
||||
@Value("${jwt.access.max-age}") long accessMaxAge
|
||||
, @Value("${jwt.access.secretKey}") String accessSecretKey) {
|
||||
|
||||
SecretKey secretKey = Keys.hmacShaKeyFor(accessSecretKey.getBytes(StandardCharsets.UTF_8));
|
||||
return new JwtHelper(jwtHandler, accessMaxAge, secretKey);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtHelper refreshTokenHelper(
|
||||
@Value("${jwt.refresh.max-age}") long refreshMaxAge
|
||||
, @Value("${jwt.refresh.secretKey}") String refreshSecretKey) {
|
||||
|
||||
SecretKey secretKey = Keys.hmacShaKeyFor(refreshSecretKey.getBytes(StandardCharsets.UTF_8));
|
||||
return new JwtHelper(jwtHandler, refreshMaxAge, secretKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.wb.domainserver.security.jwt.handler;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtHandler {
|
||||
|
||||
|
||||
//발행일
|
||||
private final Date ISSUE_AT = Date.from(ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toInstant());
|
||||
|
||||
|
||||
public String createToken(Map<String, Object> claims, long maxAgeSeconds, SecretKey secretKey) {
|
||||
return Jwts.builder()
|
||||
.addClaims(claims)
|
||||
.addClaims(Map.of(
|
||||
Claims.ISSUED_AT, ISSUE_AT,
|
||||
Claims.EXPIRATION, Date.from(ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toInstant().plusSeconds(maxAgeSeconds))
|
||||
))
|
||||
.signWith(secretKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
|
||||
public Claims parse(String token, SecretKey secretKey) throws JwtException {
|
||||
|
||||
var parser = Jwts.parserBuilder().setSigningKey(secretKey).build();
|
||||
Jws<Claims> parsedJws = parser.parseClaimsJws(unType(token));
|
||||
return parsedJws.getBody();
|
||||
|
||||
}
|
||||
|
||||
public boolean isTokenExpired(String token, SecretKey secretKey) {
|
||||
try {
|
||||
Claims claims = parse(token, secretKey);
|
||||
return claims.getExpiration().before(new Date());
|
||||
} catch (ExpiredJwtException eje) {
|
||||
log.error("Token has expired: {}", eje.getMessage());
|
||||
|
||||
return true;
|
||||
|
||||
}catch (JwtException je) {
|
||||
log.error("Invalid JWT token: {}", je.getMessage());
|
||||
throw je;
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new JwtException("Invalid JWT token");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String unType(String token) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.wb.domainserver.security.jwt.handler;
|
||||
|
||||
import com.wb.domainserver.domain.gcone.login.vo.LoginUser;
|
||||
import com.wb.domainserver.security.authentication.MemberJwtToken;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JwtHelper {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(JwtHelper.class);
|
||||
|
||||
private final JwtHandler jwtHandler;
|
||||
private final long maxAgeSeconds;
|
||||
private final SecretKey secretKey;
|
||||
|
||||
|
||||
public JwtHelper(JwtHandler jwtHandler, long maxAgeSeconds, SecretKey secretKey) {
|
||||
this.jwtHandler = jwtHandler;
|
||||
this.maxAgeSeconds = maxAgeSeconds;
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
|
||||
public String createToken(LoginUser claims) {
|
||||
Map<String, Object> claimsMap = new HashMap<>();
|
||||
claimsMap.put("mbrId", claims.getUserId());
|
||||
claimsMap.put("sopmalCd", claims.getSopmalCd());
|
||||
claimsMap.put("mbrDvCd", claims.getMbrDvCd());
|
||||
claimsMap.put("userInnb", claims.getUserInnb());
|
||||
claimsMap.put("mbrNm", claims.getUsername());
|
||||
claimsMap.put("mbtlnb", claims.getMbtlnb());
|
||||
// claimsMap.put("accessUrl", claims.);
|
||||
claimsMap.put("clientId", "account");
|
||||
claimsMap.put("sysDvCd", claims.getSysDvCd());
|
||||
claimsMap.put("mngrDv", claims.getMngrDv());
|
||||
claimsMap.put("teamInnb", claims.getTeamInnb());
|
||||
claimsMap.put("teamMngrAt", claims.getTeamMngrAt());
|
||||
|
||||
List<String> roleTypes = new ArrayList<>();
|
||||
String roleType = switch (claims.getSysDvCd()){
|
||||
case "02" -> "ROLE_ADMIN";
|
||||
case "03" -> "ROLE_MNGR";
|
||||
default -> "ROLE_USER";
|
||||
|
||||
};
|
||||
roleTypes.add(roleType);
|
||||
claimsMap.put("roleType", roleTypes);
|
||||
|
||||
return jwtHandler.createToken(claimsMap, maxAgeSeconds, secretKey);
|
||||
}
|
||||
|
||||
|
||||
public MemberJwtToken parse(String token) {
|
||||
Claims claims = jwtHandler.parse(token, secretKey);
|
||||
return convert(claims);
|
||||
}
|
||||
|
||||
public Claims getClaims(String token) {
|
||||
return jwtHandler.parse(token, secretKey);
|
||||
}
|
||||
|
||||
|
||||
public boolean expireToken(String token) {
|
||||
|
||||
return jwtHandler.isTokenExpired(token, secretKey);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private MemberJwtToken convert(Claims claims) {
|
||||
|
||||
return new MemberJwtToken(claims);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.wb.domainserver.security.jwt.mapper;
|
||||
|
||||
import com.wb.domainserver.domain.gcone.login.vo.LoginUser;
|
||||
import com.wb.domainserver.security.jwt.vo.TokenInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface TokenMapper {
|
||||
|
||||
|
||||
public TokenInfo selectToken(LoginUser loginUser);
|
||||
|
||||
public TokenInfo selectRefreshToken(TokenInfo token);
|
||||
|
||||
public void insertToken(TokenInfo token);
|
||||
|
||||
public void updateToken(TokenInfo token);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.wb.domainserver.security.jwt.service;
|
||||
|
||||
import com.wb.domainserver.domain.gcone.login.vo.LoginUser;
|
||||
import com.wb.domainserver.security.jwt.handler.JwtHelper;
|
||||
import com.wb.domainserver.security.jwt.mapper.TokenMapper;
|
||||
import com.wb.domainserver.security.jwt.vo.TokenInfo;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TokenService {
|
||||
|
||||
private final TokenMapper tokenMapper;
|
||||
private final JwtHelper accessTokenHelper;
|
||||
private final JwtHelper refreshTokenHelper;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 엑세스, 리프레쉬 토큰 생성
|
||||
*
|
||||
* */
|
||||
public TokenInfo createToken(LoginUser loginUser){
|
||||
|
||||
|
||||
String accessToken = accessTokenHelper.createToken(loginUser);
|
||||
Claims accessTokenClaims = accessTokenHelper.getClaims(accessToken);
|
||||
|
||||
//리프레쉬 토큰은 회원고유번호와 회원구분코드만 저장
|
||||
LoginUser refreshUser = new LoginUser();
|
||||
refreshUser.setUserInnb(loginUser.getUserInnb());
|
||||
refreshUser.setMbrDvCd(loginUser.getMbrDvCd());
|
||||
refreshUser.setSysDvCd(loginUser.getSysDvCd());
|
||||
String refreshToken = refreshTokenHelper.createToken(refreshUser);
|
||||
Claims refreshTokenClaims = refreshTokenHelper.getClaims(refreshToken);
|
||||
|
||||
return TokenInfo.of(accessToken, accessTokenClaims, refreshToken, refreshTokenClaims);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 토큰 만료 여부 확인
|
||||
* */
|
||||
public boolean tokenExpiredAt(String tokenDv,String token){
|
||||
|
||||
boolean isExpired = false;
|
||||
|
||||
if(tokenDv.equals("ACT")){
|
||||
isExpired = accessTokenHelper.expireToken(token);
|
||||
}else{
|
||||
isExpired = refreshTokenHelper.expireToken(token);
|
||||
}
|
||||
|
||||
return isExpired;
|
||||
}
|
||||
|
||||
/**
|
||||
* 리프레쉬 토큰 파싱 정보 조회
|
||||
*
|
||||
* */
|
||||
public Claims getRefreshToken(String refreshToken){
|
||||
|
||||
return refreshTokenHelper.getClaims(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 엑세스 토큰 DB 정보 조회
|
||||
*
|
||||
* */
|
||||
public TokenInfo selectToken(LoginUser loginUser){
|
||||
return tokenMapper.selectToken(loginUser);
|
||||
};
|
||||
|
||||
/**
|
||||
* 리프레쉬 토큰 DB 정보 조회
|
||||
*
|
||||
* */
|
||||
public TokenInfo selectRefreshToken(TokenInfo token){
|
||||
return tokenMapper.selectRefreshToken(token);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 토큰정보 저장
|
||||
*
|
||||
* */
|
||||
@Transactional
|
||||
public void insertToken(TokenInfo token){
|
||||
tokenMapper.insertToken(token);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 토큰정보 수정
|
||||
*
|
||||
* */
|
||||
@Transactional
|
||||
public TokenInfo updateToken(LoginUser loginUser){
|
||||
|
||||
TokenInfo token = createToken(loginUser);
|
||||
token.setRealm(loginUser.getRealm());
|
||||
token.setUserInnb(Long.parseLong(loginUser.getUserInnb()));
|
||||
|
||||
tokenMapper.updateToken(token);
|
||||
|
||||
return token;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 엑세스토큰 수정 및 저장
|
||||
*
|
||||
* */
|
||||
@Transactional
|
||||
public TokenInfo updateAccessToken(LoginUser loginUser, TokenInfo tokenInfo){
|
||||
|
||||
//토큰 생성
|
||||
TokenInfo token = createToken(loginUser);
|
||||
|
||||
//엑세스토큰만 가져와 교체
|
||||
tokenInfo.setAccessToken(token.getAccessToken());
|
||||
|
||||
tokenMapper.updateToken(tokenInfo);
|
||||
|
||||
return tokenInfo;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.wb.domainserver.security.jwt.vo;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
public class TokenInfo {
|
||||
|
||||
private String realm;
|
||||
|
||||
private long userInnb;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String refreshToken;
|
||||
|
||||
private Date createdAt;
|
||||
|
||||
private Date accessExpiresAt;
|
||||
|
||||
private Date refreshExpiresAt;
|
||||
|
||||
private Date lastAccessedTime;
|
||||
|
||||
//리프레쉬 토큰 만료 여부
|
||||
private String expiredAt;
|
||||
|
||||
|
||||
public void setLastAccessedTimeToNow() {
|
||||
this.lastAccessedTime = Date.from(ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toInstant());
|
||||
}
|
||||
|
||||
public TokenInfo(String tokenDv, String token, long userInnb, String realm){
|
||||
|
||||
if("ACT".equals(tokenDv)){
|
||||
this.accessToken = token;
|
||||
}else{
|
||||
this.refreshToken = token;
|
||||
}
|
||||
this.userInnb = userInnb;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public TokenInfo(String accessToken, Claims accessTokenClaims, String refreshToken, Claims refreshTokenClaims){
|
||||
|
||||
this.accessToken = accessToken;
|
||||
this.accessExpiresAt = accessTokenClaims.getExpiration();
|
||||
this.refreshToken = refreshToken;
|
||||
this.refreshExpiresAt = refreshTokenClaims.getExpiration();
|
||||
this.lastAccessedTime = Date.from(ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toInstant());
|
||||
}
|
||||
|
||||
/* mybatis constructor */
|
||||
public TokenInfo(String realm, BigInteger userInnb, String accessToken, String refreshToken, Timestamp createdAt, Timestamp refreshExpiresAt, String expiredAt, Timestamp lastAccessedTime) {
|
||||
this.realm = realm;
|
||||
this.userInnb = userInnb.longValue();
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
this.createdAt = createdAt;
|
||||
this.refreshExpiresAt = refreshExpiresAt;
|
||||
this.expiredAt = expiredAt;
|
||||
this.lastAccessedTime = lastAccessedTime;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static TokenInfo of(String accessToken, Claims accessTokenClaims, String refreshToken, Claims refreshTokenClaims) {
|
||||
return new TokenInfo(accessToken, accessTokenClaims, refreshToken, refreshTokenClaims);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.wb.domainserver.security.user;
|
||||
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class CustomAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
|
||||
private final UserDetails principal;
|
||||
|
||||
public CustomAuthenticationToken(UserDetails principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
setAuthenticated(true); // 인증 상태 적용
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.wb.domainserver.security.user;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class SecurityUser implements UserDetails {
|
||||
private String id;
|
||||
private String mbrId;
|
||||
private String sopmalCd;
|
||||
private String mbrDvCd;
|
||||
private long userInnb;
|
||||
private String userName;
|
||||
private String mbtlnb;
|
||||
private String accessUrl;
|
||||
private String clientId;
|
||||
private String originalHost;
|
||||
private String sysDvCd;
|
||||
private String tentGroup;
|
||||
private String reprsntBrand;
|
||||
private String teamInnb;
|
||||
private String mngrDv;
|
||||
private String tentcpnNm;
|
||||
private String itrntDv;
|
||||
private String strOperDv;
|
||||
|
||||
|
||||
public String getItrntDv() {
|
||||
return itrntDv;
|
||||
}
|
||||
|
||||
public void setItrntDv(String itrntDv) {
|
||||
this.itrntDv = itrntDv;
|
||||
}
|
||||
|
||||
public String getStrOperDv() {
|
||||
return strOperDv;
|
||||
}
|
||||
|
||||
public void setStrOperDv(String strOperDv) {
|
||||
this.strOperDv = strOperDv;
|
||||
}
|
||||
|
||||
|
||||
public String getTentcpnNm() {
|
||||
return tentcpnNm;
|
||||
}
|
||||
|
||||
public void setTentcpnNm(String tentcpnNm) {
|
||||
this.tentcpnNm = tentcpnNm;
|
||||
}
|
||||
|
||||
|
||||
private List<GrantedAuthority> grantedAuthorityList;
|
||||
private boolean accessToRestrictedPolicy;
|
||||
|
||||
public static SecurityUser builder() {
|
||||
return new SecurityUser();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SecurityUser withAccessToRestrictedPolicy(boolean restrictedPolicy) {
|
||||
this.accessToRestrictedPolicy = restrictedPolicy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasAccessToRestrictedPolicy() {
|
||||
return accessToRestrictedPolicy;
|
||||
}
|
||||
|
||||
public SecurityUser withGrantedAuthorityList(List<GrantedAuthority> grantedAuthorityList) {
|
||||
this.grantedAuthorityList = grantedAuthorityList;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return this.grantedAuthorityList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.userName;
|
||||
}
|
||||
|
||||
public SecurityUser withUserInnb(long userInnb) {
|
||||
this.userInnb = userInnb;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getUserInnb() {
|
||||
return this.userInnb;
|
||||
}
|
||||
|
||||
public String getMbrDvCd() { return mbrDvCd; }
|
||||
|
||||
public SecurityUser withMbrDvCd(String mbrDvCd) {
|
||||
this.mbrDvCd = mbrDvCd;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public SecurityUser withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SecurityUser withUserName(String userName) {
|
||||
this.userName = userName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withMbtlnb(String mbtlnb) {
|
||||
this.mbtlnb = mbtlnb;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withSopmalCd(String sopmalCd) {
|
||||
this.sopmalCd = sopmalCd;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withAccessUrl(String accessUrl) {
|
||||
this.accessUrl = accessUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withOriginalHost(String originalHost) {
|
||||
this.originalHost = originalHost;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withSysDvCd(String sysDvCd) {
|
||||
this.sysDvCd = sysDvCd;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withMngrDv(String mngrDv) {
|
||||
this.mngrDv = mngrDv;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withTeamInnb(String teamInnb) {
|
||||
this.teamInnb = teamInnb;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withTentGroup(String tentGroup) {
|
||||
this.tentGroup = tentGroup;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityUser withReprsntBrand(String reprsntBrand) {
|
||||
this.reprsntBrand = reprsntBrand;
|
||||
return this;
|
||||
}
|
||||
public SecurityUser withTentcpnNm(String tentcpnNm) {
|
||||
this.tentcpnNm = tentcpnNm;
|
||||
return this;
|
||||
}
|
||||
public SecurityUser withItrntDv(String itrntDv) {
|
||||
this.itrntDv = itrntDv;
|
||||
return this;
|
||||
}
|
||||
public SecurityUser withStrOperDv(String strOperDv) {
|
||||
this.strOperDv = strOperDv;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getSopmalCd() {
|
||||
return this.sopmalCd;
|
||||
|
||||
}
|
||||
|
||||
public String getMbtlnb() {
|
||||
return this.mbtlnb;
|
||||
}
|
||||
|
||||
public String getAccessUrl() {
|
||||
return this.accessUrl;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
public String getOriginalHost() {
|
||||
return this.originalHost;
|
||||
}
|
||||
|
||||
public String getSysDvCd() {
|
||||
return this.sysDvCd;
|
||||
}
|
||||
|
||||
public String getTentGroup() {
|
||||
return this.tentGroup;
|
||||
}
|
||||
|
||||
public String getReprsntBrand() {
|
||||
return this.reprsntBrand;
|
||||
}
|
||||
|
||||
public SecurityUser withMbrId(String mbrId) {
|
||||
this.mbrId = mbrId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMbrId() {return this.mbrId;}
|
||||
public String getTeamInnb() {return this.teamInnb;}
|
||||
public String getMngrDv() {
|
||||
return this.mngrDv;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.wb.domainserver.security.user;
|
||||
|
||||
import com.wb.domainserver.realm.RealmContextHolder;
|
||||
import com.wb.domainserver.security.authentication.MemberJwtToken;
|
||||
import com.wb.domainserver.security.jwt.handler.JwtHelper;
|
||||
import com.wb.domainserver.security.jwt.vo.TokenInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SecurityUserDetailService implements UserDetailsService {
|
||||
|
||||
private final SecurityUserMapper securityUserMapper;
|
||||
|
||||
private final JwtHelper accessTokenHelper;
|
||||
|
||||
public SecurityUserDetailService(
|
||||
SecurityUserMapper securityUserMapper, JwtHelper accessTokenHelper){
|
||||
this.securityUserMapper = securityUserMapper;
|
||||
this.accessTokenHelper = accessTokenHelper ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String accessToken) throws UsernameNotFoundException {
|
||||
|
||||
// accessToken 만료여부 일경우 CustomAuthenticationEntryPoint 처리 유도
|
||||
if(accessTokenHelper.expireToken(accessToken)){
|
||||
return null;
|
||||
}
|
||||
|
||||
//accessToken DB 조회 검증
|
||||
//존재 하지 않을 경우 인증 실패
|
||||
MemberJwtToken token = accessTokenHelper.parse(accessToken);
|
||||
String realm = RealmContextHolder.getRealm().toLowerCase();
|
||||
TokenInfo accessTokenInfo = new TokenInfo("ACT", accessToken, token.getUserInnb(), realm);
|
||||
if(securityUserMapper.validateAccessToken(accessTokenInfo)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return tokenToUserDetails(token);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private UserDetails tokenToUserDetails (MemberJwtToken token) {
|
||||
|
||||
|
||||
|
||||
return SecurityUser.builder()
|
||||
.withId(token.getId())
|
||||
.withMbrId(token.getMbrId())
|
||||
.withSopmalCd(token.getSopmalCd())
|
||||
.withMbrDvCd(token.getMbrDvCd())
|
||||
.withUserInnb(token.getUserInnb())
|
||||
.withUserName(token.getMbrNm())
|
||||
.withMbtlnb(token.getMbtlnb())
|
||||
.withAccessUrl(token.getAccessUrl())
|
||||
.withClientId(token.getClientId())
|
||||
.withSysDvCd(token.getSysDvCd())
|
||||
.withMngrDv(token.getMngrDv())
|
||||
.withTentcpnNm(token.getTentcpnNm())
|
||||
.withItrntDv(token.getItrntDv())
|
||||
.withStrOperDv(token.getStrOperDv())
|
||||
// .withTentGroup(token.getTentGroup())
|
||||
// .withReprsntBrand(token.getReprsntBrand())
|
||||
.withGrantedAuthorityList(token.getRoleType().stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList())
|
||||
)
|
||||
.withAccessToRestrictedPolicy(checkRestrictedPolicy());
|
||||
}
|
||||
|
||||
|
||||
private boolean checkRestrictedPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getGrantType(String token) {
|
||||
return securityUserMapper.getGrantType(token);
|
||||
}
|
||||
|
||||
public List<String> getAvailablePathList(SecurityUser user) {
|
||||
return securityUserMapper.getAvailablePathList(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.wb.domainserver.security.user;
|
||||
|
||||
import com.wb.domainserver.security.jwt.vo.TokenInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface SecurityUserMapper {
|
||||
Map<String, Object> loadUserByUsername(Map<String, Object> username);
|
||||
|
||||
String getGrantType(String token);
|
||||
|
||||
List<String> getAvailablePathList(SecurityUser user);
|
||||
|
||||
Map<String, Object> loadUserByManagername(String username);
|
||||
|
||||
String loadUsernameByToken(Map<String, Object> data);
|
||||
|
||||
boolean validateAccessToken(TokenInfo tokenInfo);
|
||||
}
|
||||
Reference in New Issue
Block a user