From ec0384b770f59952d25f2c0011fed4c0368c5299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=84=9D=20=EC=B5=9C?= Date: Tue, 21 Nov 2023 18:01:22 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=EC=A4=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/constants/url.js | 3 +- .../src/css/base.css | 2 +- .../src/css/page.css | 15 +- .../src/pages/login/EgovLoginContent.jsx | 20 +- .../src/pages/login/join/InfoShareChk.jsx | 20 ++ .../src/pages/login/join/Join.jsx | 116 +++++++ .../src/routes/index.jsx | 7 +- .../auth/EgovLoginApiController.java | 190 +++++++++++ .../common => auth/entity}/LoginVO.java | 4 +- .../auth/mapper/LoginUserMapper.java | 56 ++++ .../auth/service/EgovLoginService.java | 52 +++ .../service/impl/EgovLoginServiceImpl.java | 125 +++++++ .../config/egov/EgovFileScrty.java | 305 ++++++++++++++++++ .../config/jwt/EgovJwtTokenUtil.java | 2 +- .../config/jwt/JwtAuthenticationFilter.java | 2 +- ...CustomAuthenticationPrincipalResolver.java | 2 +- .../config/security/SecurityConfig.java | 1 + .../standardCode/StandardCodeController.java | 2 +- .../mybatisMapper/LoginUserMapper.xml | 182 +++++++++++ 19 files changed, 1088 insertions(+), 18 deletions(-) create mode 100644 egovframe-template-simple-react-contribution/src/pages/login/join/InfoShareChk.jsx create mode 100644 egovframe-template-simple-react-contribution/src/pages/login/join/Join.jsx create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/EgovLoginApiController.java rename kcsc-back-end/src/main/java/com/dbnt/kcscbackend/{config/common => auth/entity}/LoginVO.java (97%) create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/mapper/LoginUserMapper.java create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/EgovLoginService.java create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/impl/EgovLoginServiceImpl.java create mode 100644 kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/egov/EgovFileScrty.java create mode 100644 kcsc-back-end/src/main/resources/mybatisMapper/LoginUserMapper.xml diff --git a/egovframe-template-simple-react-contribution/src/constants/url.js b/egovframe-template-simple-react-contribution/src/constants/url.js index 32ddaa5..9be2671 100644 --- a/egovframe-template-simple-react-contribution/src/constants/url.js +++ b/egovframe-template-simple-react-contribution/src/constants/url.js @@ -4,7 +4,8 @@ const URL = { MAIN : "/", //메인페이지 LOGIN : "/login", //로그인 - ERROR : "/error", //로그인 + JOIN : "/join", //회원가입 + ERROR : "/error", //에러 //ABOUT ABOUT : "/about", //사이트소개 diff --git a/egovframe-template-simple-react-contribution/src/css/base.css b/egovframe-template-simple-react-contribution/src/css/base.css index 1e1a118..4c4bf41 100644 --- a/egovframe-template-simple-react-contribution/src/css/base.css +++ b/egovframe-template-simple-react-contribution/src/css/base.css @@ -84,4 +84,4 @@ button {cursor: pointer;} .detailInfoDiv > div > p { display: inline; } -.detailInfoDiv > div > input {margin-right: 5px;} \ No newline at end of file +.detailInfoDiv > div > input {margin-right: 5px;} diff --git a/egovframe-template-simple-react-contribution/src/css/page.css b/egovframe-template-simple-react-contribution/src/css/page.css index cd87aed..b219bcd 100644 --- a/egovframe-template-simple-react-contribution/src/css/page.css +++ b/egovframe-template-simple-react-contribution/src/css/page.css @@ -12,13 +12,26 @@ .Plogin .login_box input[type=password]:-ms-input-placeholder {color: #aaa; opacity: 1;} .Plogin .login_box button {display: block; position: absolute; left: 435px; top: 70px; width: 160px; height: 110px; border-radius: 8px; color: #fff; font-size: 20px; font-weight: 500; text-align: center; line-height: 110px; background: #169bd5;} .Plogin .login_box button span {display: block; position: relative; height: 100%;} -.Plogin .login_box .chk {margin-top: 20px; font-size: 0;} +.Plogin .login_box .chk {margin-top: 20px;} .Plogin .login_box .chk em {display: inline-block; height: 30px; margin-left: 40px; color: #666; font-size: 16px;} .Plogin .list {margin-top: 44px; padding: 0 360px;} .Plogin .list li {position: relative; padding-left: 15px; color: #666; font-size: 16px; line-height: 26px;} .Plogin .list li::before {content: ""; display: block; position: absolute; left: 0; top: 12px; width: 4px; height: 4px; background: #666;} .Plogin .list li + li {margin-top: 5px;} +.Pjoin h1 {color: #222; font-size: 48px; font-weight: 500; letter-spacing: -2px; line-height: 48px; text-align: center;} +.Pjoin .join_box {position: relative; width: 690px; margin: 54px auto 0; padding: 70px 95px 120px 95px ; border: 1px solid #dde2e5; border-radius: 25px; box-shadow: 3px 4px 5px #ccc;} +.Pjoin .join_box input[type=text], +.Pjoin .join_box input[type=password] {width: 100%; height: 46px; padding: 0 20px; border: 0; border-radius: 8px; color: #666; font-size: 16px; background: #f5f5f5;} +.Pjoin .join_box .group input + input {margin-top: 18px;} +.Pjoin .join_box input[type=text]:-ms-input-placeholder, +.Pjoin .join_box input[type=password]:-ms-input-placeholder {color: #aaa; opacity: 1;} +.Pjoin .join_box .chk {margin-top: 20px;} +.Pjoin .join_box .chk em {display: inline-block; height: 30px; margin-left: 40px; color: #666; font-size: 16px;} +.Pjoin .join_box button {width: 500px;height: 50px;border-radius: 8px;color: #fff;font-size: 20px;font-weight: 500;text-align: center;line-height: 50px;background: #169bd5;} +.Pjoin .join_box button span {display: block; position: relative; height: 100%;} +.Pjoin .join_box .list li {position: relative; padding-left: 15px; color: #666; font-size: 16px; line-height: 26px;} +.Pjoin .join_box .list li + li {margin-top: 5px;} /* Board */ diff --git a/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx b/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx index 040cd88..2b1698e 100644 --- a/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx +++ b/egovframe-template-simple-react-contribution/src/pages/login/EgovLoginContent.jsx @@ -1,9 +1,12 @@ import React, { useState, useEffect, useRef } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import {Link, useLocation, useNavigate} from 'react-router-dom'; import * as EgovNet from 'api/egovFetch'; import URL from 'constants/url'; import CODE from 'constants/code'; +import Row from 'react-bootstrap/Row'; +import Col from 'react-bootstrap/Col'; + import { getLocalItem, setLocalItem, setSessionItem } from 'utils/storage'; function EgovLoginContent(props) { @@ -113,11 +116,16 @@ function EgovLoginContent(props) { setUserInfo({ ...userInfo, password: e.target.value })} /> -
- -
+ + + + + + 회원가입 + + diff --git a/egovframe-template-simple-react-contribution/src/pages/login/join/InfoShareChk.jsx b/egovframe-template-simple-react-contribution/src/pages/login/join/InfoShareChk.jsx new file mode 100644 index 0000000..140969f --- /dev/null +++ b/egovframe-template-simple-react-contribution/src/pages/login/join/InfoShareChk.jsx @@ -0,0 +1,20 @@ +import React from 'react' + +function InfoShareChk() { + return ( + + ) +} +export default InfoShareChk; \ No newline at end of file diff --git a/egovframe-template-simple-react-contribution/src/pages/login/join/Join.jsx b/egovframe-template-simple-react-contribution/src/pages/login/join/Join.jsx new file mode 100644 index 0000000..b3f9968 --- /dev/null +++ b/egovframe-template-simple-react-contribution/src/pages/login/join/Join.jsx @@ -0,0 +1,116 @@ +import React, { useState, useEffect, useRef } from 'react'; +import {Link, useLocation, useNavigate} from 'react-router-dom'; +import * as EgovNet from 'api/egovFetch'; + +import URL from 'constants/url'; +import CODE from 'constants/code'; +import Row from 'react-bootstrap/Row'; +import Col from 'react-bootstrap/Col'; + +import { getLocalItem, setLocalItem, setSessionItem } from 'utils/storage'; +import EgovLoginContent from "../EgovLoginContent"; +import InfoShareChk from "./InfoShareChk"; + +function Join(props) { + console.group("JoinContent"); + console.log("[Start] JoinContent ------------------------------"); + console.log("JoinContent [props] : ", props); + + const navigate = useNavigate(); + const location = useLocation(); + console.log("JoinContent [location] : ", location); + + const [userInfo, setUserInfo] = useState({ id: '', email: '', password: '' }); + const [infoShareChk, setInfoShareChk] = useState(false); + + const submitFormHandler = (e) => { + console.log("JoinContent submitFormHandler()"); + + const loginUrl = "/auth/join" + const requestOptions = { + method: "POST", + headers: { + 'Content-type': 'application/json' + }, + body: JSON.stringify(userInfo) + } + + EgovNet.requestFetch(loginUrl, + requestOptions, + (resp) => { + let resultVO = resp.resultVO; + let jToken = resp?.jToken || null; + + setSessionItem('jToken', jToken); + + + }) + } + + console.log("------------------------------JoinContent [End]"); + console.groupEnd("JoinContent"); + + const infoShareBtn = () =>{ + setInfoShareChk(true) + } + + return ( +
+
+ {/* */} +
+
    +
  • Home
  • +
  • 로그인
  • +
  • 회원가입
  • +
+
+ {/* */} + +
+
+ {/* */} +
+

회원가입

+
+ {infoShareChk?( +
+ + setUserInfo({ ...userInfo, id: e.target.value })} /> + setUserInfo({ ...userInfo, email: e.target.value })} /> + setUserInfo({ ...userInfo, password: e.target.value })} /> + setUserInfo({ ...userInfo, passwordConfirm: e.target.value })} /> + +
    +
  • 비밀번호는 6~12자의 영문 대/소문자, 숫자, 특수문자를 혼합해서 사용하실 수 있습니다.
  • +
  • 쉬운 비밀번호나 자주 쓰는 사이트의 비밀번호가 같을 경우, 도용되기 쉬우므로 주기적으로 + 변경하셔서 사용하는 것이 좋습니다.
  • +
+ +
+ ):( + + + + + + + + + )} + +
+
+ {/* */} +
+
+
+
+ ); +} + +export default Join; \ No newline at end of file diff --git a/egovframe-template-simple-react-contribution/src/routes/index.jsx b/egovframe-template-simple-react-contribution/src/routes/index.jsx index b0cd134..76831b2 100644 --- a/egovframe-template-simple-react-contribution/src/routes/index.jsx +++ b/egovframe-template-simple-react-contribution/src/routes/index.jsx @@ -12,6 +12,7 @@ import EgovError from 'components/EgovError'; import EgovMain from 'pages/main/EgovMain'; import EgovLogin from 'pages/login/EgovLogin'; +import Join from 'pages/login/join/Join'; //ABOUT import EgovAboutSite from 'pages/about/EgovAboutSite'; @@ -146,9 +147,9 @@ const SecondRoutes = () => { } /> {/* LOGIN */} - setLoginVO(user)} - />}/> + setLoginVO(user)}/>}/> + {/*{JOIN}*/} + } /> {/* ERROR */} } /> diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/EgovLoginApiController.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/EgovLoginApiController.java new file mode 100644 index 0000000..9e1d755 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/EgovLoginApiController.java @@ -0,0 +1,190 @@ +package com.dbnt.kcscbackend.auth; + +import com.dbnt.kcscbackend.auth.service.EgovLoginService; +import com.dbnt.kcscbackend.config.common.BaseController; +import com.dbnt.kcscbackend.auth.entity.LoginVO; +import com.dbnt.kcscbackend.config.common.ResponseCode; +import com.dbnt.kcscbackend.config.common.ResultVO; +import com.dbnt.kcscbackend.config.egov.EgovMessageSource; +import com.dbnt.kcscbackend.config.jwt.EgovJwtTokenUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; + +/** + * 일반 로그인을 처리하는 컨트롤러 클래스 + * @author 공통서비스 개발팀 박지욱 + * @since 2009.03.06 + * @version 1.0 + * @see + * + *
+ * << 개정이력(Modification Information) >>
+ *
+ *  수정일      수정자      수정내용
+ *  -------            --------        ---------------------------
+ *  2009.03.06  박지욱     최초 생성
+ *  2011.08.31  JJY            경량환경 템플릿 커스터마이징버전 생성
+ *
+ *  
+ */ + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/auth") +@Tag(name="EgovLoginApiController",description = "로그인 관련") +public class EgovLoginApiController extends BaseController { + + /** EgovLoginService */ + private EgovLoginService loginService; + + /** EgovMessageSource */ + @Resource(name = "egovMessageSource") + EgovMessageSource egovMessageSource; + + /** JWT */ + @Autowired + private EgovJwtTokenUtil jwtTokenUtil; + + + @Operation( + summary = "JWT 로그인", + description = "JWT 로그인 처리", + tags = {"EgovLoginApiController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그인 성공"), + @ApiResponse(responseCode = "300", description = "로그인 실패") + }) + @PostMapping(value = "/join") + public HashMap actionJoin(@RequestBody LoginVO loginVO, HttpServletRequest request) throws Exception { + HashMap resultMap = new HashMap(); + + return resultMap; + } + + /** + * 일반 로그인을 처리한다 + * @param loginVO - 아이디, 비밀번호가 담긴 LoginVO + * @param request - 세션처리를 위한 HttpServletRequest + * @return result - 로그인결과(세션정보) + * @exception Exception + */ + + @Operation( + summary = "일반 로그인", + description = "일반 로그인 처리", + tags = {"EgovLoginApiController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그인 성공"), + @ApiResponse(responseCode = "300", description = "로그인 실패") + }) + @PostMapping(value = "/login", consumes = {MediaType.APPLICATION_JSON_VALUE , MediaType.TEXT_HTML_VALUE}) + public HashMap actionLogin(@RequestBody LoginVO loginVO, HttpServletRequest request) throws Exception { + HashMap resultMap = new HashMap(); + + // 1. 일반 로그인 처리 + LoginVO loginResultVO = loginService.actionLogin(loginVO); + + if (loginResultVO != null && loginResultVO.getId() != null && !loginResultVO.getId().equals("")) { + + request.getSession().setAttribute("LoginVO", loginResultVO); + resultMap.put("resultVO", loginResultVO); + resultMap.put("resultCode", "200"); + resultMap.put("resultMessage", "성공 !!!"); + } else { + resultMap.put("resultVO", loginResultVO); + resultMap.put("resultCode", "300"); + resultMap.put("resultMessage", egovMessageSource.getMessage("fail.common.login")); + } + + return resultMap; + + } + + @Operation( + summary = "JWT 로그인", + description = "JWT 로그인 처리", + tags = {"EgovLoginApiController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그인 성공"), + @ApiResponse(responseCode = "300", description = "로그인 실패") + }) + @PostMapping(value = "/login-jwt") + public HashMap actionLoginJWT(@RequestBody LoginVO loginVO, HttpServletRequest request, ModelMap model) throws Exception { + HashMap resultMap = new HashMap(); + + // 1. 일반 로그인 처리 + LoginVO loginResultVO = loginService.actionLogin(loginVO); + + if (loginResultVO != null && loginResultVO.getId() != null && !loginResultVO.getId().equals("")) { + + log.debug("===>>> loginVO.getUserSe() = "+loginVO.getUserSe()); + log.debug("===>>> loginVO.getId() = "+loginVO.getId()); + log.debug("===>>> loginVO.getPassword() = "+loginVO.getPassword()); + + String jwtToken = jwtTokenUtil.generateToken(loginResultVO); + + String username = jwtTokenUtil.getUserSeFromToken(jwtToken); + log.debug("Dec jwtToken username = "+username); + + //서버사이드 권한 체크 통과를 위해 삽입 + //EgovUserDetailsHelper.isAuthenticated() 가 그 역할 수행. DB에 정보가 없으면 403을 돌려 줌. 로그인으로 튕기는 건 프론트 쪽에서 처리 + request.getSession().setAttribute("LoginVO", loginResultVO); + + resultMap.put("resultVO", loginResultVO); + resultMap.put("jToken", jwtToken); + resultMap.put("resultCode", "200"); + resultMap.put("resultMessage", "성공 !!!"); + + } else { + resultMap.put("resultVO", loginResultVO); + resultMap.put("resultCode", "300"); + resultMap.put("resultMessage", egovMessageSource.getMessage("fail.common.login")); + } + + return resultMap; + } + + /** + * 로그아웃한다. + * @return resultVO + * @exception Exception + */ + @Operation( + summary = "로그아웃", + description = "로그아웃 처리(JWT,일반 관계 없이)", + tags = {"EgovLoginApiController"} + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그아웃 성공"), + }) + @GetMapping(value = "/logout") + public ResultVO actionLogoutJSON(HttpServletRequest request, HttpServletResponse response) throws Exception { + + ResultVO resultVO = new ResultVO(); + + new SecurityContextLogoutHandler().logout(request, response, null); + + resultVO.setResultCode(ResponseCode.SUCCESS.getCode()); + resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage()); + + return resultVO; + } +} \ No newline at end of file diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/common/LoginVO.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/entity/LoginVO.java similarity index 97% rename from kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/common/LoginVO.java rename to kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/entity/LoginVO.java index 6f84b1d..25e339d 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/common/LoginVO.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/entity/LoginVO.java @@ -1,4 +1,4 @@ -package com.dbnt.kcscbackend.config.common; +package com.dbnt.kcscbackend.auth.entity; import io.swagger.v3.oas.annotations.media.Schema; @@ -38,7 +38,7 @@ public class LoginVO implements Serializable{ @Schema(description = "이름") private String name; - + @Schema(description = "주민등록번호") private String ihidNum; diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/mapper/LoginUserMapper.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/mapper/LoginUserMapper.java new file mode 100644 index 0000000..73411d0 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/mapper/LoginUserMapper.java @@ -0,0 +1,56 @@ +package com.dbnt.kcscbackend.auth.mapper; + +import com.dbnt.kcscbackend.auth.entity.LoginVO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 일반 로그인을 처리하는 비즈니스 구현 클래스 + * @author 공통서비스 개발팀 박지욱 + * @since 2009.03.06 + * @version 1.0 + * @see + * + *
+ * << 개정이력(Modification Information) >>
+ *
+ *   수정일      수정자          수정내용
+ *  -------    --------    ---------------------------
+ *  2009.03.06  박지욱          최초 생성
+ *  2011.08.31  JJY            경량환경 템플릿 커스터마이징버전 생성
+ *
+ *  
+ */ +@Mapper +public interface LoginUserMapper { + + /** + * 일반 로그인을 처리한다 + * @param vo LoginVO + * @return LoginVO + * @exception Exception + */ + LoginVO actionLogin(LoginVO vo); + + /** + * 아이디를 찾는다. + * @param vo LoginVO + * @return LoginVO + * @exception Exception + */ + LoginVO searchId(LoginVO vo); + + /** + * 비밀번호를 찾는다. + * @param vo LoginVO + * @return LoginVO + * @exception Exception + */ + LoginVO searchPassword(LoginVO vo); + + /** + * 변경된 비밀번호를 저장한다. + * @param vo LoginVO + * @exception Exception + */ + void updatePassword(LoginVO vo); +} diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/EgovLoginService.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/EgovLoginService.java new file mode 100644 index 0000000..44bac61 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/EgovLoginService.java @@ -0,0 +1,52 @@ +package com.dbnt.kcscbackend.auth.service; + + +import com.dbnt.kcscbackend.auth.entity.LoginVO; + +/** + * 일반 로그인을 처리하는 비즈니스 구현 클래스 + * @author 공통서비스 개발팀 박지욱 + * @since 2009.03.06 + * @version 1.0 + * @see + * + *
+ * << 개정이력(Modification Information) >>
+ *
+ *   수정일      수정자          수정내용
+ *  -------    --------    ---------------------------
+ *  2009.03.06  박지욱          최초 생성
+ *  2011.08.31  JJY            경량환경 템플릿 커스터마이징버전 생성
+ *
+ *  
+ */ +public interface EgovLoginService { + + /** + * 일반 로그인을 처리한다 + * @return LoginVO + * + * @param vo LoginVO + * @exception Exception Exception + */ + public LoginVO actionLogin(LoginVO vo) throws Exception; + + /** + * 아이디를 찾는다. + * @return LoginVO + * + * @param vo LoginVO + * @exception Exception Exception + */ + public LoginVO searchId(LoginVO vo) throws Exception; + + /** + * 비밀번호를 찾는다. + * @return boolean + * + * @param vo LoginVO + * @exception Exception Exception + */ + public boolean searchPassword(LoginVO vo) throws Exception; + +} \ No newline at end of file diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/impl/EgovLoginServiceImpl.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/impl/EgovLoginServiceImpl.java new file mode 100644 index 0000000..ad5941f --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/auth/service/impl/EgovLoginServiceImpl.java @@ -0,0 +1,125 @@ +package com.dbnt.kcscbackend.auth.service.impl; + +import com.dbnt.kcscbackend.auth.mapper.LoginUserMapper; +import com.dbnt.kcscbackend.auth.service.EgovLoginService; +import com.dbnt.kcscbackend.auth.entity.LoginVO; +import com.dbnt.kcscbackend.config.egov.EgovFileScrty; +import com.dbnt.kcscbackend.config.util.EgovNumberUtil; +import com.dbnt.kcscbackend.config.util.EgovStringUtil; +import lombok.RequiredArgsConstructor; +import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 일반 로그인을 처리하는 비즈니스 구현 클래스 + * @author 공통서비스 개발팀 박지욱 + * @since 2009.03.06 + * @version 1.0 + * @see + * + *
+ * << 개정이력(Modification Information) >>
+ *
+ *   수정일      수정자          수정내용
+ *  -------    --------    ---------------------------
+ *  2009.03.06  박지욱          최초 생성
+ *  2011.08.31  JJY            경량환경 템플릿 커스터마이징버전 생성
+ *
+ *  
+ */ +@Service +@Transactional +@RequiredArgsConstructor +public class EgovLoginServiceImpl extends EgovAbstractServiceImpl implements EgovLoginService { + + private final LoginUserMapper loginMapper; + + /** + * 일반 로그인을 처리한다 + * @param vo LoginVO + * @return LoginVO + * @exception Exception + */ + @Override + public LoginVO actionLogin(LoginVO vo) throws Exception { + + // 1. 입력한 비밀번호를 암호화한다. + String enpassword = EgovFileScrty.encryptPassword(vo.getPassword(), vo.getId()); + vo.setPassword(enpassword); + + // 2. 아이디와 암호화된 비밀번호가 DB와 일치하는지 확인한다. + LoginVO loginVO = loginMapper.actionLogin(vo); + + // 3. 결과를 리턴한다. + if (loginVO != null && !loginVO.getId().equals("") && !loginVO.getPassword().equals("")) { + return loginVO; + } else { + loginVO = new LoginVO(); + } + + return loginVO; + } + + /** + * 아이디를 찾는다. + * @param vo LoginVO + * @return LoginVO + * @exception Exception + */ + @Override + public LoginVO searchId(LoginVO vo) throws Exception { + + // 1. 이름, 이메일주소가 DB와 일치하는 사용자 ID를 조회한다. + LoginVO loginVO = loginMapper.searchId(vo); + + // 2. 결과를 리턴한다. + if (loginVO != null && !loginVO.getId().equals("")) { + return loginVO; + } else { + loginVO = new LoginVO(); + } + + return loginVO; + } + + /** + * 비밀번호를 찾는다. + * @param vo LoginVO + * @return boolean + * @exception Exception + */ + @Override + public boolean searchPassword(LoginVO vo) throws Exception { + + boolean result = true; + + // 1. 아이디, 이름, 이메일주소, 비밀번호 힌트, 비밀번호 정답이 DB와 일치하는 사용자 Password를 조회한다. + LoginVO loginVO = loginMapper.searchPassword(vo); + if (loginVO == null || loginVO.getPassword() == null || loginVO.getPassword().equals("")) { + return false; + } + + // 2. 임시 비밀번호를 생성한다.(영+영+숫+영+영+숫=6자리) + String newpassword = ""; + for (int i = 1; i <= 6; i++) { + // 영자 + if (i % 3 != 0) { + newpassword += EgovStringUtil.getRandomStr('a', 'z'); + // 숫자 + } else { + newpassword += EgovNumberUtil.getRandomNum(0, 9); + } + } + + // 3. 임시 비밀번호를 암호화하여 DB에 저장한다. + LoginVO pwVO = new LoginVO(); + String enpassword = EgovFileScrty.encryptPassword(newpassword, vo.getId()); + pwVO.setId(vo.getId()); + pwVO.setPassword(enpassword); + pwVO.setUserSe(vo.getUserSe()); + loginMapper.updatePassword(pwVO); + + return result; + } +} \ No newline at end of file diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/egov/EgovFileScrty.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/egov/EgovFileScrty.java new file mode 100644 index 0000000..98f3c43 --- /dev/null +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/egov/EgovFileScrty.java @@ -0,0 +1,305 @@ +package com.dbnt.kcscbackend.config.egov; + +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.util.codec.binary.Base64; + +import java.io.*; +import java.security.MessageDigest; + +/** + * Base64인코딩/디코딩 방식을 이용한 데이터를 암호화/복호화하는 Business Interface class + * @author 공통서비스개발팀 박지욱 + * @since 2009.01.19 + * @version 1.0 + * @see + * + *
+ * << 개정이력(Modification Information) >>
+ *
+ *   수정일      수정자           수정내용
+ *  -------    --------    ---------------------------
+ *   2009.01.19  박지욱          최초 생성
+ *   2011.08.31  JJY            경량환경 템플릿 커스터마이징버전 생성
+ *
+ * 
+ */ +@Slf4j +public class EgovFileScrty { + // 파일구분자 + static final String FILE_SEPARATOR = System.getProperty("file.separator"); + // 버퍼사이즈 + static final int BUFFER_SIZE = 1024; + + /** + * 파일을 암호화하는 기능 + * + * @param source 암호화할 파일 + * @param target 암호화된 파일 + * @return boolean result 암호화여부 True/False + * @exception Exception + */ + public static boolean encryptFile(String source, String target) throws Exception { + + // 암호화 여부 + boolean result = false; + + String sourceFile = EgovWebUtil.filePathBlackList(source.replace("\\", FILE_SEPARATOR).replace("/", FILE_SEPARATOR)); + String targetFile = EgovWebUtil.filePathBlackList(target.replace("\\", FILE_SEPARATOR).replace("/", FILE_SEPARATOR)); + File srcFile = new File(sourceFile); + + BufferedInputStream input = null; + BufferedOutputStream output = null; + + byte[] buffer = new byte[BUFFER_SIZE]; + + try { + if (srcFile.exists() && srcFile.isFile()) { + + input = new BufferedInputStream(new FileInputStream(srcFile)); + output = new BufferedOutputStream(new FileOutputStream(targetFile)); + + int length = 0; + while ((length = input.read(buffer)) >= 0) { + byte[] data = new byte[length]; + System.arraycopy(buffer, 0, data, 0, length); + output.write(encodeBinary(data).getBytes()); + output.write(System.getProperty("line.separator").getBytes()); + } + + result = true; + } + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException ignore) { + log.debug("IGNORE: {}", ignore); + } + } + if (output != null) { + try { + output.close(); + } catch (IOException ignore) { + log.debug("IGNORE: {}", ignore); + } + } + } + return result; + } + + /** + * 파일을 복호화하는 기능 + * + * @param source 복호화할 파일 + * @param target 복호화된 파일 + * @return boolean result 복호화여부 True/False + * @exception Exception + */ + public static boolean decryptFile(String source, String target) throws Exception { + + // 복호화 여부 + boolean result = false; + + String sourceFile = source.replace("\\", FILE_SEPARATOR).replace("/", FILE_SEPARATOR); + String targetFile = target.replace("\\", FILE_SEPARATOR).replace("/", FILE_SEPARATOR); + File srcFile = new File(sourceFile); + + BufferedReader input = null; + BufferedOutputStream output = null; + + //byte[] buffer = new byte[BUFFER_SIZE]; + String line = null; + + try { + if (srcFile.exists() && srcFile.isFile()) { + + input = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile))); + output = new BufferedOutputStream(new FileOutputStream(targetFile)); + + while ((line = input.readLine()) != null) { + byte[] data = line.getBytes(); + output.write(decodeBinary(new String(data))); + } + + result = true; + } + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException ignore) { + log.debug("IGNORE: {}", ignore); + } + } + if (output != null) { + try { + output.close(); + } catch (IOException ignore) { + log.debug("IGNORE: {}", ignore); + } + } + } + return result; + } + + /** + * 데이터를 암호화하는 기능 + * + * @param data 암호화할 데이터 + * @return String result 암호화된 데이터 + * @exception Exception + */ + public static String encodeBinary(byte[] data) throws Exception { + if (data == null) { + return ""; + } + + return new String(Base64.encodeBase64(data)); + } + + /** + * 데이터를 암호화하는 기능 + * + * @param data 암호화할 데이터 + * @return String result 암호화된 데이터 + * @exception Exception + */ + public static String encode(String data) throws Exception { + return encodeBinary(data.getBytes()); + } + + /** + * 데이터를 복호화하는 기능 + * + * @param data 복호화할 데이터 + * @return String result 복호화된 데이터 + * @exception Exception + */ + public static byte[] decodeBinary(String data) throws Exception { + return Base64.decodeBase64(data.getBytes()); + } + + /** + * 데이터를 복호화하는 기능 + * + * @param String data 복호화할 데이터 + * @return String result 복호화된 데이터 + * @exception Exception + */ + public static String decode(String data) throws Exception { + return new String(decodeBinary(data)); + } + + /** + * 비밀번호를 암호화하는 기능(복호화가 되면 안되므로 SHA-256 인코딩 방식 적용). + * + * deprecated : 보안 강화를 위하여 salt로 ID를 지정하는 encryptPassword(password, id) 사용 + * + * @param data 암호화할 비밀번호 + * @return String result 암호화된 비밀번호 + * @exception Exception + */ + @Deprecated + public static String encryptPassword(String data) throws Exception { + + if (data == null) { + return ""; + } + + byte[] plainText = null; // 평문 + byte[] hashValue = null; // 해쉬값 + plainText = data.getBytes(); + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + + // 변경 시 기존 hash 값에 검증 불가.. => deprecated 시키고 유지 + /* + // Random 방식의 salt 추가 + SecureRandom ng = new SecureRandom(); + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + + md.reset(); + md.update(randomBytes); + + */ + hashValue = md.digest(plainText); + + /* + BASE64Encoder encoder = new BASE64Encoder(); + return encoder.encode(hashValue); + */ + return new String(Base64.encodeBase64(hashValue)); + } + + /** + * 비밀번호를 암호화하는 기능(복호화가 되면 안되므로 SHA-256 인코딩 방식 적용) + * + * @param password 암호화될 패스워드 + * @param id salt로 사용될 사용자 ID 지정 + * @return + * @throws Exception + */ + public static String encryptPassword(String password, String id) throws Exception { + + if (password == null) { + return ""; + } + + byte[] hashValue = null; // 해쉬값 + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + + md.reset(); + md.update(id.getBytes()); + + hashValue = md.digest(password.getBytes()); + + return new String(Base64.encodeBase64(hashValue)); + } + + /** + * 비밀번호를 암호화하는 기능(복호화가 되면 안되므로 SHA-256 인코딩 방식 적용) + * @param data 암호화할 비밀번호 + * @param salt Salt + * @return 암호화된 비밀번호 + * @throws Exception + */ + public static String encryptPassword(String data, byte[] salt) throws Exception { + + if (data == null) { + return ""; + } + + byte[] hashValue = null; // 해쉬값 + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + + md.reset(); + md.update(salt); + + hashValue = md.digest(data.getBytes()); + + return new String(Base64.encodeBase64(hashValue)); + } + + /** + * 비밀번호를 암호화된 패스워드 검증(salt가 사용된 경우만 적용). + * + * @param data 원 패스워드 + * @param encoded 해쉬처리된 패스워드(Base64 인코딩) + * @return + * @throws Exception + */ + public static boolean checkPassword(String data, String encoded, byte[] salt) throws Exception { + byte[] hashValue = null; // 해쉬값 + + MessageDigest md = MessageDigest.getInstance("SHA-256"); + + md.reset(); + md.update(salt); + hashValue = md.digest(data.getBytes()); + + return MessageDigest.isEqual(hashValue, Base64.decodeBase64(encoded.getBytes())); + } +} \ No newline at end of file diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java index f69a140..83c58c0 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/EgovJwtTokenUtil.java @@ -2,7 +2,7 @@ package com.dbnt.kcscbackend.config.jwt; import com.dbnt.kcscbackend.config.egov.EgovProperties; -import com.dbnt.kcscbackend.config.common.LoginVO; +import com.dbnt.kcscbackend.auth.entity.LoginVO; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/JwtAuthenticationFilter.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/JwtAuthenticationFilter.java index 16ef3a5..1a830ee 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/JwtAuthenticationFilter.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/jwt/JwtAuthenticationFilter.java @@ -1,6 +1,6 @@ package com.dbnt.kcscbackend.config.jwt; -import com.dbnt.kcscbackend.config.common.LoginVO; +import com.dbnt.kcscbackend.auth.entity.LoginVO; import com.dbnt.kcscbackend.config.util.EgovStringUtil; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.MalformedJwtException; diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomAuthenticationPrincipalResolver.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomAuthenticationPrincipalResolver.java index 733e86e..e57d0c5 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomAuthenticationPrincipalResolver.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/CustomAuthenticationPrincipalResolver.java @@ -1,6 +1,6 @@ package com.dbnt.kcscbackend.config.security; -import com.dbnt.kcscbackend.config.common.LoginVO; +import com.dbnt.kcscbackend.auth.entity.LoginVO; import org.springframework.core.MethodParameter; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java index a7768fc..4a242fa 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/config/security/SecurityConfig.java @@ -44,6 +44,7 @@ public class SecurityConfig { "/login/**", "/auth/login-jwt",//JWT 로그인 "/auth/login",//일반 로그인 + "/auth/join",//회원가입 "/cmm/main/**.do", // 메인페이지 "/cmm/fms/FileDown.do", //파일 다운로드 "/cmm/fms/getImage.do", //갤러리 이미지보기 diff --git a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/standardCode/StandardCodeController.java b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/standardCode/StandardCodeController.java index d21da89..63f427b 100644 --- a/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/standardCode/StandardCodeController.java +++ b/kcsc-back-end/src/main/java/com/dbnt/kcscbackend/standardCode/StandardCodeController.java @@ -1,7 +1,7 @@ package com.dbnt.kcscbackend.standardCode; import com.dbnt.kcscbackend.config.common.BaseController; -import com.dbnt.kcscbackend.config.common.LoginVO; +import com.dbnt.kcscbackend.auth.entity.LoginVO; import com.dbnt.kcscbackend.config.common.ResponseCode; import com.dbnt.kcscbackend.config.common.ResultVO; import com.dbnt.kcscbackend.standardCode.entity.TnDocumentCodeList; diff --git a/kcsc-back-end/src/main/resources/mybatisMapper/LoginUserMapper.xml b/kcsc-back-end/src/main/resources/mybatisMapper/LoginUserMapper.xml new file mode 100644 index 0000000..182a296 --- /dev/null +++ b/kcsc-back-end/src/main/resources/mybatisMapper/LoginUserMapper.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + UPDATE LETTNGNRLMBER + SET password = #{password} + WHERE mber_id = #{id} + + + + + + UPDATE LETTNENTRPRSMBER + SET entrprs_mber_password = #{password} + WHERE entrprsmber_id = #{id} + + + + + + UPDATE LETTNEMPLYRINFO + SET password = #{password} + WHERE emplyr_id = #{id} + + + + \ No newline at end of file