Spring Boot Board Project_01 로그인

송지윤·2024년 4월 19일

Spring Boot

목록 보기
32/73

Controller 및 Service, Mapper, mapper.xml, MemberDTO 생성

main.html

          <form action="/member/login" method="POST" id="loginForm">
            <fieldset class="id-pw-area">
  
              <!-- 아이디/비밀번호 입력 -->
              <section>
                <input type="text" 
                       name="memberEmail"
                       placeholder="이메일">
  
                <input type="password" 
                       name="memberPw" 
                       placeholder="비밀번호">
              </section>
  
              <!-- 로그인 버튼 -->
              <section>
                <button>로그인</button>
              </section>
            </fieldset>
  
            <label>
              <!-- label 태그 : input 태그의 제목을 나타내는 태그  -->
              <input type="checkbox" name="saveId">
              아이디 저장
            </label>
  
            <article class="signup-find-area">

              <a href="/member/signup">회원가입</a>

              <span> | </span>
              <a href="#">ID/PW 찾기</a>
            </article>
  
          </form>

mapper.xml 생성 시

catalog Mapper 로 해서 만들어야함 Config 파일과 다름

2. 기본 세팅

Controller

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

	private final MemberService service;
}

ServiceImpl

@Service
@Transactional(rollbackFor=Exception.class)
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {

	private final MemberMapper mapper;
}

모든 예외 발생 시 rollback ( 기본 RuntimeException )

MemberMapper interface

@Mapper
public interface MemberMapper {

}

member-mapper.xml

만들자마자 cache-ref 지워야함

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.home.board.member.model.mapper.MemberMapper">

</mapper>

namespace 에 연결할 Mapper 인터페이스 패키지명+인터페이스명 작성하면 Mapper interface method 호출 시 xml 파일에 작성된 태그 중 메서드명과 일치하는 id 를 가진 SQL 이 수행됨

1. html 에서 form 태그로 보낸 값 받아서 service 호출

MemberController

	@PostMapping("login")
	public String login(Member inputMember,
			RedirectAttributes ra,
			Model model) {
		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
		
		return "redirect:/"; // 메인페이지 재요청
	}

inputMember : 커맨드 객체(@ModelAttribute 생략)
(memberEmail, memberPw 세팅된 상태)
html 에서 form 태그 안에 input 태그 name 속성으로 memberEmail, memberPw 값 받아서 그대로 넘겨줌

2. Service 에서 Controller 가 넘겨준 값을 매개변수로 받아 비밀번호 암호화

ServiceImpl

BCrypt 암호화 (Spring Security 제공)

  • 입력된 문자열(비밀번호)에 salt 를 추가한 후 암호화하는 애
    (똑같은 평문을 암호화해도 다 다른 결과값이 나옴)

  • 비밀번호 확인 방법
    -> BCryptPasswordEncoder.matches(평문 비밀번호, 암호화된 비밀번호)
    -> 평문 비밀번호와 암호화된 비밀번호가 같은 경우 true 아니면 false 반환

  • 로그인 / 비밀번호 변경 / 탈퇴 등 비밀번호가 입력되는 경우
    DB에 저장된 암호화된 비밀번호를 조회해서 matches() 메서드로 비교해야 함.

sha 방식 암호화(BCrypt 이전 사용 방식)는 똑같은 평문을 암호화하면 똑같은 결과값이 나옴

1. BCrypt 암호화 사용하려면 SecurityConfig 클래스에 설정 필요

SecurityConfig 클래스

@Configuration : 설정용 객체임을 명시 + 객체로 생성해서 내부 코드를 서버 실행시 모두 수행

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfig {

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

}

@Bean : 개발자가 수동으로 생성한 객체의 관리를 스프링에게 넘기는 어노테이션 (Bean 등록)

2. SecurityConfig를 사용할 ServiceImpl 에서 의존성 주입해주기

MemberSerivceImpl

private final BCryptPasswordEncoder bcrypt;

3. DB 테이블 만들 때 평문 상태 비밀번호 테스트로 넣어둠 -> .encode 이용해서 평문상태인 비밀번호 바꿔서 DB 에 넣어주기

ServiceImpl

		String bcryptPassword = bcrypt.encode(inputMember.getMemberPw());
		
		log.debug("bcryptPassword : " + bcryptPassword);

DBeaver

UPDATE "MEMBER" SET
MEMBER_PW = '암호화된 비밀번호'
WHERE MEMBER_NO = 1;

DB에 평문상태로 비밀번호 저장해두면 안됨

4. ServiceImpl 에서 이메일이 일치하면서 탈퇴하지 않은 회원 조회하는 sql 문 보내기

ServiceImpl

Member loginMember = mapper.login(inputMember.getMemberEmail());

member-mapper.xml

parameterType 속성은 필수 속성 아님
TypeHandler 를 이용해서 자료형을 판별할 수 있음

	<select id="login" parameterType="string" 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>

비밀번호까지 돌려주는 이유는 Service 에서 평문으로 들어온 비밀번호와 sql 에 저장된 암호화된 비밀번호를 조회해야해서

5. 조회한 SQL 문을 service 단에서 받아서 처리

  1. 만약에 일치하는 이메일이 없어서 조회 결과가 null 인 경우
if(loginMember == null) return null;

loginMember 조회 결과가 있을 때

  1. loginMember 에 담긴 암호화된 비밀번호 와 입력받은 평문 비밀번호 일치하는지 비교해줘야함

2-1. 비밀번호가 일치하지 않으면 return null;
.matches(평문,암호화)
부정논리연산자 붙여서 일치하지 않을 때 true 값 나오게

if(!bcrypt.matches(inputMember.getMemberPw(), loginMember.getMemberPw())) {
	return null;
}

2-2. 비밀번호가 일치할 때 로그인 시켜줘야함
(단, 로그인 결과에서 가지고 온 비밀번호는 제거시켜줘야함)
(loginMember 는 session scope 에 실어둘 거라서)
return

loginMember.setMemberPw(null);

return loginMember;

6. ServiceImpl 에서 돌려준 값을 Controller 에서 받아 분기 처리

MemberController

로그인 실패 시

		if(loginMember == null) {
			ra.addFlashAttribute("message", "아이디 또는 비밀번호가 일치하지 않습니다.");
		}

로그인 성공 시

		if(loginMember != null) {
			// loginMember 를 session scope 에 올려둘 거임
			model.addAttribute("loginMember", loginMember);
			// request scope 에 올려둔 거
		}

Model 을 session scope 에 올리는 방법

@SessionAttributes ( { "key" } )

Controller 클래스 상단에 @SessionAttributes({"loginMember"}) 어노테이션 추가
-> Model 에 추가된 속성 중 key 값이 일치하는 속성을 session scope 로 변경해주겠다는 어노테이션

@SessionAttributes ({"key", "key", ...}) 쭉 나열해서 사용 가능

0개의 댓글