⌛️ 현재 상황
DB에 사용자의 비밀번호가 그대로 노출되는 것은
🚨 불법입니다 🚨
암호화해야 함!
현재 비밀번호가 사용자가 입력한 그대로의 평문임
사람이 그냥 눈으로 의미를 파악할 수 있는 데이터: 평문
사람이 그냥 눈으로 의미를 파악할 수 없게 처리한 데이터: 암호문
👉🏻 스프링에서 제공하는 Bcrypt 방식으로 비밀번호를 암호화 할 것
암호화 적용하는 법
1) 스프링 시큐리티 모듈에서 제공하는 라이브러리를 추가 (Maven)
스프링 시큐리티 모듈 라이브러리들 (core, web, config)의 버전을 모두 일치시켜야 함
👉🏻 3개 모두 다운로드받아야 함
👉🏻 Usages가 높은 5.7.3으로 통일하려 함
💻 pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.7.3</version>
</dependency>
👉🏻 다운로드 완료 확인!
2) BCryptPasswordEncoder 클래스를 xml 파일에 bean으로 등록
👉🏻 암호화 파일의 경우 이렇게 따로 빼 두는 추세임
webapp/WEB-INF/spring 폴더 내부에 Spring Bean Configuration File 생성
💻 spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="bcryptPasswordEncoder" />
</beans>
3)web.xml에 spring-security.xml 파일을 로딩할 수 있게 등록
💻 web.xml
👉🏻 <param-value>에 추가해 주기
<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>
💻 MemberController에 변수 생성
👉🏻 위치 확인하려고 위/아래 코드 함께 기재하였으나 실제로 추가한 것은 @Autowired 어노테이션과 bcryptPasswordEncoder 변수뿐임!
@Controller
public class MemberController {
// 스프링 방식
@Autowired // @Autowired 어노테이션 기재해 주고
private MemberService memberService; // 선언만 해 주면 됨!
// 비밀번호 암호화를 위한 변수
@Autowired
private BCryptPasswordEncoder bcryptPasswordEncoder; // 어노테이션과 이 줄만 추가해 줌!
💻 출력 테스트
package com.kh.spring.member.controller;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.kh.spring.member.model.service.MemberService;
import com.kh.spring.member.model.vo.Member;
@Controller
public class MemberController {
// 스프링 방식
@Autowired // @Autowired 어노테이션 기재해 주고
private MemberService memberService; // 선언만 해 주면 됨!
// 비밀번호 암호화를 위한 변수
@Autowired
private BCryptPasswordEncoder bcryptPasswordEncoder;
@RequestMapping("insert.me")
public void insertMember(Member m) {
System.out.println(m);
System.out.println("평문: " + m.getUserPwd());
// 암호화 작업 (암호문을 만들어 내는 과정)
String encPwd = bcryptPasswordEncoder.encode(m.getUserPwd());
System.out.println("암호문: " + encPwd);
}
}
👉🏻 같은 평문이어도 매번 다른 암호문 결과가 나옴
👉🏻 평문 + salt(랜덤값)을 한 암호화 작업이 이루어지기 때문
📍 하지만, 현재의 문제점!
일치하는 정보를 입력했음에도
로그인이 되지 않는다!
👉🏻 현재 로직상 평문으로 비교를 하기 때문에 당연히 암호문으로 저장된 DB의 내용과 일치하지 않음!
👉🏻 해당 로직 모두 주석 처리 (MemberControllerdml loginMember 메소드)
💻 member-mapper.xml
👉🏻 현재의 조건은 USER_PWD(DB 컬럼명, 암호문) = #{userId}(필드명, 평문)이기 때문에 애초에 일치할 수가 없음!
👉🏻 암호화 작업 후: 매번 암호문이 다르게 나오기 때문에 비밀번호 관련 일치 조건을 기술 불가
👉🏻 오로지 아이디만으로 회원을 조회할 것 (어차피 PK니까!)
<!-- 로그인용 쿼리문 -->
<select id="loginMember" parameterType="member" resultMap="memberResultSet">
SELECT *
FROM MEMBER
WHERE USER_ID = #{userId}
AND STATUS = 'Y'
</select>
💻 MemberController
👉🏻 BCrypt 방식에 의해 복호화가 불가능한 암호문 형태의 비밀번호와 일치하는지 대조 작업
💡 Member m의 userId 필드: 사용자가 입력한 아이디 (평문)
💡 userPwd 필드: 사용자가 입력한 비밀번호 (평문)
👉🏻 loginUser: 오로지 아이디만 가지고 조회된 회원의 정보
👉🏻 Member loginUser의 userPwd 필드: DB에 기록된 암호화된 비밀번호
💡 일치하는 아이디 기준으로 모든 컬럼을 가지고 왔으므로 현재 DB에서 가지고 온 정보에는 "암호화된 비밀번호"가 들어 있음
@RequestMapping("login.me")
public ModelAndView loginMember(Member m, ModelAndView mv, HttpSession session) {
// 암호화 작업 후 로직
Member loginUser = memberService.loginMember(m); // 그대로 호출
if(loginUser != null &&
bcryptPasswordEncoder.matches(m.getUserPwd(), loginUser.getUserPwd())) {
// 비밀번호도 일치한다면 => 로그인 성공
session.setAttribute("loginUser", loginUser);
session.setAttribute("alertMsg", "로그인에 성공했습니다.");
mv.setViewName("redirect:/");
} else { // 일치하지 않는다면 => 로그인 실패
mv.addObject("errorMsg", "로그인 실패");
// /WEB-INF/views/common/errorPage.jsp
mv.setViewName("common/errorPage");
}
return mv;
}
BCryptPasswordEncoder 객체의 matches 메소드
matches(평문, 암호문) 을 작성하면 내부적으로 평문과 암호문을 맞추는 작업이 이루어짐
👉🏻두 구문이 일치하는지 비교 후 일치하면 true 반환
💻 출력 화면 확인