Skip to content

로그인

Gaeun Kim edited this page Feb 26, 2023 · 3 revisions

📌 로그인

👆🏻 로그인 화면


✔️ DB값 검증

  1. 사용자가 입력한 ID를 기반으로 DB에서 회원 정보 조회
  2. 조회되는 정보가 있다면 사용자가 입력한 비밀번호와 DB에 암호화되어 저장된 비밀번호의 일치 여부 확인
  3. 비밀번호가 일치하다면 session에 저장
    3-1. 관리자일 경우 관리자 페이지로 이동
    3-2. 일반회원일 경우 메인 페이지로 이동

👨‍💻code

MemberController
    @PostMapping("/login")    // 사용자가 입력한 ID와 PW를 담는 Map
    public ModelAndView login(CommitMap commitMap,
                              HttpServletRequest request,
                              HttpSession session) throws Exception {

        ModelAndView mv = new ModelAndView();
                                                       // 회원정보 조회
        Map<String, Object> memberInfo = memberService.getMemberDetail(commitMap.getMap(), request);

        // 아이디를 잘못 입력했을경우
        if (memberInfo == null) {
            mv.addObject("msg", "아이디 또는 비밀번호를 잘못 입력했습니다.");
            mv.addObject("path", "/member/login");
            mv.setViewName("/alert/alert");
            return mv;
        }

        // 간편 로그인 회원이라면
        if (memberInfo.get("OAUTH") != null) {
            mv.addObject("msg", "간편 로그인을 이용해주세요.");
            mv.addObject("path", "/member/login");
            mv.setViewName("/alert/alert");
            return mv;
        }

        // 사용자가 입력한 비밀번호
        String rowPassword = commitMap.get("MEM_PW").toString();

        // DB에 암호화되어 저장된 비밀번호
        String encodePassword = memberInfo.get("MEM_PW").toString();
  
        // BCryptPasswordEncoder로 일치 여부 확인
        if (encoder.matches(rowPassword, encodePassword)) {
            // 관리자
            if (memberInfo.get("ADMIN").equals("Y")) {
                session.setAttribute("MEM_ID", memberInfo.get("MEM_ID"));
                session.setAttribute("MEM_NAME", memberInfo.get("MEM_NAME"));
                session.setAttribute("admin", memberInfo.get("ADMIN"));
                mv.setViewName("redirect:/admin/main");
            // 일반 회원
            } else {
                session.setAttribute("MEM_ID", memberInfo.get("MEM_ID"));
                session.setAttribute("MEM_NAME", memberInfo.get("MEM_NAME"));
                mv.setViewName("redirect:/main");
            }
          // 비밀번호 불일치
        } else {
            mv.addObject("msg", "아이디 또는 비밀번호를 잘못 입력했습니다.");
            mv.addObject("path", "/member/login");
            mv.setViewName("/alert/alert");
        }

        return mv;
    }
MemberServiceImpl
    @Override
    public Map<String, Object> getMemberDetail(Map<String, Object> map, HttpServletRequest request) throws Exception {

        /* 다른 Controller(ex. MyController)에서 getMemberDetail메소드를 호출할 때는 map에 MEM_ID가 담겨있지 않음. 대신 request를 통해 session에서 MEM_ID 값을 얻어옴. 
        (로그인 요청시에만 map에 MEM_ID가 담겨있음) */
        if (map.get("MEM_ID") == null) {
            map = sessionHelper.make(map, request);
        }

        Map<String, Object> memberInfo = memberDAO.selectMemberDetail(map);

        if (memberInfo != null) {
            if (memberInfo.get("ZIPCODE") != null && !"".equals(memberInfo.get("ZIPCODE").toString())) {
                // 주소 분리 작업(MyPage 정보 수정에서 사용) - ADDRESS컬럼에 '도로명주소 + | + 상세주소'로 들어가 있음.
                String totalAddress = memberInfo.get("ADDRESS").toString();
                int index = totalAddress.indexOf("|");
                String loadAddress = totalAddress.substring(0, index);
                String addressDetail = totalAddress.substring(index + 1);

                memberInfo.put("ROAD_ADDRESS", loadAddress);
                memberInfo.put("ADDRESS_DETAIL", addressDetail);

            }

        }

        return memberInfo;

    }
