본문 바로가기
Spring

[웹프로젝트] 6. 회원가입(2) - 암호화

by moca7 2024. 10. 28.
 

 

 

ㅁ 암호화

- 어떠한 값(평문)을 다른사람들이 알아볼 수 없는 암호문으로 변경하는 과정

- DB 같은 저장소에 개인정보 중 보호해야되는 값들은 암호문으로 보관해야됨.

 

- 암호화: 평문 -> 암호문

- 복호화: 암호문 -> 평문

 

- 양방향 암호화 방식: 암호화, 복호화 둘 다 가능.

- 단방향 암호화 방식: 암호화만 가능.

 

- 옛날에는 양방향 암호화 방식으로 비밀번호를 처리했다.

그래서 비밀번호 찾기하면 기존 비번을 알려줄 수 있었다.

평문을 유추할 수 있다는 것은 보안에 취약하다는 뜻이다.

그래서 단방향 암호화 방식으로 보안정책이 바뀌었다.

이때 많이 사용하던 방식이 SHA 암호화 방식이다. (해쉬알고리즘)

 

- 그런데 SHA방식도 취약점이 있었다.

매번 똑같은 평문을 입력하면 매번 똑같은 암호문을 만들어준다.

ex) 1111 => Axasdf23@#52a..

ex) 1111 => Axasdf23@#52a..

많은 데이터가 쌓이다 보니까 평문을 가지고 암호문을 유추할 수 있게 되었다.

암호문을 집어넣으면 그걸로 평문을 만들어 낼 수 있는 레인보우 테이블이 만들어졌다.

- 샘플 데이터가 많이 취합되면서 평문을 가지고 암호문을 유추할 수 있고 역으로 암호문만 넣으면 평문을 알려주는 레인보우 테이블이 만들어졌다.

 

- 그래서 솔팅 기법이 만들어졌다.

저 평문에다가 매번 랜덤한 숫자를 넣어서 암호문을 만들게 해서 매번 다르게 암호문이 만들어진다. 

- SHA 방식의 문제점을 보완하기 위해서 솔팅기법(salting)이 추가되었다.

- 솔팅기법은 평문에 매번 랜덤값을 덧붙여서 암호화한다.

똑같은 평문을 입력해도 암호문이 달라진다.

ex) 1111 + salt값(45211) => Axasdf23@#52a..

ex) 1111 + salt값(45211) => Axasdf2341asdf2@$1

 

- 스프링 시큐리티 모듈에서 제공하는 Bcrypt 암호화방식(솔팅기법이 추가된 암호화 방식)이 있다.

(1) 스프링 시큐리티 라이브러리 추가 (pom.xml)

(2) BcryptPasswordEncodr 빈으로 등록 (spring-security.xml파일)

(3) web.xml에서 해당 spring-security.xml 파일 pre-loading ~

 

 

 

 

 

ㅁ pom.xml

 

 

- 저 3개 전부 추가한다.

- <!-- Spring -->과 <!-- AspectJ (AOP를 위한 라이브러리)--> 사이에 추가한다.

