스프링과 JPA 기반 웹 애플리케이션 개발 #28-1 @CurrentUser 애노테이션 수정 + 커스텀 필터(IfLoginRedirectFilter) 도입

Jake Seo·2021년 6월 2일
0

스프링과 JPA 기반 웹 애플리케이션 개발 #28-1 @CurrentUser 애노테이션 수정

해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.

강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.

제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.


@CurrentUser 애노테이션 수정

이 애노테이션을 수정하는 이유는

  1. 기존 강의에서는 @UserAccount라는 이름을 썼다.
  2. 이후에 이름이 직관적이지 않은 것 같아서, @CurrentUser라는 애노테이션명으로 바꿨다.
  3. 이후에 @LoginAccount라는 이름이 더 와닿아서 또 바꾸었다.

@LoginAccount Account loginAccount와 같은 형식으로 컨트롤러에서 사용하여 로그인된 유저를 불러올 수 있다.

UserAccount 클래스의 구현 내용 변경

@Getter
public class UserAccount extends User {

    private final Long id;
    private final AccountRepository accountRepository;
    private Account account;

    public UserAccount(Account account, AccountRepository accountRepository) {
        super(account.getNickname(), account.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER")));
        this.id = account.getId();
        this.accountRepository = accountRepository;
    }

    public Account getAccount() {
        this.account = accountRepository.findById(id).get();
        return this.account;
    }
}

이제는 생성할 때 AccountRepository 의존성을 내려받아서, .getAccount() 시에 매번 리포지토리에서 가져온 영속 상태의 Account 객체를 가져온다. detached된 것을 반환하지 않는다.

    public void login(Account account) {
        UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(
                        new UserAccount(account, accountRepository), // principal을 변경했음
                        account.getPassword(),
                        List.of(new SimpleGrantedAuthority("ROLE_USER"))
                );

        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(token);
        ...

대신 위와 같이 생성자 메소드에서 accountRepository를 같이 내려줘야 한다.

IfLoginRedirectFilter 클래스 생성

public class IfLoginRedirectFilter extends GenericFilterBean {

    private List<String> urlsToRedirect;

    public IfLoginRedirectFilter(List<String> urlsToRedirect) {
        this.urlsToRedirect = urlsToRedirect;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;

        if(isAuthenticated() && urlsToRedirect.contains(servletRequest.getRequestURI())) {
            String encodedRedirectURL = ((HttpServletResponse) response).encodeRedirectURL(servletRequest.getContextPath() + "/");

            servletResponse.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
            servletResponse.setHeader("Location", encodedRedirectURL);
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }

    private boolean isAuthenticated() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication == null
                || AnonymousAuthenticationToken.class.isAssignableFrom(authentication.getClass())) {
            return false;
        }

        return authentication.isAuthenticated();
    }
}

이 필터는 이미 인증된 사용자가 또 로그인 등과 같은 인증 페이지에 다시 접근하면 메인 URL인 /로 다시 보내주는 역할을 한다. 서블릿에 필터를 거는 간단한 방식이다.

ArrayList<String> 타입인 urlsToRedirect에 추가하고 싶은 URL을 추가하면 된다.

IfLoginRedirectFilter 클래스 적용

        ArrayList<String> ifAuthenticatedUrlsToRedirect = new ArrayList<>();
        ifAuthenticatedUrlsToRedirect.add("/login");

        http.addFilterAfter(new IfLoginRedirectFilter(ifAuthenticatedUrlsToRedirect), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests().mvcMatchers(ifAuthenticatedUrlsToRedirect.toArray(new String[0])).permitAll();

일단 임의로 /login 경로에만 지정해봤는데 문제없이 잘 동작한다.

이 과정에서 mvcMatcherantMatcher에 대한 정보를 찾아봤는데, mvcMatcher/login을 경로로 설정하면, /login.html등 경로가 정확히 일치하지 않아도 mvc 패턴 내부에서 경로가 일치하면 일치하는 것으로 판단하고, antMatcher는 정확히 /login이 일치해야 경롹 일치하는 것으로 판단한다.

ArrayList<String>String[] 형태로 변경하고 싶다면 .toArray(new String[0]) 과 같은 메소드를 사용하면 된다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글