ArgumentResolver


๐Ÿ”Ž ArgumentResolver

๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž๋ฅผ ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—†์„๊นŒ?

@Login์ด๋ผ๋Š” argument๋ฅผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ArgumentResolver๋ฅผ ๋งŒ๋“ค๊ณ  ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ถฉ๋ถ„ํžˆ ๊ฐ€๋Šฅํ•˜๋‹ค.


@Login

@Login ์• ๋…ธํ…Œ์ด์…˜์€ ์ž๋™์œผ๋กœ ์„ธ์…˜์— ์žˆ๋Š” ๋กœ๊ทธ์ธ ํšŒ์›์„ ์ฐพ์•„์ฃผ๊ณ ,

๋งŒ์•ฝ ์„ธ์…˜์— ์—†๋‹ค๋ฉด null ์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•œ๋‹ค.

1. @Login ์• ๋…ธํ…Œ์ด์…˜ ์ƒ์„ฑ

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {}
  • @Target(ElementType.PARAMETER) : ํŒŒ๋ผ๋ฏธํ„ฐ์—๋งŒ ์‚ฌ์šฉ
  • @Retention(RetentionPolicy.RUNTIME) : ๋Ÿฐํƒ€์ž„๊นŒ์ง€ ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๊ฐ€ ์œ ์ง€


2. @Login์„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ArgumentResolver ์ƒ์„ฑ

// import
import org.springframework.web.method.support.HandlerMethodArgumentResolver;

// main class
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {

        // @Login ์• ๋…ธํ…Œ์ด์…˜์ด ํŒŒ๋ผ๋ฏธํ„ฐ์— ์žˆ๋Š”์ง€?
        boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);

        /** 
        * parameter.getParameterType() : @Login ์• ๋…ธํ…Œ์ด์…˜ ๋’ค์— ๋ถ™์€ ํด๋ž˜์Šค
        * isAssignableFrom : ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋Š”์ง€.
        */
        boolean hasModelType = Member.class.isAssignableFrom(parameter.getParameterType()); 

        return hasLoginAnnotation && hasModelType;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();

        HttpSession session = request.getSession();
        if (session == null) {
            return null;
        }
        return session.getAttribute("LOGIN_MEMBER");
    }
}

supportsParameter()

@Login ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ์œผ๋ฉด์„œ, Model ํƒ€์ž…์ด๋ฉด ArgumentResolver๊ฐ€ ์‹คํ–‰๋œ๋‹ค.


resolveArgument()

์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ง์ „์— ํ˜ธ์ถœ๋˜์–ด์„œ ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

์œ„์˜ ์ฝ”๋“œ์—์„œ๋Š” ๋กœ๊ทธ์ธ ํšŒ์›์ •๋ณด์ธ member ๊ฐ์ฒด๋ฅผ ์ฐพ์•„์„œ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.

session=Member(id=1, loginId=test, name=ํ…Œ์Šคํ„ฐ, password=1234)

์ดํ›„ ์Šคํ”„๋งMVC๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์—ฌ๊ธฐ์—์„œ ๋ฐ˜ํ™˜๋œ member ๊ฐ์ฒด๋ฅผ

ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ „๋‹ฌํ•ด์ค€๋‹ค.

model={member=Member(id=1, loginId=test, name=ํ…Œ์Šคํ„ฐ, password=1234)}


3. ์ž‘์„ฑํ•œ ArgumentResolver ๋“ฑ๋ก

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
          resolvers.add(new LoginMemberArgumentResolver());
      }
}


๊ณ ์ฐฐ

JWT Token ์ธ์ฆ์˜ ๊ฒฝ์šฐ ์ด๋Ÿฐ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•˜๋ฉด ๋˜๋Š”๊ฑด๊ฐ€??

โ’ˆ token ์œ ํšจ ์—ฌ๋ถ€ ํ™•์ธ

โ’‰ token์˜ claims ์ •๋ณด์—์„œ ๊ณ ์œ  ๊ฐ’(ex. Email)์œผ๋กœ DB ์กฐํšŒ

โ’Š ๋ฐ˜ํ™˜ ๋œ raw๊ฐ’์„ ์ปจํ‹€๋กค๋Ÿฌ ๊ฐ์ฒด์— ๋„ฃ์–ด์คŒ.


ArgumentResolver

@RequiredArgsConstructor
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

    private final UserMapper userMapper;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {

        
        boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
        boolean hasModelType = User.class.isAssignableFrom(parameter.getParameterType()); 

        return hasLoginAnnotation && hasModelType;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        
        // Token ์ •๋ณด ํ™•์ธ 
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        String header = request.getHeader("Authorization");
        String token = JwtUtils.getTokenFromHeader(header);


        if (JwtUtils.isValideToken(token)) {

            // Email ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
            String email = JwtUtils.getUserEmailFromToken(token);

            // ๊ฐ€์ ธ์˜จ email์ •๋ณด๋กœ ํ•ด๋‹น User๊ฐ์ฒด ๋ฐ˜ํ™˜
            User checkUser = findUserByEmail(String email);
            return checkUser;

        } else if (!JwtUtils.isValideToken(token)) {

            /** Access Token์ด ์œ ํšจํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ
            * ๋งŒ๋ฃŒ๋œ ํ† ํฐ์—์„œ Email ๊ฐ€์ ธ์˜จ๋‹ค.
            * DB์—์„œ Email๋กœ ํ•ด๋‹น ๊ฐ์ฒด์˜ Refresh Token์„ ๊ฐ€์ ธ์˜จ๋‹ค.
            * ๋‹ค์‹œ ์ƒˆ๋กœ์šด AccessToken์„ ๋งŒ๋“ค์–ด ์ค€๋‹ค.
            */
            String email = JwtUtils.getUserEmailFromToken(token);
            User checkUser = findUserByEmail(String email);
            String reToken = checkUser.getRetoken();

            if (JwtUtils.isValideReToken(reToken)) {

                // Refresh Token์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด Access Token์„ ์ƒ์„ฑ
                User user = new User();
                user.setAuthCode(JwtUtils.getClaimsCodeFromToken(reToken));
                user.setEmail(JwtUtils.getUserEmailFromToken(reToken));
                user.setUserSeq(Long.parseLong(JwtUtils.getClaimsSeqFromToken(reToken)));

                response.setHeader(AuthConstants.AUTH_HEADER, AuthConstants.TOKEN_TYPE + " " + JwtUtils.generateJwtToken(user));
                response.setHeader(AuthConstants.RE_AUTH_HEADER, AuthConstants.TOKEN_TYPE + " " + reToken);
                
                HttpSession session = request.getSession();
                session.setAttribute(user.getEmail()+"_token", reToken);
        
                return checkUser;
                
            } else {
                response.addHeader("Content-Type", "application/json");
                response.getWriter().print("Fail");
                return false;
            }
        }
    }
}