본문 바로가기
프로젝트/게시판 프로젝트

[기능구현#3] 로그인 기능

by 거북이의 기술블로그 2024. 11. 20.


로그인 서비스 흐름도

로그인 인증)
- 로그인 인증의 경우, Filter와 Interceptor를 이용하여 구현할 수 있다
- Filter의 경우, spring의 Distpatcher에 들어가기 전에 필터링이 가능하므로 주로 Filter에서 로그인 인증이 일어난다
  • 현재 프로젝트 적용
    • 1차 로그인 인증 (Filter)
      • Session값의 유무를 통해, Session을 가지고 있으면 Filter 통과
    • 2차 로그인 인증 (Interceptor)
      • Session 값 유무 판단 + URL 변경을 통한 사용자 인증을 막고자 정규표현식을 이용한 필터링 진행
      • ex) http://localhost/members/test1 -> http://localhost/members/admin (이동 방지)

 

로그인 인증 방법

1) JWT
2) Session
  • JWT
    • JWT의 경우, 멤버 객체의 특정 값들을 조합하여 토큰을 발행 후, 클라이언트에게 전달하여 인증하는 방법
    • 장점 : 서버의 저장 리소스를 줄일 수 있음 (클라이언트가 저장하고 있기에) -> Session 방식과 차이점
    • 단점 : 유효기간이 지나기 전까지는 클라이언트가 탈취를 당하더라도 해결하는 방법이 적절치 못함
  • Session
    • Session의 경우, 인스턴스당 session값을 매칭하여 session store에 서버가 저장한 후 인증 하는 방법
    • 장점 : 세션 관리가 용이함
    • 단점 : 서버의 리소스를 소모하므로, 너무 많은 인원의 경우 저장하는데 많은 리소스를 소모할 수 있음
  • 현재 프로젝트 적용
    • Http Session을 사용하여, member 객체의 정보를 이용해 session 생성 후 인증진행

 

주요 구현 부분

  • Filter 인증 부분
    • getSession(false)를 통해, 세션이 있는지 확인 후 없으면 로그인화면으로 리다이렉트 진행
try{
            log.info("인증체크 필터 시작 {}", requestURI);

            if(loginCheckPath(requestURI)){
                log.info("인증체크 로직 실행 {} ", requestURI);
                HttpSession session = httpRequest.getSession(false);

                if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null){
                    log.info("미인증 사용자 요청 {}", requestURI);
                    httpResponse.sendRedirect("/members/login?redirectURL=" + requestURI);
                    return;
                }
            }
            chain.doFilter(request,response);
        }catch (Exception e){
            throw e;
        }finally{
            log.info("인증 체크 필터 종료 {} ", requestURI);
        }
  • Interceptor 인증 부분
    • session값에 저장된 Member와 URL에 나와있는 memberId와 다를 경우, 로그인화면으로 리다이렉트
if(matcher.find()){
            String account = matcher.group(1);

            Member authAccount = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);

            if (authAccount.getLoginId() == null ||  !authAccount.getLoginId().equals(account)){
                response.sendRedirect("/members/login?redirectURI="+requestURI);
                log.info("Session loginId: {}, URL account: {}", authAccount.getLoginId(), account);
                return false;
            }
  • Filter와 Interceptor 등록
    @Bean
    public FilterRegistrationBean loginCheckFilter(){
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new LoginCheckFilter());
        filterFilterRegistrationBean.setOrder(2);
        filterFilterRegistrationBean.addUrlPatterns("/*");
        filterFilterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);
        return filterFilterRegistrationBean;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/*.ico", "/error", "/errors/custom/**");

        registry.addInterceptor(new LoginCheckInterceptor())
                .order(2)
                .addPathPatterns("/**")
                .excludePathPatterns("/members/*/boards/*","/members/login", "/members/form", "/members/logout", "/css/**", "/*.ico", "/error","/errors/custom/**");

    }
  • 로그인 인증 서비스 부분
    • 로그인이 성공적으로 될경우, Session 발급
if (loginService.loginCheck(loginForm.getLoginId(), loginForm.getPasswd())){
            HttpSession session = request.getSession(true);
            Member loginMember= memberRepository.findByLoginId(loginForm.getLoginId());
            session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
            return "redirect:/members/" + loginForm.getLoginId();
    }

 

아쉬운점

1. HttpSession이 아닌, session store를 운용하지 못한점을 보완하고 싶다 
2. URL를 통한 계정 이동을 방지하고자, Interceptor에 추가하였지만, URL상에 개인정보가 유출되는 문제도 있고, 사용자가 너무 쉽게 유추하기가 쉬우므로 수정하고자 한다