diff --git a/main_vm/build.gradle b/main_vm/build.gradle index 2d57736..4d5e649 100644 --- a/main_vm/build.gradle +++ b/main_vm/build.gradle @@ -119,6 +119,11 @@ dependencies { implementation files("lib/ASRLIB-2.4.0.2.jar") implementation files("lib/pttsnet_class.jar") + + // jwt + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' } tasks.named('test') { diff --git a/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/controller/LoginController.java b/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/controller/LoginController.java index 645f34e..ae8233d 100644 --- a/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/controller/LoginController.java +++ b/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/controller/LoginController.java @@ -5,6 +5,7 @@ import com.icomsys.main_vm.biz.advice.excep.CustomBadRequestException; import com.icomsys.main_vm.biz.advice.excep.CustomNoSuchFieldException; import com.icomsys.main_vm.biz.advice.excep.CustomNotFoundException; import com.icomsys.main_vm.biz.common.common.service.FileService; +import com.icomsys.main_vm.biz.common.login.CinnamonToken; import com.icomsys.main_vm.biz.common.login.req.LoginReq; import com.icomsys.main_vm.biz.common.login.req.MainOprReq; import com.icomsys.main_vm.biz.common.login.req.PwdUpdateReq; @@ -68,6 +69,15 @@ public class LoginController { return loginService.LoginValidService(loginReq, model); } + @PostMapping("/adm/tokenLogin") + @ResponseBody + public CinnamonToken tokenLogin(@RequestBody LoginCheckReq dto) { + String id = dto.getUserId(); + String pwd = dto.getPassword(); + return loginService.tokenLogin(dto); + } + + @RequestMapping(value = "/adm/main/actionMain.do") public String actionMain(ModelMap model) { return loginService.actionMain(model); diff --git a/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/CustomUserDetailsService.java b/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/CustomUserDetailsService.java index f15a3b4..77dd455 100644 --- a/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/CustomUserDetailsService.java +++ b/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/CustomUserDetailsService.java @@ -45,8 +45,7 @@ public class CustomUserDetailsService implements UserDetailsService { List permission = authList.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); - return new User(String.valueOf(dto.getUserId()), dto.getPassword(), permission) - ; + return new User(String.valueOf(dto.getUserId()), dto.getPassword(), permission); } diff --git a/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/LoginService.java b/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/LoginService.java index b203596..65af366 100644 --- a/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/LoginService.java +++ b/main_vm/src/main/java/com/icomsys/main_vm/biz/common/login/service/LoginService.java @@ -5,6 +5,8 @@ import com.icomsys.main_vm.biz.advice.excep.CustomBadRequestException; import com.icomsys.main_vm.biz.advice.excep.CustomNotFoundException; import com.icomsys.main_vm.biz.common.common.service.LogService; import com.icomsys.main_vm.biz.common.common.service.LogVO; +import com.icomsys.main_vm.biz.common.login.CinnamonToken; +import com.icomsys.main_vm.biz.common.login.TokenProvider; import com.icomsys.main_vm.biz.common.login.req.LoginReq; import com.icomsys.main_vm.biz.common.login.req.MainOprReq; import com.icomsys.main_vm.biz.common.login.req.PwdUpdateReq; @@ -29,6 +31,7 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -54,6 +57,8 @@ import java.util.stream.Collectors; */ public class LoginService { + private final TokenProvider tokenProvider; + private final AuthenticationManagerBuilder authenticationManagerBuilder; private final AuthenticationManager authenticationManager; private final PasswordEncoder passwordEncoder; @@ -113,6 +118,26 @@ public class LoginService { } + @Transactional + public CinnamonToken tokenLogin(LoginCheckReq dto){ + // 1. Login ID/PW 를 기반으로 Authentication 객체 생성 + // 이때 authentication 는 인증 여부를 확인하는 authenticated 값이 false + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(dto.getUserId(), dto.getPassword()); + + // 2. 실제 검증 (사용자 비밀번호 체크)이 이루어지는 부분 + // authenticate 매서드가 실행될 때 CustomUserDetailsService 에서 만든 loadUserByUsername 메서드가 실행 + try { + Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); + + // 3. 인증 정보를 기반으로 JWT 토큰 생성 + CinnamonToken tokenInfo = tokenProvider.generateToken(authentication); + return tokenInfo; + } catch ( AuthenticationException e) { + log.info(e.getMessage()); + throw e; + } + } + public String actionMain(ModelMap model) { diff --git a/main_vm/src/main/java/com/icomsys/main_vm/common/filter/LoginFilter.java b/main_vm/src/main/java/com/icomsys/main_vm/common/filter/LoginFilter.java index 229d662..242dad7 100644 --- a/main_vm/src/main/java/com/icomsys/main_vm/common/filter/LoginFilter.java +++ b/main_vm/src/main/java/com/icomsys/main_vm/common/filter/LoginFilter.java @@ -4,12 +4,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; //import com.icomsys.main_vm.db.jpa.repo.bot.TbBotAuthMenuRepo; //import com.icomsys.main_vm.db.jpa.repo.bot.TbBotMenuRepo; import com.google.gson.Gson; +import com.icomsys.main_vm.biz.common.login.TokenProvider; import com.icomsys.main_vm.common.code.enumresource.SessionResource; import com.icomsys.main_vm.db.mybatis.alias.LoginVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; @@ -27,6 +31,8 @@ import java.time.LocalDateTime; public class LoginFilter extends OncePerRequestFilter { private final ObjectMapper objectMapper; + + private final TokenProvider tokenProvider; // private final TbBotAuthMenuRepo tbBotAuthMenuRepo; // private final TbBotMenuRepo tbBotMenuRepo; // @Value("${Server.servlet.context-path}") @@ -43,21 +49,12 @@ public class LoginFilter extends OncePerRequestFilter { // log.info("필터 요청유알엘 - {}, {}", reqUrl); - if (reqUrl.startsWith("/re1")){ - response.sendError(490); - return; - } - - if (reqUrl.startsWith("/re2")){ - response.sendError(491); - return; - } if (reqUrl.startsWith("/lib") || reqUrl.startsWith("/css") || reqUrl.startsWith("/images") || reqUrl.startsWith("/js") - || reqUrl.startsWith("/adm") + || reqUrl.startsWith("/adm/") || reqUrl.startsWith("/ws") || reqUrl.startsWith("/rest") || reqUrl.startsWith("/create") @@ -66,9 +63,28 @@ public class LoginFilter extends OncePerRequestFilter { || reqUrl.startsWith("/error") || reqUrl.startsWith("/files") || reqUrl.equals("/") +// || reqUrl.equals("/tokenLogin") ) { filterChain.doFilter(request, response); } else { + + // 1. Request Header 에서 JWT 토큰 추출 + String token = resolveToken((HttpServletRequest) request); + + // 2. validateToken 으로 토큰 유효성 검사 + if (token != null && tokenProvider.validateToken(token)) { + + // Todo: 토큰 유효성 검사 후 에러 처리 + // 토큰이 유효할 경우 토큰에서 Authentication 객체를 가지고 와서 SecurityContext 에 저장 + Authentication authentication = tokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + + response.sendError(403); + } + filterChain.doFilter(request, response); + } + +/* if (request.getSession().getAttribute(SessionResource.UserVO.getName()) == null || request.getSession().getAttribute(SessionResource.UserVO.getName()).equals("")) { log.info("session Check"); response.sendError(490); @@ -104,6 +120,17 @@ public class LoginFilter extends OncePerRequestFilter { } } // response.copyBodyToResponse(); + + */ + } + + // Request Header 에서 토큰 정보 추출 + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) { + return bearerToken.substring(7); + } + return null; } } diff --git a/main_vm/src/main/resources/application-local.yml b/main_vm/src/main/resources/application-local.yml index 217f35b..3d83e2d 100644 --- a/main_vm/src/main/resources/application-local.yml +++ b/main_vm/src/main/resources/application-local.yml @@ -29,6 +29,8 @@ spring: dialect: com.icomsys.main_vm.common.util.CustomDialect generate_statistics: true + jwt: + secret: f2d7e5002d67c8d118ebf800274b6a5c83ed7b3d3518b2cddcd7226f7484eb34 #https://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/dialect/package-summary.html feign: diff --git a/main_vm/src/main/webapp/WEB-INF/jsp/adm/signin/signin.jsp b/main_vm/src/main/webapp/WEB-INF/jsp/adm/signin/signin.jsp index 44d9788..b051faa 100644 --- a/main_vm/src/main/webapp/WEB-INF/jsp/adm/signin/signin.jsp +++ b/main_vm/src/main/webapp/WEB-INF/jsp/adm/signin/signin.jsp @@ -17,6 +17,8 @@ }); $(document).ready(function () { + let tmp = window.localStorage.getItem("cinnmonToken"); + console.log(tmp); $('.btn_wrap').on("click", function () { // fncSend(); fncCheck(); @@ -87,24 +89,46 @@ contentType: "application/json; charset=utf-8", datatype: "JSON", async: false, - url: "", + url: "", data: JSON.stringify(json), }) .complete(function (data) { + console.log(data); + if (data.status == 200) { + window.localStorage.setItem("cinnmonToken", data.responseJSON.accessToken); + + console.log(data.responseJSON); + + let tmp = window.localStorage.getItem("cinnmonToken"); + console.log(tmp); + document.signinInfoForm.action = ""; document.signinInfoForm.submit(); - } else if (data.status == 400) { - // alert("비밀번호 5회 실패로 계정이 잠겼습니다. 관리자에게 잠금해제 요청하세요."); - $.utils.warning("계정이 잠겼습니다. 관리자에게 잠금해제 요청하세요."); - } else if (data.status == 401) { - // alert("비밀번호 변경후 90일이 지났습니다."); - $.utils.warning("비밀번호 변경후 90일이 지났습니다."); - document.location.href = "" ; - } else if (data.status == 404) { - // alert("로그인 정보가 올바르지 않습니다."); - $.utils.warning("로그인 정보가 올바르지 않습니다."); - } + + } else { + // login 실패 + // Todo: 실패에 코드 정의 및 처리 방식 고려 필요 + console.log("data.status: " + data.status); + $.utils.warning("로그인 실패!! 에러 코드 처리 필요"); + } + + + <%--if (data.status == 200) {--%> + <%-- document.signinInfoForm.action = "";--%> + <%-- document.signinInfoForm.submit();--%> + <%--} else if (data.status == 400) {--%> + <%-- // alert("비밀번호 5회 실패로 계정이 잠겼습니다. 관리자에게 잠금해제 요청하세요.");--%> + <%-- $.utils.warning("계정이 잠겼습니다. 관리자에게 잠금해제 요청하세요.");--%> + <%--} else if (data.status == 401) {--%> + <%-- // alert("비밀번호 변경후 90일이 지났습니다.");--%> + <%-- $.utils.warning("비밀번호 변경후 90일이 지났습니다.");--%> + <%-- document.location.href = "" ;--%> + <%--} else if (data.status == 404) {--%> + <%-- // alert("로그인 정보가 올바르지 않습니다.");--%> + <%-- $.utils.warning("로그인 정보가 올바르지 않습니다.");--%> + <%--}--%> + }) }