- 빨간 글씨는 이슈가 있었던 것. 큰 문제는 아님. 있어도 많이 씀. 5.7.5로 간다. usage가 많아서.

 

 
<?xml version="1.0" encoding="UTF-8"?>
 
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.br</groupId>
  <artifactId>spring</artifactId>
  <name>springWeb</name>
  <packaging>war</packaging>
  <version>1.0.0-BUILD-SNAPSHOT</version>
 
  <properties>
    <java-version>11</java-version>
    <org.springframework-version>5.3.27</org.springframework-version>
    <org.aspectj-version>1.9.19</org.aspectj-version>
    <org.slf4j-version>2.0.7</org.slf4j-version>
  </properties>
 
  <dependencies>
 
    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework-version}</version>
      <exclusions>
        <!-- Exclude Commons Logging in favor of SLF4j -->
        <exclusion>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>

    <!-- Spring Security 모듈 라이브러리 -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-core</artifactId>
      <version>5.7.5</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>5.7.5</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>5.7.5</version>
    </dependency>

    <!-- AspectJ (AOP를 위한 라이브러리)-->
    <dependency> <!-- 원래 있던 것. 이게 있어야 "Advice 동작 시점"의 어노테이션들을 사용할 수 있다. -->
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${org.aspectj-version}</version>
    </dependency>
   
    <!-- 위빙 : Advice(공통로직)를 PointCut(핵심로직)에 로딩되도록 함 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>${org.aspectj-version}</version>
      <scope>runtime</scope>
    </dependency>  
   
   
   
   
    <!-- Logging (logback) -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.4.14</version>
      <!-- <scope>test</scope> 이건 지워야 함 -->
    </dependency>

    <!-- (2) slf4j -->
    <dependency> <!-- 기존 dependency 중 이것만 남기고 다 지운다.-->
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${org.slf4j-version}</version>
    </dependency>
   
    <!-- (3) log4jdbc -->
    <dependency>
      <groupId>org.bgee.log4jdbc-log4j2</groupId>
      <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
      <version>1.16</version>
    </dependency>


   

    <!-- @Inject -->
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>
       
    <!-- Servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
 
    <!-- Test -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>        
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${org.springframework-version}</version>
        <scope>test</scope>
    </dependency>
   
   
    <!-- Lombok 라이브러리 추가 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>

    <!-- Jackson 라이브러리 추가 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.14.2</version>
    </dependency>


    <!-- db관련 라이브러리 -->
    <!-- (1) ojdbc8 -->
    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc8</artifactId>
      <version>23.2.0.0</version>
    </dependency>

    <!-- (2) spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${org.springframework-version}</version>
    </dependency>

    <!-- (3) mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.14</version>
    </dependency>

    <!-- (4) mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>

    <!-- (5) commons-dbcp -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>2.9.0</version>
    </dependency>


    <!-- 파일 업로드 관련 라이브러리 -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.5</version>
    </dependency>
   
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
   

  </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java-version}</source>
                    <target>${java-version}</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
 

 

- core, web, config 부분만 다르다. 복붙해서 저것만 고쳤다.

 

 

 

 

 

ㅁ root-context.xml

- 여기다 해도 되는데 안함.

 

 

ㅁ 새 spring bean configuration xml 파일 생성

 

 

 

- 근데 보통 스프링 시큐리티 관련해서는 따로 빼서 xml 파일을 만든다.

Spring Bean Configuration 파일을 만든다.

- 저곳에 저 이름으로 만든다. finish.

 

 

 

 

- (확인필요) security를 체크한다.

- 전화면에서 finish말고 next해서 체크하고 만들어도 된다.

 

 

 
<?xml version="1.0" encoding="UTF-8"?>


  <bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="bcryptPwdEncoder"></bean>

</beans>

 

- root-context.xml뿐 아니라 지금 이 파일ㄷ ㅗ강제로 서버 스타트시 읽혀직 ㅔ만들어야 한다.

- web.xml로 간다.

 

 

 

 

ㅁ web.xml

 

 
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"

  <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      /WEB-INF/spring/root-context.xml
      /WEB-INF/spring/spring-security.xml
    </param-value>
  </context-param>
 
  <!-- Creates the Spring Container shared by all Servlets and Filters -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- Processes application requests -->
  <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
   
  <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
 
  <!-- 인코딩 필터 등록 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
 
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
 

</web-app>

 

- "/WEB-INF/spring/spring-security.xml"를 추가했다.

 

 

 

 

 

ㅁ MemberController

 