Member_SQL.xml
        SELECT MEM_ID,
               MEM_PW,
               MEM_NAME,
               PHONE,
               ZIPCODE,
               ADDRESS,
               EMAIL,
               EMAIL_AGREE,
               ADMIN,
               OAUTH
        FROM MEMBER
        WHERE MEM_ID = LOWER(#{MEM_ID})
          AND DEL = 'N'

✔️ ID 찾기

사용자가 입력한 이름과 전화번호로 DB에서 일치하는 회원이 있는지 조회 후 사용자의 아이디를 보여준다.

👨‍💻code

MemberController
    @PostMapping("/findID")
    public ModelAndView findID(CommitMap commitMap, HttpServletRequest request) throws Exception {
        
        // 조회되는 회원이 있다면 1 없다면 0 
        int member = memberService.findMemberByInfo(commitMap.getMap(), request);
        ModelAndView mv = new ModelAndView();

        if (member == 0) {
            mv.addObject("msg", "해당 정보를 가진 회원이 없습니다.");
            mv.addObject("path", "/member/findID");
            mv.setViewName("/alert/alert");
        } else {
            String findID = memberService.findID(commitMap.getMap());
            mv.addObject("MEM_ID", findID);
            mv.setViewName("/member/successFindID");
        }
        return mv;
    }
MemberServiceImpl
    @Override
    public int findMemberByInfo(Map<String, Object> map, HttpServletRequest request) {

        String requestURI =  request.getRequestURI();
        // map에 findID라는 값을 넣는 이유는 코드 1-2를 참고해주세요.
        if("/member/findID".equals(requestURI)){
            map.put("findID", "findID");
        }

        return memberDAO.selectMemberByInfo(map);
    }

코드 1-1

Member_SQL.xml
        SELECT COUNT(*)
        FROM MEMBER
        WHERE
        <if test="findID != NULL">
            MEM_NAME = #{MEM_NAME} AND PHONE = #{PHONE}
        </if>
        <if test="findID == NULL">
            MEM_ID = LOWER(#{MEM_ID}) AND EMAIL = LOWER(#{EMAIL})
        </if>

코드 1-2

  • 파라미터로 들어오는 map에 FindID라는 값이 있다면 - 아이디 찾기(이름과 전화번호로 회원 조회)

  • 파라미터로 들어오는 map에 FindID라는 값이 없다면 - 비밀번호 찾기(아이디와 이메일로 회원 조회)

🖥️view

ID 찾기 결과 아이디_찾기_결과

✔️ E-mail로 임시 비밀번호 발급

사용자가 입력한 아이디와 이메일로 DB에서 일치하는 회원이 있는지 조회 후 회원가입시 입력한 이메일로 임시 비밀번호를 발급한다.

👨‍💻code

MemberController
    @PostMapping("/findPW")
    public ModelAndView findPW(CommitMap commitMap, HttpServletRequest request) throws Exception {
        // 조회되는 회원이 있다면 1 없다면 0 
        int member = memberService.findMemberByInfo(commitMap.getMap(), request);
        ModelAndView mv = new ModelAndView();

        if (member == 0) {
            mv.addObject("msg", "해당 정보를 가진 회원이 없습니다.");
            mv.addObject("path", "/member/findPW");
            mv.setViewName("/alert/alert");
        } else {
                          //메일 전송
            memberService.sendEmail(commitMap.getMap());
            mv.addObject("msg", "임시 비밀번호가 이메일로 전송되었습니다.");
            mv.setViewName("/alert/windowClose");
        }

        return mv;
    }
MemberServiceImpl
    @Override
    public int findMemberByInfo(Map<String, Object> map, HttpServletRequest request) {

        String requestURI =  request.getRequestURI();
        // map에 findID라는 값을 넣는 이유는 ID 찾기 파트의 코드 1-2를 참고해주세요.
        if("/member/findID".equals(requestURI)){
            map.put("findID", "findID");
        }

        return memberDAO.selectMemberByInfo(map);
    }

회원정보 조회

    @Override
    public void sendEmail(Map<String, Object> map) throws Exception {
                        // UUID
        String tempPW = CommitUtils.getRandomString().substring(0, 10);
        // 임시 비밀번호 암호화
        String encodePassword = encoder.encode(tempPW);
        map.put("TEMP_PW", encodePassword);
        // 암호화된 임시 비밀번호로 회원의 기존 비밀번호 변경
        memberDAO.updateTempPW(map);

        String lineSeparator = System.lineSeparator() + System.lineSeparator();
        String email = map.get("EMAIL").toString();
        String commitEmail = "commitpcShop@gmail.com";
        String title = "[Commit] 🔑 임시비밀번호 보내드립니다.";
        String content = "안녕하세요 Commit입니다." + lineSeparator + "발급된 임시비밀번호를 보내드립니다." + lineSeparator + tempPW;
                    // 메일 전송
        mailService.sendEmail(email, commitEmail, title, content);
    }

임시 비밀번호 메일 전송

📩 MailService
	public void sendEmail(String toAddress, String fromAddress, String subject, String msgBody) {
		
        SimpleMailMessage smm = new SimpleMailMessage();
        //수신자 설정
		smm.setTo(toAddress);
        //발신자 설정
		smm.setFrom(fromAddress);
        //메일 제목 설정
		smm.setSubject(subject);
        //메일 내용 설정
		smm.setText(msgBody);
		
		mailSender.send(smm);
	}

실제 메일 전송 코드

Member_SQL.xml
        SELECT COUNT(*)
        FROM MEMBER
        WHERE
        <if test="findID != NULL">
            MEM_NAME = #{MEM_NAME} AND PHONE = #{PHONE}
        </if>
        <if test="findID == NULL">
            MEM_ID = LOWER(#{MEM_ID}) AND EMAIL = LOWER(#{EMAIL})
        </if>

회원정보 조회

        UPDATE
            MEMBER
        SET MEM_PW = #{TEMP_PW}
        WHERE MEM_ID = LOWER(#{MEM_ID})

임시 비밀번호로 회원의 기존 비밀번호 변경

🖥️view

임시 비밀번호 메일 전송 임시비밀번호_전송
실제 받은 메일 임시비밀번호

✔️ 카카오 API 로그인

  1. 카카오 로그인 API를 연동하여 사용자에 대한 인증, 인가 처리
  2. 전달받은 인가코드로 액세스 토큰 요청
  3. 액세스 토큰을 통해 얻은 사용자의 정보를 조합하여 사용자의 아이디 생성
  4. 생성된 아이디로 기존 회원인지 신규 회원인지 확인
    4-1. 기존 회원일 경우 로그인 처리
    4-2 신규 회원일 경우 회원가입 페이지로 이동

👨‍💻code

KakaoLoginController
    @GetMapping("/oauth/login")
    public String oAuthLogin(String code, Model model, HttpSession session, HttpServletRequest request) throws Exception {


        // 인가코드로 액세스 토큰 발급받기
        String accessToken = kaKaoLoginService.getAccessToken(code);
        // 액세스 토큰으로 userInfo 얻기
        KakaoUserInfo userInfo = kaKaoLoginService.getUserInfo(accessToken);
        // 가입된 회원인지 확인
        int result = kaKaoLoginService.joinCheck(userInfo);

        // 가입된 회원이 아님(신규회원)
        if (result == 0) {
            model.addAttribute("userInfo", userInfo);
            return "member/oauthJoinForm";
        // 이미 가입된 회원
        } else {
            Map<String, Object> memberID = kaKaoLoginService.makeUserId(userInfo);
            // 회원정보를 꺼내옴
            Map<String, Object> memberInfo = memberService.getMemberDetail(memberID, request);
            if (memberInfo != null) {
                // 로그인 처리 
                kaKaoLoginService.kakaoLogin(memberInfo, session);
            }

            return "redirect:/";
        }

    }
    @PostMapping("/oauth/member/join")
    public String join(CommitMap commitMap, HttpSession session) throws Exception {

        // 회원가입
        kaKaoLoginService.join(commitMap.getMap());
        // 회원가입 후 로그인 처리
        kaKaoLoginService.kakaoLogin(commitMap.getMap(), session);

        return "redirect:/";
    }

신규 회원일 경우 회원가입

AppConfig
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public HttpHeaders header() {
        return new HttpHeaders();
    }

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

Bean 등록

KakaoLoginService
    private final RestTemplate restTemplate;

    private final HttpHeaders header;

    @Value("${kakao.clientId}")
    private String clientId;

    @Value("${kakao.redirectURI}")
    private String redirectURI;

    public KakaoLoginService(RestTemplate restTemplate, HttpHeaders header){
        this.restTemplate = restTemplate;
        this.header = header;
    }

객체 주입 및 properties파일 값 주입

    public String getAccessToken(String code) {

        // 토큰 발급을 위한 요청 주소
        String requestURI = "https://kauth.kakao.com/oauth/token";

        // 요청 header 설정
        header.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        // 요청 body
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code");
        body.add("client_id", clientId);
        body.add("redirect_uri", redirectURI);
        body.add("code", code);

        // 요청 주소에 보낼 entity
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, header);

        AccessTokenInfo tokenInfo = restTemplate.exchange(requestURI, HttpMethod.POST, request, AccessTokenInfo.class).getBody();

        return tokenInfo.getAccess_token();
    }

액세스 토큰 발급

    public KakaoUserInfo getUserInfo(String accessToken) {


        String requestURI = "https://kapi.kakao.com/v2/user/me";

        header.add("Authorization","Bearer "+accessToken);

        HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(header);

        KakaoUserInfo userInfo = restTemplate.exchange(requestURI, HttpMethod.GET, request, KakaoUserInfo.class).getBody();
        // 각 요청마다 같은 header 객체를 공유하기 때문에 clear 해줘야 함.
        header.clear();
        return userInfo;
    }

액세스 토큰으로 사용자 정보 가져오기

    public Map<String,Object> makeUserId(KakaoUserInfo userInfo){

        Map<String, Object> map = new HashMap<>();
        String memId = userInfo.getId() +"_"+ userInfo.getProperties().getNickname() +"_"+ "oauth";
        map.put("MEM_ID", memId);
        return map;

    }

사용자의 정보를 조합하여 사용자의 아이디 생성

  public int joinCheck(KakaoUserInfo userInfo) throws Exception {

        Map<String, Object> map = makeUserId(userInfo);

        // 이미 가입이 되어있다면 1 신규 회원이라면 0
        return memberService.confirmId(map);

    }

가입된 회원인지 확인

  public void join(Map<String, Object> map) throws Exception {
        
        // 아이디 생성(id, nickname 활용 - makeUserId메소드와 생성 규칙 동일)
        String id = map.get("ID").toString();
        String mem_name = map.get("MEM_NAME").toString();
        String mem_id = id +"_"+ mem_name +"_oauth";
        map.put("MEM_ID", mem_id);

        
        // oauth 필드(enum 사용)
        map.put("OAUTH", Oauth.KAKAO);
        // 회원가입
        memberService.joinMember(map);

    }

회원가입

    public void kakaoLogin(Map<String, Object> map, HttpSession session) {

        session.setAttribute("MEM_ID",map.get("MEM_ID"));
        session.setAttribute("MEM_NAME", map.get("MEM_NAME"));
    } 

로그인 처리

Member_SQL.xml
        SELECT COUNT(MEM_ID)
        FROM MEMBER
        WHERE MEM_ID = LOWER(#{MEM_ID})

🖥️view

카카오 API 로그인 창 카카오_로그인_창
카카오 API 동의화면 카카오_동의화면
카카오 로그인 회원가입 창 카카오_회원가입