• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java EE 정리 08

07 Mar 2019

Reading time ~4 minutes

Java EE 정리 08 - 암호화, DBCP를 이용한 회원가입


암호화

  • 입력한 값들을 담은 form을 갖고 요청을 보낼 때 중요 정보를 탈취하는 “중간자 공격”을 막고자 수행
  • java.security 패키지에서 제공
  • SHA
    • 일방향 Hash 알고리즘
      • 처리된 데이터는 유일성을 가지며 원래의 문자열로 돌아갈 수 없다.
    • MD2, MD5, SHA-1, SHA-224, SHA-512, PGP ->갈수록 키가 길어진다.
      • MD - MessageDigest
      • SHA - Secure Hash Algorithm
  • 암호화(PKI, Public Key Infrastructure)
    • 암호화된 문자열 키를 가지고 복호화 할 수 있는 것
    • DES, RSA
// 1. 알고리즘을 설정하여 객체를 얻는다.
MessageDigest md = MessageDigest.getInstance("알고리즘"); 
// MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512

// 2. 평문(Plain Text)을 알고리즘에 해당하는 문자열로 변경
String text = "이히히";
md.update(text.getBytes());

// 3. 변경된 문자열을 얻는다.
new String(md.digest()); // digest()는 byte[]을 반환

01

public class ShaUtil {
     public static String shaEncoding(String plainText) {
          String cipherText = "";
          
          if (plainText != null || !"".equals(plainText)) {
               try {
                    // 1. 알고리즘을 사용할 수 있는 개체를 얻는다.
                    MessageDigest md =  MessageDigest.getInstance("MD5");
                    
                    // 2. 평문(Plain Text)를 암호문(Cipher Text)로  변환
                    md.update(plainText.getBytes());
                    
                    // 3. 변환된 암호문 (Cipher Text) 얻기
                    cipherText = new String(md.digest());
                    
               } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
               }
          }
          
          return cipherText;
     }
     public static void main(String[] args) {
          System.out.println(shaEncoding("ABCD"));
     }
}

02

...
private static Base64 base64 = new Base64(); // tomcat-util에 있는 Base 64 클래스

