YJ의 새벽

JDBC 활용 연습용3. ( 암호화 (로그인)) 본문

SelfStudy/JDBC

JDBC 활용 연습용3. ( 암호화 (로그인))

YJDawn 2023. 3. 27. 16:36

jbufdyvxikcbxutr

jbufdyvxikcbxutr

 

-- SHA-512  ( 해시함수 ) 암호화 함수 .

-- 어떤 문자열이든 일정한 길이의 무작위 문자열로 변환하는 함수 ( 중복X )

    ex  ) pass01!  --->  sdg454 

                a         ---> 4sd4g1 

 

 

pstmt  :  sql 문이 담겨있음.

stmt  :  sql문에 담겨있지않음.

 

 

  • Cookie  :  클라이언트(브라우저) 에서 관리하는 파일

-- 특정주소 요청시마다 해당주소와 연관된 쿠키 파일을 브라우저가 알아서 읽어옴.

  --> 읽어온 쿠키 파일 내용을 서버에 같이 전달.

-- 생성, 사용방법

  1) 서버가 요청에 대한 응답을 할 때 쿠키를 생성한 후

      응답에 쿠키를 담아서 클라이언트에 전달

  2) 응답에 담긴 쿠키가 클라이언트에 파일 형태로 저장

  3) 이후 특정 주소 요청시 쿠키 파일을 브라우저가 찾아 자동으로 요청에 실어서 보냄

  4) 서버는 요청에 실려온 쿠키 파일을 사용함

 

 

 

  • forword ( 요청위임 )

-- Servlet 으로 응답화면 만들기가 불편해서 . 

    JSP 로 req, resp 객체 위임하여 요청에 대한 응답화면 대신 만든다.

 ex )  아메리카노 아이스 주세요 ~  -->  주문받음   -->  바리스타가 만든커피

                  클라이언트          -->     캐셔 (Servlet)   -->    응답결과  (JSP)

 

  • redirect ( 재요청 )

-- 현재 Servlet에서 응답페이지를 만들지 않고, 

    응답페이지를 만들수 있는 다른 요청의 주소로 클라이언트를 이동시킴 ( 재요청 )

-- 클라이언트 재요청

   -->  기존 HttpServletRequest / Response 제거

   --> 새로운 HttpServletRequest / Response 생성

-- redirect 시 request 객체가 유지되지 않기때문에

   특정 데이터를 전달하거나 유지하고싶으면

   session 또는 application 스코프(범위) 에 세팅해야한다 . !! 

ex )    팝콘주세요 ~        -->  팝콘파는 위치 알려줌    -->  팝콘 파는곳으로 이동

          클라이언트       -->       캐셔 (Servlet)            -->      클라이언트 다른주소 재요청

 

 

 

 

 

 

--  member-sql.xml  에  'login' sql문 추가

<entry key="login">
	SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICK, MEMBER_TEL,
	MEMBER_ADDR, PROFILE_IMG,
	TO_CHAR(ENROLL_DT, 'YYYY-MM-DD HH24:MI:SS') AS ENROLL_DT
	FROM MEMBER
	WHERE MEMBER_EMAIL = ?
	AND MEMBER_PW = ?
	AND SECESSION_FL = 'N'
</entry>

 

 

-- DB 에서 연습용 테이블 변경.

--MEMBER TABLE 비밀번호 컬럼크기 변경
ALTER TABLE MEMBER
MODIFY MEMBER_PW VARCHAR2(100);

-- 암호화된 비밀번호로 업데이트
UPDATE MEMBER SET
MEMBER_PW = 'AWK1nM+CjAOElicQOH742Hgdbvccpnn/sChKmDsie2JcQEGbfWqKblQ4JrLuN2ZtAVwTr3NTJqoC/05NxrR74g=='
WHERE MEMBER_NO = 3;
COMMIT;

 

 

 

-- 기존 index.jsp  회원가입 폼 .   ( member/login )  (/community/member/logout)

    <section class="content-2">

        <%-- if/else  --%>
        <c:choose>

            <%--로그인이 되어있지 않은경우 --%>
            <c:when test="${ empty sessionScope.loginMember}">
                <form action="member/login" method="POST" name="login-form"
                    onsubmit="return loginValidate()">

                    <fieldset id="id-pw-area">
                        <section>
                            <input type="text" name="inputEmail" placeholder="이메일"
                                autocomplete="off" value="${cookie.saveId.value}"> 
                                <input type="password" name="inputPw" placeholder="비밀번호">
                        </section>

                        <section>
                            <button>로그인</button>
                        </section>
                    </fieldset>

                    <label> <input type="checkbox" name="saveId"> 아이디
                        저장
                    </label>


                    <%-- 회원가입 / ID/PW 찾기 --%>
                    <article id="signUp-find-area">
                        <a href="#">회원가입</a> <span>|</span> <a href="#">ID/PW찾기</a>
                    </article>
                </form>
            </c:when>

            <%--로그인이 된경우 --%>
            <c:otherwise>
                <article class="login-area">
                    <!-- 회원 프로필 이미지 -->
                    <a
                        href="${pageContext.request.contextPath}/member/myPage/profile">

                        <c:if test="${empty loginMember.profileImage}">
                            <img
                                src="${pageContext.request.contextPath}/resources/images/user.png"
                                id="member-profile">
                        </c:if> <c:if test="${!empty loginMember.profileImage}">
                            <img
                                src="${pageContext.request.contextPath}${loginMember.profileImage}"
                                id="member-profile">
                        </c:if>

                    </a>

                    <!-- 회원 정보 + 로그아웃 버튼 -->
                    <div class="my-info">
                        <div>
                            <a href="${pageContext.request.contextPath}/member/myPage/info"
                                id="nickname">${loginMember.memberNickname}</a> <a
                                href="/community/member/logout" id="logout-btn">로그아웃</a>
                        </div>

                        <p>${loginMember.memberEmail}</p>
                    </div>
                </article>
            </c:otherwise>

        </c:choose>

    </section>

 

 

