ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [기능구현#1] 로그인 기능
    프로젝트/게시판 프로젝트 2024. 11. 20. 20:12


    로그인 서비스 흐름도

    로그인 인증)
    - 로그인 인증의 경우, 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를 운용하지 못한점을 보완하고 싶다 (ex_ redis)
    - redis 사용이유 : in-memory기능으로서 굳이 DB에 쌓는 것이아닌 세션 값이기에 빠른 조회 및 탐색을 위해 in-memory 방식을 택하고자 함

    2. URL를 통한 계정 이동을 방지하고자, Interceptor에 추가하였지만 Spring security를 이용하여 안정적으로 서비스를 제공하고 싶다
Designed by Tistory.