08_Spring_240409(화)_61일차(2) - ★BoardProject★ - 1. 로그인

soowagger·2024년 4월 9일

8_Spring

목록 보기
12/38

1. 로그인

1) 비밀번호 암호화(BCryptPasswordEncoder)

BCrypt 암호화 (Spring Security 제공)
  
 * - 입력된 문자열(비밀번호)에 salt를 추가한 후 암호화
 *
 * ex) A 회원 : 1234	->    $12!asdfg
 * ex) B 회원 : 1234	->    $12!qwert
 * 
 * - 비밀번호 확인 방법
 * -> BCryptePasswordEncoder.matches(평문 비밀번호, 암호화된 비밀번호)
 * --> 평문 비밀번호와 암호화된 비밀번호가 같은 경우 true 아니면 false 반환
 * 
 * 
 * * 로그인 / 비밀번호 변경 / 탈퇴 등 비밀번호가 입력되는 경우
 * - DB에 저장된 암호화된 비밀번호를 조회해서
 *   matches() 메서드로 비교해야 한다!

 sha 방식 암호화
 * ex) A 회원 : 1234	->    암호화 : abcd
 * ex) B 회원 : 1234	->    암호화 : abcd(암호화 시 변경된 내용이 같음)

SecurityConfig

/* @Configuration
 * - 설정용 클래스임 명시
 * + 객체로 생성해서 내부 코드를 서버 실행 시 모두 수행
 * 
 * @Bean
 * - 개발자가 수동으로 생성한 객체의 관리를
 *   스프링에게 넘기는 어노테이션 (Bean 등록)
 * 
 */

@Configuration
public class SecurityConfig {
	
	@Bean
	public BCryptPasswordEncoder bCryptPasswordEncoder() {
		
		return new BCryptPasswordEncoder();
	}
	
}

MemberserviceImpl

// BCrypt 암호화 객체 의존성 주입(securityConfig 참고)
@Autowired
private BCryptPasswordEncoder bcrypt;

// * 로그인 서비스 
@Override
public Member login(Member inputMember) {
	
	 
	// 테스트
	
	// bcrypt.encode(문자열) : 문자열을 암호화하여 반환
	String bcryptPassword = bcrypt.encode(inputMember.getMemberPw());
	
	log.debug("bcryptPassword : " + bcryptPassword);
	// bcryptPassword : $2a$10$mgtPFqZcmxckFQ9cfX5OGOvvV34vgnyZIWXVLzZAzAMU46FZ1Viky
	
	
	return null;
}

2) 암호화된 비밀번호로 샘플 데이터 변경

boolean result = bcrypt.matches(inputMember.getMemberPw(), bcryptPassword);

log.debug("result : " + result);

3) 로그인 결과

Controller

/* @SessionAttributes({"key", "key", "key", ...})
 * - Model에 추가된 속성 중 key 값이 일치하는 속성을
 *   session scope로 변경
 */


@SessionAttributes({"loginMember"})
@Controller
@Slf4j
@RequestMapping("member")
public class MemberController {
	
	@Autowired // 의존성 주입(DI)
	private MemberService service;
	
	/* [로그인]
	 * - 특정 사이트에 아이디/비밀번호 등을 입력해서
	 *   해당 정보가 있으면 조회/서비스 이용
	 *
	 * - 로그인 한 정보를 session에 기록하여
	 *   로그아웃 또는 브라우저 종료 시 까지
	 *   해당 정보를 계속 이용할 수 있게 함.
	 *   
	 */
	
	/** 로그인
	 * @param inputMember : 커맨드 객체 (@ModelAttribute 생략)
	 * 						(memberEmail, memberPw 세팅된 상태)
	 * @param ra : 리다이렉트 시 requset scope로 데이터를 전달하는 객체
	 * @param model : 데이터 전달용 객체(기본 request scope)
	 * @return "redirect:/"
	 */
	@PostMapping("login")
	public String login(Member inputMember, 
						RedirectAttributes ra,
						Model model) {
		
		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
		
		
		// 로그인 실패 시
		if(loginMember == null) {
			ra.addFlashAttribute("message", "아이디 또는 비밀번호가 일치하지 않습니다.");
		} 
		
		// 로그인 성공 시
		if(loginMember != null) {
			
			// Session scope에 loginMember 추가
			model.addAttribute("loginMember", loginMember);
			// 1단계 : request scope에 세팅됨
			
			// 2단계 : 클래스 위에 @SessionAttributes() 어노테이션 때문에
			//         session scope로 이동됨
			
		}
		
		return "redirect:/"; // 메인페이지 재요청
	}
}

Service

@Transactional(rollbackFor = Exception.class) // 해당 클래스 메서드 종료 시 까지
			   // - 예외(RuntimeException)가 발생하지 않으면 commit
			   // - 예외(RuntimeException)가 발생하면 rollback
@Service // 비즈니스 로직 처리 역할
@Slf4j
public class MemberServiceImpl implements MemberService {
	
	// 등록된 bean 중에서 같은 타입 또는 상속관계인 bean을
	// 자동으로 의존성 주입
	@Autowired
	private MemberMapper mapper;
	
	
	// BCrypt 암호화 객체 의존성 주입(securityConfig 참고)
	@Autowired
	private BCryptPasswordEncoder bcrypt;
	
	// * 로그인 서비스 
	@Override
	public Member login(Member inputMember) {
		
		// 1. 이메일이 일치하면서 탈퇴하지 않은 회원 조회
		Member loginMember = mapper.login(inputMember.getMemberEmail()); 
		
				
		// 2. 만약에 일치하는 이메일이 없어서 조회 결과가 null 인 경우
		if(loginMember == null) return null;
		
		// 3. 입력 받은 비밀번호(inputMember.getMemberPw() 평문)와
		//    암호화된 비밀번호(loginMember.getMemberPw())
		//    -> 두 비밀번호가 일치하는지 확인
		
		// 3-1. 일치하지 않으면
		if(!bcrypt.matches(inputMember.getMemberPw(), loginMember.getMemberPw())) {
			return null;
		}
		
		// 4. 로그인 결과에서 비밀번호 제거
		loginMember.setMemberPw(null);
		
		return loginMember;
	}
}

mapper

<!-- parameterType 속성은 필수 속성 아님! 
	Why ? TypeHandler를 이용해서 자료형은 판별할 수 있음 -->

<!-- 1. 로그인 -->
<select id="login"  resultType="Member">
	SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_PW,
	MEMBER_TEL, MEMBER_ADDRESS, PROFILE_IMG, AUTHORITY, 
	TO_CHAR(ENROLL_DATE, 'YYYY"년" MM"월" DD"일" HH24"시" MI"분" SS"초"') ENROLL_DATE 
	FROM "MEMBER"
	WHERE MEMBER_EMAIL = #{memberEmail}
	AND MEMBER_DEL_FL = 'N'
</select>

↑ 비밀번호 오입력 시 메시지 출력 / footer.html 설정

<script th:inline="javascript">
	const message =  /*[[${message}]]*/ "전달 받은 message";

	if(message != null) alert(message);
</script>
profile

0개의 댓글