22.07.08 TIL + userArgumentResolver

Wintering·2022년 7월 8일
0

2022TIL

목록 보기
65/87

7월8일(금)

내일배움캠프 D+81 📚


Today I Learnd📌

현재 로그인 한 유저에 대한 정보를 가져오는 코드는 아래와 같다.

   public String main(@CookieValue(required = false, name = "access_token") 
   					String token, Model model) {
        if (token != null) {
            String userEmail = tokenProvider.getUserEmailByToken(token);
            User user = userService.getUserByEmail(userEmail);
            model.addAttribute("user", user);

token에서 userEmail정보를 빼온 이후, userEmail 정보를 이용하여 해당하는 유저의 정보를 넘겨받는 형식인데, 우리 프로젝트의 특성상 유저의 정보가 어디서든 필요하고 + 프로젝트의 볼륨이 작지 않다보니 이 대략 5줄의 코드가 모든 컨트롤러에 존재하는 게 상당히 보기 좋지 않은 요인이라고 생각했고 모듈화 시킬 수 있는 방법을 고민했다.

튜터님의 도움을 받아 userArgumentResolver라는 개념에 대해 알게되었다. 컨트롤러 메서드에서 특정 조건에 맞는 파라미터가 있을 때 원하는 값을 바인딩해주는 인터페이스인 HandlerMethodArgumentResolver를 상속받아 만드는, 로그인 한 유저를 판별하는 Resolver이다.

HandlerMethodArgumentResolver

  • 실제적용
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return User.class.isAssignableFrom(methodParameter.getParameterType());
    }
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!(authentication instanceof AnonymousAuthenticationToken)) {
            return authentication.getPrincipal();
        }
        return null;
    }
}

return User.class.isAssignableFrom(methodParameter.getParameterType()); 에서 User는 SpringSecurity에 principal로 등록한 객체를 의미한다.
원래 코드에서는 Java에서 제공하는 User라이브러리를 사용해 유저아이디와 비밀번호, 권한만 넘겨서 인증받는 객체를 principal에 등록했었는데, 로그인 한 유저의 정보 전체를 받아오고 싶었기 때문에 코드를 수정해주었다.

if (authToken.validate()) {

            Claims claims = authToken.getTokenClaims();
            Collection<? extends GrantedAuthority> authorities =
                    Arrays.stream(new String[]{claims.get(AUTHORITIES_KEY).toString()})
                            .map(SimpleGrantedAuthority::new)
                            .collect(Collectors.toList());

            log.debug("claims subject := [{}]", claims.getSubject());
            //수정전
            User principal = new User(claims.getSubject(), "", authorities);
            return new UsernamePasswordAuthenticationToken(principal, authToken, authorities);

			//수정후
            User principal = userRepository.findByUserEmail(claims.getSubject()).orElseThrow();
            return new UsernamePasswordAuthenticationToken(principal, authToken, authorities);

(+)넘겨준 객체는 어디까지는 원하는 정보를 어디까지 담을지를 스스로 지정하는 것도 충분히 가능.
모든 코드에서 user객체 자체를 필요로 하는 경우가 많아서 user객체를 전부 넘기도록 세팅했지만, 초반에 로그인 한 유저의 정보를 받아, 로그인하면 닉네임을 표시할 수 있는 정도만 고려하고 코드를 짰을 땐, userPrincipalForResolver라는 원하는 User정보만 받을 클래스를 따로 생성하기도 했었다.

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class UserPrincipalForResolver {
    private String userEmail;
    private String userNickname;
    private RoleType authorities;
}
//자체 생성한 클래스에 원하는 정보를 토큰에서 빼와 담는 식으로 구성
 if (authToken.validate()) {
            Claims claims = authToken.getTokenClaims();
            Collection<? extends GrantedAuthority> authorities =
                    Arrays.stream(new String[]{claims.get(AUTHORITIES_KEY).toString()})
                            .map(SimpleGrantedAuthority::new)
                            .collect(Collectors.toList());
            String userNickname = claims.get(NICKNAME_KEY, String.class);
            RoleType roleType = RoleType.of(claims.get(AUTHORITIES_KEY).toString());

            User principal = userRepository.findByUserEmail(claims.getSubject()).orElseThrow();
            log.debug("claims subject := [{}]", claims.getSubject());

            return new UsernamePasswordAuthenticationToken(principal, authToken, authorities);

(+)resolver를 사용하기 위해선 webConfig로 resolver를 꼭 등록해줘야한다.
처음에 등록하지 않고 무턱대고 사용했어서 계속 null값만 리턴받는 오류를 마주했었다. webConfig 클래스를 만들어 꼭 등록해주도록하자


@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
    private final UserArgumentResolver userArgumentResolver;
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userArgumentResolver);
    }
}

1개의 댓글

comment-user-thumbnail
2022년 7월 11일

너무너무 잘하고있어 이 복잡한 코드랑 열심히 싸우는거 나한테 하라하면 못해 ㅋㅋㅋ 너니까 하는거야 우리팀 정신적 지주 힘내라우

답글 달기