public static String shaEncoding(String plainText) {
     String cipherText = "";
     
     if (plainText != null || !"".equals(plainText)) {
          try {
               // 1. 알고리즘을 사용할 수 있는 개체를 얻는다.
               MessageDigest md =  MessageDigest.getInstance("MD5");
               
               // 2. 평문(Plain Text)를 암호문(Cipher Text)로  변환
               md.update(plainText.getBytes());
               
               // 3. 변환된 암호문 (Cipher Text) 얻기 , 알아볼 수 있도록 Base64 객체로 인코딩
               cipherText = new  String(base64.encode(md.digest())); 
               ...

03

DBCP를 이용한 회원가입

  • 0218 교육내용(회원가입, 팝업 페이지)을 재사용
  • 코드가 긴 관계로 링크만 추가
  • servlet_prj/src
  • servlet_prj/WebContent/web_member

  • 전체 흐름
    • 클라이언트가 member_join.html 요청
    • 서버가 페이지 응답
    • 클라이언트가 아이디 중복확인, 팝업페이지 요청, 응답받음
      • 팝업페이지에 중복확인 버튼 클릭 시 IdDuplication Servlet 요청
        • GET 방식으로 요청
      • DB서버와 통신 후 존재하는여부 판단, 응답하여 “이미 사용중” 또는 “사용가능” 표시
      • “사용가능” 시 사용버튼 클릭하면 부모창 아이디 input으로 값 전달 후 창 종료
    • 나머지 필수 회원정보 입력 후 가입 버튼 클릭 시 WebMemberJoin Servlet 요청
      • POST 방식으로 요청
    • Servlet에서 쿼리스트링에 있는 Parameter를 사용하여 검증 후 DB에 추가한 후 완료 페이지 응답
      • 비연결성 문제 발생

06

  • 테이블 설계서 작성

04

  • 테이블 생성
CREATE TABLE web_member(
id        VARCHAR2(20) CONSTRAINT pk_web_member PRIMARY KEY,    
pass      VARCHAR2(200)    NOT NULL,
name      VARCHAR2(60) NOT NULL,
ssn       VARCHAR2(200) NOT NULL,
gender    CHAR(6) NOT NULL,
birth     VARCHAR2(10) NOT NULL,
age       NUMBER(3)    NOT NULL,
marriage  CHAR(6)    NOT NULL,
marriage_date  VARCHAR2(10) NULL,
zipcode   CHAR(5)    NOT NULL,
addr1     VARCHAR2(300)    NOT NULL,
addr2     VARCHAR2(300)    NOT NULL,
greeting  VARCHAR2(4000) NOT NULL,
input_date     DATE    DEFAULT SYSDATE,        
ip_addr   VARCHAR2(15) NOT NULL,
);

CREATE TABLE web_lang(
id    VARCHAR2(20) CONSTRAINT fk_web_lang_id REFERENCES web_member(id),
lang VARCHAR2(7)    not null
);

COMMIT;
  • 클래스 다이어그램

05

  • 중복 체크 실행모습

07

08

웹에서 발생가능한 문제들

09

10

  • Web Browser에서 커서를 가질 수 있는 HTML Form Control이 하나인 경우 엔터키를 치면 JavaScript의 유효성 검증과 상관없이 submit이 된다.
    • ”” 처리를 BackEnd(Servlet)에서 수행한다.(방어코드)
if (id != null && !"".equals(id)) { // 파라미터가 있는 경우, 아이디가 ""가 아닌 경우 처리
  • 비연결성 문제
    • 웹은 비연결성이기 때문에 중복확인한 동일한 아이디로 모든 여러 유저가 가입시도를 할 수 있음.
    • 가장 먼저 가입버튼을 누른 사람만 처리해야 된다.
    • 회원정보를 insert 하기 전 아이디를 먼저 사용중인지 다시 체크한 후 insert 수행
public class WebMemberJoin extends HttpServlet {
     protected void doPost(HttpServletRequest request,  HttpServletResponse response) throws ServletException,  IOException {
         
         response.setContentType("text/html;charset=UTF-8"); // MIME Type 설정
         PrintWriter out = response.getWriter();

         //post방식 한글처리
         request.setCharacterEncoding("UTF-8");

         //사용자가 입력한 값(HTML Form Control)을 받기(getParameter)
         String id=request.getParameter("id");
         String pass1=request.getParameter("pass1");
         String name=request.getParameter("name");
         String ssn1=request.getParameter("ssn1");
         String ssn2=request.getParameter("ssn2");
         String gender=request.getParameter("gender");
         String b_year=request.getParameter("b_year");
         String  b_month=request.getParameter("b_month");
         String b_day=request.getParameter("b_day");
         String age=request.getParameter("age");
         String[]  interestlang=request.getParameterValues("interestlang");
         String  marriageFlag=request.getParameter("marriageFlag");
         String m_year=request.getParameter("m_year");
         String  m_month=request.getParameter("m_month");
         String m_day=request.getParameter("m_day");
         String  zipcode=request.getParameter("zipcode");
         String addr1=request.getParameter("addr1");
         String addr2=request.getParameter("addr2");
         String  greetings=request.getParameter("greetings");
         
         // DB에 데이터를 저장하기 위해 파라메터값을 VO에 저장, 비밀번호와 ssn은 일방향 해쉬알고리즘으로 암호화해서 저장
         // 암호화로 중간자 공격을 막을 수 있다. DB정보를 빼가더라도 암호화된 상태로 저장되기 때문에 안전
         // DAO에서는 DB작업 처리만 집중하도록 해야 한다. 
         // DAO보다 암호화는 DAO를 쓰는쪽(Servlet, Controller, Business Logic을 처리하는 곳)에서 처리
         WebMemberVO wm_vo = new WebMemberVO(id,  ShaUtil.shaEncoding(pass1), name,  ShaUtil.shaEncoding(ssn1+"-"+ssn2),
                  gender,  b_year+"-"+b_month+"-"+b_day, age, marriageFlag,  m_year+"-"+m_month+"-"+m_day,
                  zipcode, addr1, addr2, greetings,  request.getRemoteAddr(), interestlang); // 접속자의 IP를 얻는 getRemoteAddr()
         
         ...
         try {
             // 웹은 비연결성
             // 비연결성의 문제 - 중복확인한 동일한 아이디로 모든 여러 유저가 가입시도를 할 수 있다!
             // 비연결성에 대한 해결코드를 준비를 해야 한다! (비연결성 문제가 발생할 가능성이 있다면 재조회 후 처리)
             // 회원 정보를 insert 전에 아이디를 사용중인지 다시 체크한 후 insert!
             addWebMember(wm_vo);
             out.write("\t<div  id=\"resultTitle\">\r\n");
             out.write("\t\t<span>");
             out.print(name);
             out.print("</span>님께서 입력하신 정보는  아래와 같습니다.\r\n");
             out.write("\t</div>\r\n");
             out.write("\t<div  id=\"resultInput\">\r\n");
             out.write("\t\t<ul>\r\n");
             out.write("\t\t\t<li>아이디 : <strong>");
             out.print(id);
             out.print("</strong></li>\r\n");
             out.write("\t\t\t<li>성별 : <strong>");
             out.print(gender);
             out.print("</strong></li>\r\n");
             out.write("\t\t\t<li>생년월일 : <strong>");
             out.print(b_year+" "+b_month+" "+b_day);
             out.print("</strong></li>\r\n");
             out.write("\t\t\t<li>결혼여부 : <strong>");
             out.print(marriageFlag);
             out.print("</strong></li>\r\n");
             out.write("\t\t\t<li>관심언어 : \r\n");
             out.write("\t\t\t\t\t<strong>");
               
             if(interestlang!=null) {
              for(int i=0; i<interestlang.length;i++) {
                  out.write("\t\t\t\t\t<strong>");
                  out.print(interestlang[i]);
                  out.print(" <strong>\r\n");
              }
                
             }
             out.print("</strong>\r\n");
             out.write("\t\t\t</li>\r\n");
             out.write("\t\t\t<li><a  href=\"#void\">로그인</a></li>\r\n");
             out.write("\t\t</ul>\r\n");
             out.write("\t</div>\r\n");
         } catch(RuntimeException re) { // 중복 아이디가 존재해서 던진 RuntimeException 예외처리
          out.println("아이디가 이미 사용중입니다.  <br/>");
          out.println("<a href='#void'  onclick='history.back();'>다시 입력<a/>. <br/>");
         } catch(SQLException se) {
          se.printStackTrace();
          out.println("회원가입이 정상적으로 이루어지지  않았습니다.<br/>");
          out.println("서비스가 원활하지 못한점  죄송합니다.<br/>");
          out.println("정상적인 서비스를 위해서 저희  임직원들은 최선을 다하고 있습니다.<br/>");
          out.println("신속하게 복구하겠습니다. 사용중  불편한 점은 010-디잘잘에게 연락해주세요.<br/>");
         }//end catch
         ...
     }//doPost

     private void addWebMember(WebMemberVO wm_vo) throws  SQLException,RuntimeException {
         
         WebMemberDAO wm_dao =  WebMemberDAO.getInstance();

         //추가전에 동일 아이디가 존재하는지 다시 검색하여 처리(비연결성의 처리)
         if(wm_dao.selectId(wm_vo.getId())){
              throw new RuntimeException(); // 존재하면 런타임 예외 던짐
         }//end if

         wm_dao.insertMember(wm_vo);
         
     }//addWebMember
}

11



Java EEServlet