본문 바로가기
스프링

로그인

by moonstal 2022. 6. 18.

로그인 - 쿠키, 세션

  • domain은 web을 참조하면 안된다.

      // LoginService MemberRepository주입
      public Member login(String loginId, String password) {
          return memberRepository.findByLoginId(loginId)
          .filter(m -> m.getPassword().equals(password))
          .orElse(null);
      }
    
      //LoginController - login()
      Member loginMember = loginService.login(form.getLoginId(),form.getPassword());
      if (loginMember == null) {
          bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
          return "login/loginForm";
      }

로그인 상태 유지 - 쿠키 사용

  • 영속 쿠키: 만료 날짜 까지

  • 세션 쿠키: 만료 날짜 생략, 브라우저 종료시 까지

  • 세션 쿠키 생성

      //LoginController - login() HttpServletResponse response 추가
      ~로그인 성공로직~
      Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
      response.addCookie(idCookie);
    
      //HomeController 
      @CookieValue(name = "memberId", required = false) Long memberId
      // memberId,loginMember null이면 그냥화면
      //로그인 됐으면 
      model.addAttribute("member", loginMember); -> loginHome
      th:text="|로그인: ${member.name}|"
  • 로그아웃

      private void expireCookie(HttpServletResponse response, String cookieName) {
          Cookie cookie = new Cookie(cookieName, null);
          cookie.setMaxAge(0);
          response.addCookie(cookie);
      }

보안 문제

  • 쿠키 값 변경 가능
  • 쿠키에 보관된 정보 훔쳐갈 수 있음
  • 쿠키 평생 사용 가능
  • 임의의 토큰 노출, 서버에서 토큰과 사용자 id 매핑, 토큰 만료시간

로그인 - 세션

  • 세션: 서버에 중요한 정보를 보관하고 연결을 유지하는 방법

      @Component
      SessionManager 
      Map<String, Object> sessionStore
    
      //세션 생성
      //세션 id생성
      String sessionId = UUID.randomUUID().toString();
      sessionStore.put(sessionId, value);
      // 쿠키 생성
      Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId);
      response.addCookie(mySessionCookie);
    
      //세션 조회
      Cookie sessionCookie = Arrays.stream(request.getCookies())
          .filter(cookie -> cookie.getName().equals(cookieName))
          .findAny()
          .orElse(null);
      sessionStore.get(sessionCookie.getValue());
    
      //세션 만료
      쿠키 찾고->sessionStore.remove(sessionCookie.getValue());

세션 적용

    LoginController
    private final SessionManager sessionManager;

    // login
    sessionManager.createSession(loginMember, response);

    //logout
    sessionManager.expire(request);

    HomeController
    private final SessionManager sessionManager;
    Member member = (Member)sessionManager.getSession(request);
    model.addAttribute("member", member);

서블릿 HTTP 세션1

    LoginController

    // login
    //세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성(true)<->null(false)
    HttpSession session = request.getSession(); //true
    //세션에 로그인 회원 정보 보관
    session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);

    //logout
    HttpSession session = request.getSession(false);
    if (session != null) {
        session.invalidate();//세션 제거
    }

    HomeController
    HttpSession session = request.getSession(false); //session null체크
    Member loginMember = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);
    //loginMembers null체크
    model.addAttribute("member", loginMember);

서블릿 HTTP 세션2

  • @SessionAttribute
      HomeController
      @SessionAttribute(name = "loginMember", required = false) Member loginMember

세션 정보와 타임아웃 설정

  • sessionId,maxInactiveInterval,creationTime,lastAccessedTime,isNew
  • 타임아웃: 서버에 최근에 요청한 시간을 기준으로 30분
  • server.servlet.session.timeout=1800(30분)

로그인 - 필터, 인터셉터

서블릿 필터

  • 로그인 여부를 체크하는 로직-> 웹과 관련된 공통 관심사-> HTTP의 헤더나 URL의 정보들이 필요 -> HttpServletRequest 제공

  • 서블릿 필터(수문장): HTTP 요청 -> WAS -> 필터 -> 디스패처 서블릿 -> 컨트롤러

      // Filter의 doFilter구현
      try {
          if (isLoginCheckPath(requestURI)) {
    
              HttpSession session = httpRequest.getSession(false);
    
              if (session == null ||session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
                  httpResponse.sendRedirect("/login?redirectURL=" +requestURI);//로그인 화면 갔다가 다시와
                  return; 
              }
          }
          chain.doFilter(request, response);
      }
    
      // WebConfig - loginCheckFilter()
      FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
      filterRegistrationBean.setFilter(new LoginCheckFilter());
      filterRegistrationBean.setOrder(2);
      filterRegistrationBean.addUrlPatterns("/*");//모든 요청에 로그인 필터
      return filterRegistrationBean;
    
      // LoginController
      @RequestParam(defaultValue = "/") String redirectURL
      return "redirect:" + redirectURL;

스프링 인터셉터

  • HTTP 요청 -> WAS -> 필터 -> 디스패처 서블릿 -> 스프링 인터셉터 -> 컨트롤러
  • preHandle -> handler -> 핸들러 어댑터 -> 핸들러 (컨트롤러) -> ModelAndView -> postHandle -> render(Model) -> afterCompletion
    //HandlerInterceptor preHandle구현
    String requestURI = request.getRequestURI();
    HttpSession session = request.getSession(false);
    if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)== null) {
    response.sendRedirect("/login?redirectURL=" + requestURI);
    return false;
    }

    //WebMvcConfigurer addInterceptors구현
    registry.addInterceptor(new LoginCheckInterceptor())
        .order(2)
        .addPathPatterns("/**")
        .excludePathPatterns(
            "/", "/members/add", "/login", "/logout",
            "/css/**", "/*.ico", "/error"
        );

ArgumentResolver 활용

    //HomeController
    @Login Member loginMember

    //@Login
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Login {
    }

    //HandlerMethodArgumentResolver구현
    supportsParameter:@Login,Member 타입 체크
    resolveArgument:
    HttpSession session = request.getSession(false);
    if (session == null) {
    return null;
    }
    return session.getAttribute(SessionConst.LOGIN_MEMBER);

    //WebMvcConfigurer에 등록
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginMemberArgumentResolver());
    }

링크

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2

'스프링' 카테고리의 다른 글

타입 컨버터  (0) 2022.06.20
예외 처리  (0) 2022.06.19
검증  (0) 2022.06.17
타임리프  (0) 2022.06.16
[스프링] 스프링 MVC  (0) 2022.06.09