package com.br.spring.controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.br.spring.dto.MemberDto;
import com.br.spring.service.MemberService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {

      private final MemberService memberService;
      private final BCryptPasswordEncoder bcryptPwdEncoder;
   
     
    @PostMapping("/signin.do")
    public void signin(MemberDto m, HttpServletResponse response, HttpSession session, HttpServletRequest request) throws IOException {
        MemberDto loginUser = memberService.selectMember(m);

        // 로그인 성공시 => 세션에 회원정보를 담고, alert와 함께 메인 페이지로 이동
        // 로그인 실패시 => alert와 함께 기존에 보던 페이지 유지(아이디, 비번 입력하는 로그인 폼 유지. 작성하던 입력데이터도 그대로.)

        // script문을 응답데이터로 돌려줘서 흐름제어
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<script>");

        if(loginUser != null) { // 로그인 성공
            session.setAttribute("loginUser", loginUser);
            out.println("alert('" + loginUser.getUserName() + "님 환영합니다~');");
            out.println("location.href = '" + request.getHeader("referer") + "';"); // 메인페이지가 아닌 이전에 보던 페이지로 이동
        }else { // 로그인 실패
            out.println("alert('로그인에 실패하였습니다. 아이디 및 비밀번호를 다시 확인해주세요.');");
            out.println("history.back();"); // modal이 띄워진 상태였으면 modal도 유지된다.
        }

        out.println("</script>");
    }
   
   
    @GetMapping("/singout.do")
    public String signout(HttpSession session) {
        session.invalidate();
        return "redirect:/";
    }
   
   
    @GetMapping("/signup.do")
    public void signupPage() { } //WEB-INF/views/member/signup.jsp
       
    @ResponseBody
    @GetMapping("/idcheck.do")
    public String idCheck(String checkId) {
        return memberService.selectUserIdCount(checkId) == 0 ? "YYYY" : "NNNN";
    }
   
   
    @PostMapping("/insert.do")
    public void signup(MemberDto m) {
        log.debug("암호화 전 member: {}", m);
       
        m.setUserPwd( bcryptPwdEncoder.encode(m.getUserPwd()) );
       
        log.debug("암호화 후 member: {}", m);
    }
   
   
   
   

   
}

 

 

- 전역변수를 뒀다. 

- m.setUserPwd( bcryptPwdEncoder.encode(m.getUserPwd()) ); log.debug("암호화 후 member: {}", m);를 작성했다.

 

 

 

 

 

 

 

=======================================================================

 

 

 

- 비번 pass07!!이다.

- 커밋한다.

- 3계정 다 $2a$10$YG8TWlRA7J2U5HQaf/u/6OehDRKcdo4E2Eja41yuoBx0/3jBbP7/O

- 로그인하려면 다 pass07!! 입력해야 한다.

 

 

 

ㅁ MemberController

 

package com.br.spring.controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.br.spring.dto.MemberDto;
import com.br.spring.service.MemberService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {

      private final MemberService memberService;
      private final BCryptPasswordEncoder bcryptPwdEncoder;
   
     
    @PostMapping("/signin.do")
    public void signin(MemberDto m, HttpServletResponse response, HttpSession session, HttpServletRequest request) throws IOException {
        MemberDto loginUser = memberService.selectMember(m);

        // 로그인 성공시 => 세션에 회원정보를 담고, alert와 함께 메인 페이지로 이동
        // 로그인 실패시 => alert와 함께 기존에 보던 페이지 유지(아이디, 비번 입력하는 로그인 폼 유지. 작성하던 입력데이터도 그대로.)

        // script문을 응답데이터로 돌려줘서 흐름제어
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<script>");

        if(loginUser != null) { // 로그인 성공
            session.setAttribute("loginUser", loginUser);
            out.println("alert('" + loginUser.getUserName() + "님 환영합니다~');");
            out.println("location.href = '" + request.getHeader("referer") + "';"); // 메인페이지가 아닌 이전에 보던 페이지로 이동
        }else { // 로그인 실패
            out.println("alert('로그인에 실패하였습니다. 아이디 및 비밀번호를 다시 확인해주세요.');");
            out.println("history.back();"); // modal이 띄워진 상태였으면 modal도 유지된다.
        }

        out.println("</script>");
    }
   
   
    @GetMapping("/singout.do")
    public String signout(HttpSession session) {
        session.invalidate();
        return "redirect:/";
    }
   
   
    @GetMapping("/signup.do")
    public void signupPage() { } //WEB-INF/views/member/signup.jsp
       
    @ResponseBody
    @GetMapping("/idcheck.do")
    public String idCheck(String checkId) {
        return memberService.selectUserIdCount(checkId) == 0 ? "YYYY" : "NNNN";
    }
   
   
    @PostMapping("/insert.do")
    public String signup(MemberDto m, RedirectAttributes rdAttributes) {
       
        log.debug("암호화 전 member: {}", m);
       
        m.setUserPwd( bcryptPwdEncoder.encode(m.getUserPwd()) );
       
        log.debug("암호화 후 member: {}", m);
       
       
        int result = memberService.insertMember(m);
       
        // 성공시 alert와 함께 메인페이지 이동
        // 실패시 alert와 함께 기존에 작업중이던 페이지 유지
        if(result > 0) {
            rdAttributes.addFlashAttribute("alertMsg", "성공적으로 회원가입 되었습니다.");
        }else {
            rdAttributes.addFlashAttribute("alertMsg", "회원가입에 실패하였습니다.");
            rdAttributes.addFlashAttribute("historyBackYN", "Y");
        }
       
        return "redirect:/";
    }
   
   
   
   

   
}

 

