JAVA/SpringBoot

게시판 프로젝트 - 로그인/ 로그아웃/ 로그인 세션 체크 기능

whyHbr 2023. 11. 15. 19:18
728x90
반응형

로그인, 로그아웃 기능을 구현하고, 인터셉터를 이용해 로그인이 되어있지 않은 경우, 게시판에 접근 할 수 없게 구현할 것

 

- MemberService 로그인 메서드 추가

public MemberResponse findMemberByLoginId(final String loginId) {
    return memberMapper.findByLoginId(loginId);
}

/**
 * 회원 정보 수정
 * @param params - 회원 정보
 * @return PK
 */
@Transactional
public Long updateMember(final MemberRequest params) {
    params.encodingPassword(passwordEncoder);
    memberMapper.update(params);
    return params.getId();
}

/**
 * 회원 정보 삭제 (회원 탈퇴)
 * @param id - PK
 * @return PK
 */
@Transactional
public Long deleteMemberById(final Long id) {
    memberMapper.deleteById(id);
    return id;
}

 

회원정보 수정 : 로그인 페이지에 입력한 아이디와 비밀번호를 전달받아 회원 정보를 조회한다.

encodingPassword는 회원 테이블에 암호화된 비밀번호이다. member가 null인 경우에 member.getPassword()를 실행시 NPE가 발생하기 때문에 빈문자열(" ")로 처리한다

 

 회원 정보 및 비밀번호 체크 :passwordEncoder의 matches()로 사용자가 입력한 비밀번호와 회원 테이블에 암호화된 비밀번호를 비교한다. 두 조건 모두 false인 경우가 정상적인 케이스이다.

 

회원 정보 삭제(회원 탈퇴) :member.clearPassword() 로 암호화된 회원의 비밀번호를 초기화 (" " ) 한 후 회원 응답 객체를 리턴한다.

 

-MemberController 로그인 로그아웃 메서드 추가하기

// 로그인
    @PostMapping("/login")
    @ResponseBody
    public MemberResponse login(HttpServletRequest request) {

        // 1. 회원 정보 조회
        String loginId = request.getParameter("loginId"); //이 정보를 받아오기
        String password = request.getParameter("password");
        MemberResponse member = memberService.login(loginId, password);

        // 2. 세션에 회원 정보 저장 & 세션 유지 시간 설정
        if (member != null) {
            HttpSession session = request.getSession();
            session.setAttribute("loginMember", member); //사용자 정보 세션에 저장
            session.setMaxInactiveInterval(60 * 30);
        }

        return member;
    }

    // 로그아웃
    @PostMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/login.do";
    }
}

 

메서드

login : 1.request 객체의 getParameter() 사용자가 로그인 페이지에 입력한 아이디와 비밀번호를 변수에 담아 회원 정보를 조회한다.

logout() : session.invaildate() 로 세선을 무효화( 초기화) 한 후 사용자를 로그인 페이지로 이동시킵니다.

 

- 로그인(세션) 체크용 인터셉터 추가하기

로그인 상태를 확인할 인터셉터를 추가합니다.

package com.study.interceptor;

import com.study.domain.member.MemberResponse;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 1. 세션에서 회원 정보 조회
        HttpSession session = request.getSession();
        MemberResponse member = (MemberResponse) session.getAttribute("loginMember");

        // 2. 회원 정보 체크
        if (member == null || member.getDeleteYn() == true) {
            response.sendRedirect("/login.do");
            return false;
        }

        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

}

1 : MemberController의 login() 에서세션에 저장한 회원 정보를 조회합니다

2 : member객체가 null이면 로그인이 되어있지 않음을 의미한다. 2번의 조건이 하나라도 true인 경우에는 사용자를 로그인 페이지로 이동시키는데 이를 통해 url을 입력해 강제로 게새판에 접근하는 사용자를 막을 수 있다.

 

-애플리케이션에 인터셉터 등록하기

package com.study.config;

import com.study.interceptor.LoggerInterceptor;
import com.study.interceptor.LoginCheckInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggerInterceptor())
                .excludePathPatterns("/css/**", "/images/**", "/js/**");

        registry.addInterceptor(new LoginCheckInterceptor())
                .addPathPatterns("/**/*.do")
                .excludePathPatterns("/log*");
    }

}

앞에 추가한 인터셉터 클래스도 스프링이 인식할 수 있도록 config클래스에 등록해준다. 

 

메서드

addInterceptors() : 이전에 로그용 인터셉터를 처리하는 과정에서 구현한 오버라이드 메서드이다. 애플리케이션내의 모든 페이지 URI에 접근할 떄 LoginCheckInterceptor의 preHandle()이 작동한다. excludePathPatterns()을 이용해 로그인 페이지와 로그인, 로그아웃 URI인터셉터 실행에서 제외시킨다 만약 로그인, 로그아웃 관련 uri를 호출했을 때 인터셉터가 작동하게 되면 LoginCheckInterceptor의 preHandle()에 의해 계속해서 로그인 페이지를 호출하는 무한 루프에 빠지게 된다.

 

- login.html 로그인 함수 추가하기

// Enter 로그인 이벤트 바인딩
window.onload = () => {
   document.querySelectorAll('#loginId, #password').forEach(element => {
      element.addEventListener('keyup', (e) => {
         if (e.keyCode === 13) {
            login();
         }
      })
   })
}


// 로그인
function login() {

   const form = document.getElementById('loginForm');

   if ( !form.loginId.value || !form.password.value ) {
      alert('아이디와 비밀번호를 모두 입력해 주세요.');
      form.loginId.focus();
      return false;
   }

   $.ajax({
      url : '/login',
      type : 'POST',
      dataType : 'json',
      data : {
         loginId: form.loginId.value,
         password: form.password.value
      },
      async : false,
      success : function (response) {
         location.href = '/post/list.do';
      },
      error : function (request, status, error) {
         alert('아이디와 비밀번호를 확인해 주세요.');
      }
   })
}

로그인 페이지에 MemberController의 login()을 호출할 onload()와 login()함수를 추가한다.

 

함수

onload() : 해당 함수 안의 로직은 로그인 페이지의 아이디와 패스워드 필드에서 엔터 입력 시 login()을 호출할 수 있도록 이벤트를 바인딩하는 역할을 합니다

login() : MemberController의 login()을 호출해서 로그인을 시도한다. 로그인에 성공ㅎ아면 게시글 리스트 페이지로 이동하고, 실패했을 땐 error()안 alert메시지를 보내준다.

이 함수는 로그인에 실패했을 때 에러가 발생하는데 @ResponseBody가 선언된 컨트롤러의 메소드가 null을 리턴하면 Ajax호출 시 에러가 발생하기 때문에 callAPi()를 사용하지 않고 Ajax 함수를 따로 선언해주었다. 

 

- body.html 회원이름 출력, 로그아웃 폼 추가

<header>
    <div class="head">
        <h1>대림 게시판 </h1>
        <div th:if="${session.loginMember != null}" class="top_menu">
            <div class="login_user"><strong><i class="far fa-user-circle"></i> [[ ${session.loginMember.name} ]]</strong>님 반갑습니다.</div>
            <div class="logout">
                <form action="/logout" method="post">
                    <button type="submit"><span class="skip_info">로그아웃</span><i class="fas fa-sign-out-alt"></i></button>
                </form>
            </div>
        </div>
    </div>
</header>

 

태그 

div.top_menu : 로그인된 경우에만 출력되는 태그,

form : 로그아웃action을 호출하는 폼 태그이다. MemberController의 logout을 호출해 세선을 초기화한다.

728x90