-- LoginServlet.class   (  "/member/login" );


@WebServlet("/member/login")
public class LoginServlet extends HttpServlet{
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		// 전달된 파라미터 변수에 저장
		String inputEmail = req.getParameter("inputEmail");
		String inputPw = req.getParameter("inputPw");
		
		System.out.println("inputEmail : "+ inputEmail);
		System.out.println("inputPw : "+ inputPw);
		
		Member mem = new Member();
		mem.setMemberEmail(inputEmail);
		mem.setMemberPw(inputPw);
		
		try {
			// 서비스객체 생성
			MemberService service = new MemberService();
			
			// 이메일 , 비밀번호 일치하는 회원 조회하는 서비스 호출후 결과반환 받기
			Member loginMember = service.login(mem);
			
			// ** 로그인 **
			// ID/PW 가 일치하는 회원정보를 Session scope 에 세팅 하는것
			// Session 객체 얻어오기
			HttpSession session = req.getSession();
			
			if( loginMember != null ) { // 성공
				// 회원정보 Session 세팅
				session.setAttribute("loginMember", loginMember);
				// 특정시간동안 요청없으면 세션만료
				session.setMaxInactiveInterval(3600);  // 3600 초
				
				// ==================================================
				// 쿠키 객체 생성
				Cookie c = new Cookie("saveId",inputEmail);
				
				//아이디 저장이 체크된 경우
				if( req.getParameter("saveId") != null) {
					// 쿠키파일 30일동안 유지
					c.setMaxAge( 60*60*24*30 );  // 30일 (1초단위)
				}else {
					// 쿠키파일 0초 동안 유지   (삭제)
					// --> 기존에 존재하던 쿠키파일의 유지시간을 0초로 덮어씌움
					c.setMaxAge(0);
				}
				
				//해당 쿠키파일이 적용될 주소를 지정
				c.setPath(req.getContextPath());    // 최상위 주소 ( /community)
				
				// 응답 객체를 이용해서 클라이언트로 전달
				resp.addCookie(c);
				// ==================================================
				
			}else { // 실패
				session.setAttribute("message","아이디 또는 비밀번호가 일치하지 않습니다.");
			}
			resp.sendRedirect( req.getContextPath() );  // 최상위주소 ( /community)
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

 

-- MemberService.class   (Member mem)

	/** 로그인 서비스
	 * @param mem
	 * @return
	 * @throws Exception 
	 */
	public Member login(Member mem) throws Exception {
		
		// Connection 얻어오기
		Connection conn = getConnection();
		// DAO 수행
		Member loginMember = dao.login(conn , mem);
		// Connection 반환
		close(conn);
		// 결과 반환
		return loginMember;
	}
}

 

-- MemberDAO.class  ( Connection conn, Member mem )

	/** 로그인 DAO
	 * @param conn
	 * @param mem
	 * @return loginMember
	 */
	public Member login(Connection conn, Member mem) throws Exception {
		// 결과 저장용 변수 선언
		Member loginMember = null;
		
		try {
			// SQL 얻어오기
			String sql = prop.getProperty("login");
			
			// PreparedStatement 생성 및 SQL 적재
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, mem.getMemberEmail());
			pstmt.setString(2, mem.getMemberPw());
			
			//SQL 수행
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				loginMember = new Member();

				loginMember.setMemberNo(  rs.getInt("MEMBER_NO") );
				loginMember.setMemberEmail( rs.getString("MEMBER_EMAIL") );
				loginMember.setMemberNickname( 	rs.getString("MEMBER_NICK")	 );
				loginMember.setMemberTel( 	rs.getString("MEMBER_TEL") 	 );
				loginMember.setMemberAddress( rs.getString("MEMBER_ADDR")  );
				loginMember.setProfileImage( rs.getString("PROFILE_IMG")  );
				loginMember.setEnrollDate( 	rs.getString("ENROLL_DT") 	 );
			}
		}finally {
			close(rs);
			close(pstmt);
		}
		return loginMember; // null 또는 Member 객체주소
	}
}

 

 