- redirect하고 포워딩하는 곳까지 데이터를 유지하려면 RedirectAttributes 객체를 써야 한다.

매개변수에 추가한다.

 

 

 

 

ㅁ header.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   

<!-- Bootstrap 사용을 위한 CDN -->
<!-- ------------------------- -->

<style>
    header{height: 50px}
    header a{color:black;}
    header .profile-img{width:30px;}
</style>

<script>
    if('${alertMsg}' != ''){
        alert('${alertMsg}');
        if('${historyBackYN}' == 'Y'){
            history.back();
        }
    }
</script>



<header class="row m-3">
    <div class="col-3 d-flex justify-content-center align-items-center">
        <a href=""><img src="${ contextPath }/resources/images/goodee_logo.png" width="100px"></a>
    </div>
    <div class="col-5"></div>
    <div class="col-4 d-flex justify-content-center align-items-center">
   
       
        <c:choose>
            <c:when test="${ empty loginUser }">
            <!-- case1. 로그인전 -->
            <a href="${contextPath}/member/signup.do">회원가입</a> &nbsp;|&nbsp;
            <a href="#" data-toggle="modal" data-target="#loginModal">로그인</a>
        </c:when>
       
        <c:otherwise>
            <!-- case2. 로그인후 -->
            <div>
            <img class="profile-img" src="${ contextPath }<c:out value='${loginUser.profileURL}' default='/resources/images/defaultProfile.png' />">&nbsp;
            <a href="">${loginUser.userName}님</a> &nbsp;|&nbsp;
            <a href="${contextPath}/member/signout.do">로그아웃</a>
            </div>
        </c:otherwise>
      </c:choose>  
   
    </div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
<ul class="navbar-nav">
    <li class="nav-item">
    <a class="nav-link" href="#">Home</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="#">공지사항</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="#">일반게시판</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="#">사진게시판</a>
    </li>
</ul>
</nav>

<!-- 로그인 클릭 시 뜨는 모달 (기존에는 안보이다가 위의 a 클릭시 보임) -->
<div class="modal fade" id="loginModal">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
        <!-- Modal Header -->
        <div class="modal-header">
            <h4 class="modal-title">Login</h4>
            <button type="button" class="close" data-dismiss="modal">&times;</button>
        </div>

        <form action="${contextPath}/member/signin.do" method="post">
            <!-- Modal Body -->
            <div class="modal-body">
                <label for="userId" class="mr-sm-2">ID :</label>
                <input type="text" class="form-control mb-2 mr-sm-2" placeholder="Enter ID" id="userId" name="userId" required> <br>
                <label for="userPwd" class="mr-sm-2">Password:</label>
                <input type="password" class="form-control mb-2 mr-sm-2" placeholder="Enter password" id="userPwd" name="userPwd" required>
            </div>
           
            <!-- Modal footer -->
            <div class="modal-footer">
                <button type="submit" class="btn btn-primary">로그인</button>
                <button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
            </div>
        </form>
        </div>
    </div>
</div>
 

 

- 상단에 <style> 태그 다음에 <script> 태그를 작성했다.

- 이전 페이지로 이동해야하는 경우가 있다면 앞으로 alertMsg 라는 key값에 "Y"를 담으면 된다.

 

 

 

 

 

- id는 user09, 비번은 위의 3계정과 같이 pass07!!로 회원가입 요청을 보냈다.

- 같은 평문이지만 암호문은 다른걸 볼 수 있다.

(위의 세 계정의 비번은 sql developer에서 수동으로 다 같게 수정한 것)

 

- 참고로 이제 로그인이 안된다. 로그인 시에도 Bcrypt~를 써야 한다.