스프링 인증블로그 (1) 로그인, 필터

gov's avatar
Nov 27, 2024
스프링 인증블로그 (1) 로그인, 필터
💡
  • 인증/인가
  • 필터

목표

  • 인증(Authentication) 사용자가 누구인지 확인하는 과정 (ex. 로그인)
  • 인가(Authorization) 인증된 사용자가 어떤 권한을 가지는지 확인하는 과정 (ex. 관리자 권한)
인증은 "누구인가?", 인가는 "무엇을 할 수 있는가?"에 초점
  • 필터 (Filter)
    • 요청(Request)와 응답(Response)에 대해 중간에서 처리 로직을 수행하는 인터페이스
    • 주요 용도: 인증, 로깅, CORS 처리 등
  • 연관관계 (조인)
    • 연관관계: 엔티티 간의 관계를 설정 (ex. 1:1, 1:N, N:M)
    • 조인: 관계형 DB에서 연관된 데이터를 함께 조회하기 위해 사용하는 SQL 연산
    • 스프링 JPA에서 연관관계를 설정하면, @OneToOne, @ManyToOne 등으로 조인을 자동화
 

세팅

1. 의존성 세팅

notion image

2. 클래스 의존성

  1. 어플리케이션
  1. 패키지 및 클래스 생성과 의존성
    1. 레파지토리(엔티티), 서비스(레파지토리), 컨트롤러(서비스)
  1. 레파지토리 쿼리문 작성 > 만든 쿼리문 테스트

기능 구현

1. 로그인

섹션 흐름
@RequiredArgsConstructor @Repository public class UserRepository { // DB조회해서 가져오기 private final EntityManager em; public User findByUsername(String username) { // 유저클래스로 바로 캐스팅 Query q = em.createQuery("select u from User u where u.username = :username", User.class); // JPQL쿼리문 q.setParameter("username", username); try { return (User) q.getSingleResult(); } catch (RuntimeException e) { throw new RuntimeException("아이디 혹은 패스워드가 일치하지 않습니다"); } } } ---- public class UserRequest { @Data public class LoginDTO { private String username; private String password; } } ---- @RequiredArgsConstructor @Controller public class UserController { private final UserSevice userService; // 다른 방법을 써도 final 반드시 붙이기 private final HttpSession session; @PostMapping("/user") public String login(UserRequest.LoginDTO loginDTO) { User sessionUser = userService.로그인(loginDTO); session.setAttribute("sessionUser", sessionUser); return "redirect:/"; // 로그인되면 메인페이지 이동 } @ResponseBody @GetMapping("/test/auth") public String testAuth() { User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { return "인증안됨"; } return "인증됨 : "+sessionUser.getUsername(); } } ---- @RequiredArgsConstructor @Service public class UserSevice { private final UserRepository userRepository; public User 로그인(UserRequest.LoginDTO loginDTO) { User userPS = userRepository.findByUsername(loginDTO.getUsername()); if(!userPS.getPassword().equals(loginDTO.getPassword())) { throw new RuntimeException("아이디 혹은 패스워드가 일치하지 않습니다"); // 로그를 남겨서 서버에 패스워드가 실패 카운팅 } return userPS; } }
 

2. 필터

💡
필터 정의
  • 필터 클래스 생성 FilterConfigAuthenticationFilter를 애플리케이션에 적용하기 위한 설정을 담당하며, AuthenticationFilter는 해당 설정을 기반으로 인증 검사를 수행.
AuthenticationFilter 클래스
public class AuthenticationFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; HttpSession session = req.getSession(); // 리퀘스트 객체에서 세션 가져옴 User sessionUser = (User) session.getAttribute("sessionUser"); if(sessionUser != null) { resp.sendRedirect("/login-form"); } else { chain.doFilter(request, response); // null이 아니면 다음 필터로 } } }
역할
  • HTTP 요청에서 세션 정보를 확인해 인증 여부를 판단.
  • 인증되지 않은 사용자는 로그인 화면(/login-form)으로 리다이렉트.
  • 인증된 사용자는 요청 처리를 다음 필터나 컨트롤러로 넘김.
 
동작 흐름
  1. 요청/응답 객체 형변환
    1. HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response;
      전달받은 ServletRequestServletResponse를 각각 HTTP 프로토콜에 맞게 HttpServletRequestHttpServletResponse로 형변환.
  1. 세션 객체 가져오기
    1. HttpSession session = req.getSession(); User sessionUser = (User) session.getAttribute("sessionUser")
      • req.getSession()으로 현재 요청의 세션 객체를 가져옴.
      • 세션에서 sessionUser 속성을 조회해 유저 정보를 확인.
  1. 인증 여부 확인
    1. if(sessionUser != null) { resp.sendRedirect("/login-form"); } else { chain.doFilter(request, response); }
      sessionUser != null (세션에 유저 정보가 있음):
      • 이미 로그인된 상태 → **"/login-form"**으로 리다이렉트.이는 로그인 상태에서 불필요한 접근을 막는 용도.
      sessionUser == null (세션에 유저 정보가 없음):
      • 로그인되지 않은 상태 → 다음 필터 또는 컨트롤러로 요청 전달 (chain.doFilter()).
FilterConfig 클래스
@Configuration // 메서드 때리기 public class FilterConfig { @Bean // 리턴값이 IoC에 등록됨 public FilterRegistrationBean addAuthFilter() { FilterRegistrationBean rg = new FilterRegistrationBean(); rg.setFilter(new AuthenticationFilter()); rg.addUrlPatterns("/board/*"); // 주소가 board 이면 허용 rg.addUrlPatterns("/user/*"); rg.setOrder(1); // 필터 순서 정하기 // 해당 메서드 실행시, 리턴되는 객체를 IOC 컨테이너에 등록 return rg; } }
전체 흐름
  1. 애플리케이션 시작 시 필터 등록
      • FilterConfig:
        • addAuthFilter() 메서드 실행:
          • 새로운 FilterRegistrationBean 생성.
          • AuthenticationFilter를 필터로 설정.
          • URL 패턴 (/board/*, /user/*)에 대해 필터가 적용되도록 지정.
          • 필터 실행 순서를 설정 (setOrder(1)).
  1. 클라이언트 요청
    1. 클라이언트가 /board/* 또는 /user/* 경로로 HTTP 요청을 보냄.
  1. 필터 실행
  1. 4단계: 컨트롤러 처리
      • 필터를 통과한 요청은 컨트롤러(BoardController 등)로 전달.
      • 컨트롤러는 추가 비즈니스 로직을 처리.
  1. 응답 반환
    1. 컨트롤러에서 처리한 결과를 클라이언트에 반환.
Share article

goho