-- EncryptFilter.class  ( 암호화 필터 클래스 ) 

   -- 이클립스 제공하는 . filter 클래스 재정의.

// 암호화를 적용해야하는 요청 : 로그인,회원가입,비밀번호변경,회원탈퇴

// 필터가 적용될 url이 여러개인경우 : String 배열 초기화형태 {} 로 작성

@WebFilter(filterName = "encryptFilter",
        urlPatterns = {	"/member/login",
                        "/member/signUp",
                        "/member/myPage/changePw",
                        "/member/myPage/secession"
			})
public class EncryptFilter extends HttpFilter implements Filter {
       
	public void init(FilterConfig fConfig) throws ServletException {
	}
	public void destroy() {
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

		// 비밀번호는 파라미터 -> HttpServletRequest 에 담겨있다.
		// doFilter 메서드 매개변수는 부모타입인 ServletRequest 에 담겨있음.
		//  --> 다운캐스팅
		HttpServletRequest req = (HttpServletRequest)request;
		
		// 파라미터를 다시 세팅하는방법은 필터에서 불가능함 !! 
		// --> Servlet의 Wrapper 클래스를 이용해서
		// HttpServletRequest의 메서드를 오버라이딩 할 수 있다.
		//    --> 오버라이딩(재정의) 를 통해 비밀번호 암호화 진행.
		
		EncryptWrapper wrapper = new EncryptWrapper(req);
		
		//request -> wrapper 전달
		chain.doFilter(wrapper, response);
	}
}

 

 

-- EncryptWrapper.class  ( 암호화 Wrapper 클래스 )

public class EncryptWrapper extends HttpServletRequestWrapper{
	// HttpServletRequestWrapper
	// - 클라이언트 요청 객체 HttpServletRequest를 오버라이딩하는 방법을 제공하는 클래스
	
	// 생성자가 작성되어있으면
	// 컴파일러가 기본생성자 자동으로 추가 안해줌.
	
	// -> Wrapper 클래스 생성시 반드시 HttpServletRequest 객체를 매개변수로 전달해야함 !!
	public EncryptWrapper( HttpServletRequest request ) {
		super(request);
	}
	
	// getParameter() 오버라이딩
	@Override
	public String getParameter(String name) {
		// 매개변수 name : input 태그의 name속성값
		// super.getParameter(name) : 기존 getParameter() 메서드
		
		String value = null;
		
		switch(name){
		case "inputPw" : 
		case "memberPw" :
		case "currentPw" : 
		case "newPw" : 
			value = getSha512(super.getParameter(name));
			break;
			
			//암호화가 되지않으면 기존 getParameter() 메서드형태 유지
		default : value = super.getParameter(name);	
		}
		return value;
	}
	
	// 암호화 메서드 ( SHA-512 해시함수 )
	// 해시함수 : 어떤 문자열이든 일정한 길이의
	
	private String getSha512(String pw) {
		// 매개변수 pw  :  암호화 되기 전 비밀번호
		
		// 암호화된 비밀번호 저장변수
		String encryptPw = null;
		
		// 1. 해시함수를 수행할 객체를 참조할 변수 선언
		MessageDigest md = null;
		
		try {
			// 2. SHA-512 방식의 해시함수를 수행할 수 있는 객체를 얻어옴
			md = MessageDigest.getInstance("SHA-512");
			
			// 3. md 를 이용해 암호화를 진행할 수 있도록 pw를 byte[] 형태변환
			byte[] bytes = pw.getBytes(Charset.forName("UTF-8"));
			
			// 4. 암호화 수행 --> 암호화 결과가 md 내부에 저장됨
			md.update(bytes);
			
			// 5. 암호화된 비밀번호를 encryptPw에 대입
			//   --> byte[] 를 String 변환할 필요가 있음.
			//  Base64 : byte 코드를 문자열로 변환하는 객체.
			encryptPw = Base64.getEncoder().encodeToString(md.digest());
					
			System.out.println("암호화 전 : "+pw);
			System.out.println("암호화 후 : "+encryptPw);
		}catch(NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return encryptPw;
	}
}

 

--LogoutServlet.class  ( /member/logout )

@WebServlet ("/member/logout")
public class LogoutServlet extends HttpServlet{
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		//** 로그아웃 **
		// Session Scope에 세팅된 회원 정보를 없앰
		
		// Session 얻어오기
		HttpSession session = req.getSession();
		
		// 1) Session 에서 회원 정보만 없앰
		//session.removeAttribute("loginMember");
		
		// 2) Session 전체를 없애고 새로운 Session 만들기 ( 더많이 사용되는방법)
		session.invalidate();   // 세션 무효화 --> 현재세션 없앰 --> 자동으로 새로운세션 생성됨
	
		// 메인페이지로 돌아가기
		resp.sendRedirect(req.getContextPath());
	}
}

 

 

 

 

 

 

아이디입력, 패스워드입력시

 

 

 

로그인성공

 

 

 

Comments