이 구현은 시큐리티 의존성을 사용하지 않고 필터를 사용하여 Http 리퀘스트를 통해 구현하고자 한다.
먼저 Filter는 기본적인 Filter와 OncePerRequestFilter가 있다.
이러한 필터는 HTTP 요청 응답 전후에 로직을 제어할 수 있다.
+ 이렇게 요청, 응답 제어를 하는 역할로는 인터셉터, AOP가 있다. 각각은 처리 시점이 다르다는 차이점이 있다.
Filter와 OncePerRequestFilter의 차이점으로는 단 한 번의 요청을 받는 지의 차이점이 있다.
로그인을 구현할 때는 단 한 번의 요청에 대하여 필터를 적용하기 위해 OncePerRequestFilter를 사용하고자 한다.
1. OncePerRequestFilter를 상속받는 클래스 생성 후 메소드 오버라이드를 통해 필터 로직을 작성한다.
public class CustomAuthFilter extends OncePerRequestFilter
2. 로직은 다음과 같이 작성하였다.
로그인 컨트롤러를 만든 후 로그인 성공 시 유저라는 세션 정보를 만든다.
그 세션 정보에 따라서 로그인 여부를 파악하고 API에 접근할 수 있도록 한다.
현재 로직은 /api 경로로 접근하는 경우 로그인없이 접근할 수 있다.
지금은 기능들이 동작하는 지 테스트 하고자 하기 때문에 /login,/register와 같이 로그인 없이 접근가능한 부분에 대해서 작성하지 않고
/api로 두었지만 프로젝트가 완성이 되면 /api/login, /api/register는 로그인없이 접근할 수 있도록 수정해야 한다.
그 외 게시판 조회와 같은 내용도 필요 시 추가하면 좋을 것 같다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String requestMethod = request.getMethod();
log.info("Method : {}, URI : {}", requestMethod, requestURI);
if (requestURI.startsWith("/api")) {
filterChain.doFilter(request, response);
return;
}
HttpSession session = request.getSession(false);
if(session == null || session.getAttribute("user") == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"message\": \"로그인이 필요합니다.\"}");
return;
}
filterChain.doFilter(request,response);
}
3. 로그인 시 세션 정보 저장
현재는 메모리에 세션을 저장하고 있다. 하지만 서버 재시작 시 세션이 없어지기 때문에 세션 저장소를 따로 만들어 사용하는 것이 좋다고 생각한다.
@PostMapping("/login")
public ResponseEntity<Void> loginUser(
HttpServletRequest request,
@Valid @RequestBody LoginReqDTO loginReqDTO
){
UserDTO user = userService.login(loginReqDTO.getEmail(), loginReqDTO.getPassword());
HttpSession session = request.getSession();
session.setAttribute("user", user);
return ResponseEntity.status(HttpStatus.OK).build();
}
추가적으로 이와 같이 시큐리티필터체인을 사용하지 않고, 로그인 기능을 구현한다면 글 쓰기, 수정, 삭제와 같이 권한이 필요한 기능들에는
DTO를 통해 사용자 정보를 받는 것보다 세션을 통해 받는 것이 좋다. 왜냐하면 프론트에서 유저 정보를 바꿔서 DTO에 담아 보내게 된다면
내 계정이 아닌 다른 유저의 계정으로 CRUD가 가능해지게 된다.
아래의 코드와 같이 UserDTO를 만들고 세션 정보를 저장하는 객체를 생성 후 Getter를 통해 필요한 유저 번호, 이메일, 이름 등을 사용하도록 하는 것이 좋다.
HttpSession session = request.getSession(false);
UserDTO loginUser = (UserDTO) session.getAttribute("user");
+ 시큐리티는 인증된 사용자의 정보를 자동으로 가져올 수 있게 해주는 @AuthenticationPrincipal 어노테이션이 있기 때문에
다음과 같이 작성하지 않는다.
+ 시큐리티를 사용하지 않기 때문에 비클립트 암호화 기능은 아래 의존성을 추가하여 가져와야 한다.
implementation 'at.favre.lib:bcrypt:0.10.2'
그리고 다음과 같이 컴포넌트 등록 후 시큐리티의 패스워드인코드와 동일하게 주입하여 사용하면 된다.
@Component
public class PasswordEncoder {
public String encode(String rawPassword) {
return BCrypt.withDefaults().hashToString(BCrypt.MIN_COST, rawPassword.toCharArray());
}
public boolean matches(String rawPassword, String encodedPassword) {
BCrypt.Result result = BCrypt.verifyer().verify(rawPassword.toCharArray(), encodedPassword);
return result.verified;
}
}
'스프링부트' 카테고리의 다른 글
| JWT 토큰을 이용한 로그인 구현 - 1 (0) | 2025.04.16 |
|---|---|
| 일정 관리 페이지 만들기 2 (JPA + 댓글 기능) (1) | 2025.04.02 |
| 일정 관리 페이지 만들기 (0) | 2025.03.24 |
| 스프링부트 + JPA에 대하여 (환경 세팅) (0) | 2025.03.17 |
| UrlResource 객체 사용하여 이미지 객체 반환하기 (0) | 2025.03.05 |