스프링/4. 스프링 MVC-2

56. 인증 체크 필터 구현

sdafdq 2023. 9. 9. 05:02

이제 로그인 하지 않았다면 상품관리 등의 페이지에 접근하지 못하도록 하는 필터 로직을 개발해 보자.

 

 

@Slf4j
public class LoginCheckFilter implements Filter {

    private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();

        HttpServletResponse httpResponse = (HttpServletResponse) response;

        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);
        } catch (Exception e){
            throw e;
        } finally {

        }
    }

    private boolean isLoginCheckPath(String requestURI){
        return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
    }


}

먼저 체크할 url 배열을 만들어 준다.

저 url을 제외한 곳은 로그인이 필요한 url로 간주할 것이다.

 

doFilter를 보자.

먼저 HttpServelt 기능들을 이용하기 위해 형변환을 한다.

requestURI는 로그인 안한 상태로 로그인이 필요한 url에 접속한 경우, 로그인 성공하면 바로 거기로 리다이렉트로 보내버리기 위해 저장해놓는거다.

 

isLoginCheckPath()에서 

PatternMatchUtils는 매칭과 관련된 자바에서 제공해주는 라이브러리 같다.

PatternMatchUtils.simpleMatch(리스트, 매치시킬것)

 

사실 상 그냥 contains다.

 

매칭되는게 있다면 true, 없다면 false이다.

매칭이 된다면 로그인없이 접근 가능한 url이다.

 

그래서 반전연산자로 만약 로그인이 필요없는 url에 접속하였다면 로그인에 대한 검사(세션검사)를 안하고 바로 chain.doFilter()해서 넘겨버린다.

 

로그인에 대한 검증은 먼저 세션을 가져오는데, 없으면 생성하지 않게 인자를 false로 준다.

그 다음 session이 null 이거나, 혹은 로그인 상태에 관한 세션이 있는지 가져와본다.

둘 중 하나라도 없으면 그냥 HttpServletResponse 기능인

response.sendRedirect(url) 해서 보내버린다.

근데 쿼리 파라미터로 클라이언트가 원래 접속하려고 했던 uri를 넘긴다.

 

 

이제 컨트롤러에서 저 넘긴 url를 처리하도록 수정하여야 하는데,

 

그 전에,

 

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean logFilter(){
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LogFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean loginCheckFilter(){
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LoginCheckFilter());
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.addUrlPatterns("/*");

        return filterRegistrationBean;
    }
}

등록 해주자.

 

그냥 똑같다.

FilterRegistrationBean으로 등록해주고,

filter를 set해주고,

순서 정해주고,

패턴은 그냥 전체로 하고, 관리는 LoginCheckFilter에서 한다(whitelist).

두번 검사하는거니 리소스가 더 많이 들지 않을까요? 라는 것에 대해선 "모래사장의 모래"라는 답변을 하였다.

 

return filterRegistrationBean 해 주면 loginCheckFilter라는 이름으로 빈 등록이 완료된다.

 

아마 원리는, FilterRegistrationBean의 리스트로 가지고 있는 무언가가 자동주입으로 FilterRegistrationBean을 받는 듯 하다. Filter라는 객체가 Bean에 등록되어 있고, 그 객체는 List<FilterRegistrationBean> filters를 가지고 있으며, 이걸 자동 주입으로 받는 듯 하다(추측)

 

 

@Slf4j
@Controller
@RequiredArgsConstructor
public class LoginContoller {
    private final LoginService loginService;
    private final SessionManager sessionManager;

    @GetMapping("/login")
    public String loginForm(@ModelAttribute LoginForm form){
        return "login/loginForm";
    }

    @PostMapping("/login")
    public String loginV4(@Validated @ModelAttribute LoginForm loginForm, BindingResult bindingResult, HttpServletRequest request,
                          @RequestParam(defaultValue = "/") String redirectURL){
        if(bindingResult.hasErrors()){
            return "login/loginForm";
        }

        Member loginMember = loginService.login(loginForm.getLoginId(), loginForm.getPassword());

        if(loginMember == null){
            bindingResult.reject("loginFail", "아이디 혹은 비밀번호가 맞지 않습니다.");
            return "/login/loginForm";
        }

        HttpSession session = request.getSession(); //세션 있으면 반환, 없으면 신규 생성
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
        return "redirect:" + redirectURL;
    }
}

 

 

로그인만 처리해 주면 된다.

기존 것에 새로

@RequestParam(defaultValue = "/") String redirectURL

이 부분 추가하고 살짝 바꿔줬다.

아까 말 했던 클라이언트가 원래 가고자 했던 url를 쿼리 파라미터로 받았다.

 

return 부분만 redirect 하면서 추가로 붙여준다.

 

'스프링 > 4. 스프링 MVC-2' 카테고리의 다른 글

58. 스프링 인터셉터 요청로그  (0) 2023.09.09
57. 스프링 인터셉터  (0) 2023.09.09
55. 필터 구현  (0) 2023.09.09
54. 필터, 인터셉터  (0) 2023.09.08
53. 세션 정보 확인과 타임아웃 설정  (0) 2023.09.07