SpringBoot : 로그인, 쿠키
package edu.kh.project.member.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import edu.kh.project.member.model.dto.Member;
import edu.kh.project.member.model.service.MemberService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@Controller
@SessionAttributes({"loginMember"})
@RequestMapping("/member")
public class MemberController {
@Autowired
private MemberService service;
@PostMapping("/login")
public String login(Member inputMember, Model model,
@RequestHeader("referer") String referer,
RedirectAttributes ra,
@RequestParam(value="saveId", required = false) String saveId,
HttpServletResponse resp
) {
Member loginMember = service.login(inputMember);
String path = "redirect:";
if(loginMember != null) { // 로그인 성공시
path += "/";
model.addAttribute("loginMember", loginMember);
Cookie cookie = new Cookie("saveId", loginMember.getMemberEmail());
if(saveId != null) { // 체크가 되었을 때
cookie.setMaxAge(60*60*24*30);
} else { // 체크가 안되었을 때
cookie.setMaxAge(0);
}
cookie.setPath("/");
resp.addCookie(cookie);
} else { // 로그인 실패
path += referer;
ra.addFlashAttribute("message", "아이디 또는 비밀번호 불일치");
}
return path;
}
@GetMapping("/logout")
public String logout(SessionStatus status) {
status.setComplete();
return "redirect:/";
}
}
package edu.kh.project.member.model.service;
import edu.kh.project.member.model.dto.Member;
public interface MemberService {
/** 로그인 서비스
* @param inputMember (email, pw)
* @return email, pw가 일치하는 회원정보 또는 null
*/
Member login(Member inputMember);
/** 회원 가입 서비스
* @param inputMember
* @return result
*/
int signUp(Member inputMember);
}
package edu.kh.project.member.model.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import edu.kh.project.member.model.dao.MemberDAO;
import edu.kh.project.member.model.dto.Member;
@Service
public class MemberServiceImpl implements MemberService{
@Autowired
private MemberDAO dao;
@Autowired // bean으로 등록된 객체 중 타입이 일치하는 객체를 DI(의존성 주입)
private BCryptPasswordEncoder bcrypt;
// 암호화가 필요한 곳? 로그인, 회원가입
@Override
public Member login(Member inputMember) {
// dao 메서드 호출
Member loginMember = dao.login(inputMember);
if(loginMember != null) { // 아이디가 일치하는 회원이 조회된 경우
if(bcrypt.matches(inputMember.getMemberPw(), loginMember.getMemberPw())) {
// 비밀번호를 유지하지 않기 위해서 로그인 정보에서 제거
loginMember.setMemberPw(null);
} else { // 다를 경우
loginMember = null;
}
}
return loginMember;
}
// 회원 가입 서비스
@Transactional
@Override
public int signUp(Member inputMember) {
// 비밀번호 암호화 (Bcrypt) 후 다시 inputMember 세팅
String encPw = bcrypt.encode(inputMember.getMemberPw());
inputMember.setMemberPw(encPw);
return dao.signUp(inputMember);
}
}
package edu.kh.project.member.model.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import edu.kh.project.member.model.dto.Member;
@Repository
public class MemberDAO {
@Autowired // 객체로 만들어진 것 -> 의존성 주입
private MemberMapper memberMapper; // MemberMapper 인터페이스를 상속받은 자식 객체
// 자식객체가 sqlSessionTemplate 이용
public Member login(Member inputMember) {
// return sqlSession.selectOne("memberMapper.login", inputMember);
return memberMapper.login(inputMember);
}
public int signUp(Member inputMember) {
return memberMapper.signUp(inputMember);
}
}
package edu.kh.project.member.model.dao;
import org.apache.ibatis.annotations.Mapper;
import edu.kh.project.member.model.dto.Member;
//마이바티스 mapper와 연결된 인터페이스임을 명시
//- 해당 인터페이스에 메서드명과 mapper.xml파일의 id가 일치하는 태그가 자동으로 연결됨
//-> 단, mapper.xml파일의 namespcae가 해당 인터페이스의 패키지명+클래스명으로 등록되어 있어야함.
//- @Mapper가 작성된 인터페이스를 사용하면 DAO에서 sqlSessionTemplate 객체를 사용하지 않아도됨
//-> DAO에 매퍼 인터페이스를 @Autowired 하면 sqlSessionTemplate 객체가 의존성 주입됨.
@Mapper
public interface MemberMapper {
Member login(Member inputMember);
// 메서드 이름 login -> 연결된 mapper.xml에서 id가 login인 sql이 수행
int signUp(Member inputMember);
}
<?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" >
<!-- namespace를 Mapper 인터페이스로 지정 -->
<mapper namespace="edu.kh.project.member.model.dao.MemberMapper"> <!-- MemberMapper와 mapper.xml을 연결(매핑)해주는 역할 -->
<resultMap type="Member" id="member_rm">
<id property="memberNo" column="MEMBER_NO" />
<result property="memberEmail" column="MEMBER_EMAIL" />
<result property="memberPw" column="MEMBER_PW" />
<result property="memberNickname" column="MEMBER_NICKNAME" />
<result property="memberTel" column="MEMBER_TEL" />
<result property="memberAddress" column="MEMBER_ADDR" />
<result property="profileImage" column="PROFILE_IMG" />
<result property="enrollDate" column="ENROLL_DATE" />
<result property="memberDeleteFlag" column="MEMBER_DEL_FL" />
<result property="authority" column="AUTHORITY" />
</resultMap>
<!-- 로그인 -->
<select id="login" resultMap="member_rm">
SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_PW,
MEMBER_TEL, MEMBER_ADDR, PROFILE_IMG, AUTHORITY,
TO_CHAR(ENROLL_DATE, 'YYYY"년" MM"월" DD"일" HH24"시" MI"분" SS"초"') AS ENROLL_DATE
FROM "MEMBER"
WHERE MEMBER_DEL_FL = 'N'
AND MEMBER_EMAIL = #{memberEmail}
</select>
<!-- 회원 가입 -->
<insert id="signUp">
INSERT INTO "MEMBER"
VALUES(SEQ_MEMBER_NO.NEXTVAL
, #{memberEmail}
, #{memberPw}
, '${memberNickname}'
, #{memberTel}
, #{memberAddress}
, NULL, DEFAULT, DEFAULT, DEFAULT)
</insert>
</mapper>
아이디 : user01@kh.or.kr
비밀번호 : pass01! 입력 후 로그인 버튼 클릭 시,
타임리프 -> 쿠키를 바로 꺼내 쓸 수 없음
(자바스크립트가 필요!)
// 자바스크립트로 쿠키 얻어오기
function getCookie(key) {
//saveId=user01@kh.or.kr; test=가나다; aaa=100
const cookies = document.cookie; // 브라우저에 있는 전체쿠키 가져오기
// ['saveId=user01@kh.or.kr', 'test=가나다', 'aaa=100']
const cookieList = cookies.split("; ").map(cookie => cookie.split("=")); // 배열 -> K:V 형태
//배열.map() : 배열의 모든 요소를 순차접근하여 특정 함수 수행 후
// 수행 결과를 이용해서 새로운 배열을 만드는 함수
// ex) [1,2,3].map(num => num*2) -------> [2,4,6]
// [ ['saveId', 'user01@kh.or.kr'], ['test, 가나다'], ['aaa=100'] ]
console.log(cookieList);
const obj = {};
for(let i = 0; i < cookieList.length; i++) {
obj[cookieList[i][0]] = cookieList[i][1];
}
return obj[key];
}
// 쿠키에 saveId가 있을 경우
if( document.querySelector("input[name='memberEmail']") != null ) {
// 화면에 memberEmail 입력박스가 있을 경우
const saveId = getCookie("saveId");
// 있으면 이메일값, 없으면 undefined
if(saveId != undefined) { // 쿠키에 저장된 이메일이 있을 때
document.querySelector("input[name='memberEmail']").value = saveId;
document.querySelector("input[name='saveId']").checked = true;
}
}