관리 메뉴

JAN's History

JWT 구현하기 : 로그인 요청 시 JWT 응답(Response) 본문

JWT

JWT 구현하기 : 로그인 요청 시 JWT 응답(Response)

JANNNNNN 2024. 7. 10. 15:08

RestApiController

  • 로그인 기능을 구현하기 전 회원가입을 먼저 진행해보자
    @PostMapping("join")
    public String join(@RequestBody User user) {
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        user.setRoles("ROLE_USER");
        userRepository.save(user);
        return "회원가입완료";
    }

SecurityConfig에 BCryptPasswordEncoder Bean으로 등록

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

Postman 으로 회원가입 요청

JwtAuthenticationFilter 수정

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        System.out.println("JWT AuthenticationFilter : 로그인 시도중");
        //1. request에 username, password를 받아서
        try {
//            BufferedReader br = request.getReader();
//
//            String input = null;
//            while((input = br.readLine())!= null){
//                System.out.println(input);
//            }
//            System.out.println(request.getInputStream().toString()); //stream안에 username와 id ..이 담겨있음
            ObjectMapper om = new ObjectMapper(); //ObjectMapper가 **<json>** 데이터를 알아서 파싱해줌
            User user = om.readValue(request.getInputStream(), User.class); //user model에 알아서 파싱해준다 완전 GOOOD!!
            System.out.println(user); //잘 파싱된 것을 확인함.

            //2. user model token화 하기(원래 자동으로 해주는데 우리가 Filter 구현한 거라서 해줘야함)
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());

            //3. PrincipalDetailsService의 loadUserByUsername()함수가 실행됨
            //loadUserByUsername 함수가 실행된 후 정상이면 authentication이 리턴됨
            // 즉 DB에 있는 username과 password가 일치한다는 뜻.
            Authentication authentication = authenticationManager.authenticate(authenticationToken);

            PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
            System.out.println("로그인 완료" + principalDetails.getUser().getUsername()); //꺼냈을 때 값이 있으면 로그인이 정상적으로 되었다는 뜻

            //authentication 객체가 session 영역에 저장을 해야하는데 그 방법이 return을 해주면 됨
            //리턴을 해주는 이유는 권한 관리를 security가 대신 해주기 때문에 편하려고 하는 것!!
            //굳이 jwt 토큰을 사용하면서 세션을 만들 이유가 없.지.만, 단지 권한 처리 때문에 sessin을 넣어 주는 것이다.
            return authentication; // 이 값이 세션에 저장됨

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
  • /login 요청을 하면 ObjectMapper가 JSON 데이터를 user entity로 매핑해 데이터를 알아서 파싱해준다!
  • temptAuthentication실행 후 인증이 정상적으로 되었다면 successfulAuthentication가 실행된다

➡️정상적으로 파싱된 모습

JwtAuthenticationFilter SucessfulAuthentication 에 메서드 추가

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        System.out.println("successfulAuthentication이 실행됨 => 인증이 완료됨");
        super.successfulAuthentication(request, response, chain, authResult);
    }
  • 여기서 JWT를 생성하여 request한 사용자에게 토큰을 반환해주면 된다.
  • 일단 정상 동작하는지 확인해보자

➡️정상적으로 실행됨

JwtAuthenticationFilter->SucessfulAuthentication->JWT 토큰 생성 관련 코드 추가

@Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        System.out.println("successfulAuthentication 실행됨 : 인증 완료!!");
        PrincipalDetails principalDetails = (PrincipalDetails) authResult.getPrincipal(); //authResult이 Authentication의 객체

        //RSA 방식은 아니고 Hash암호 방식
        String jwtToken = JWT.create()
                .withSubject("cos토큰")
                .withExpiresAt(new Date(System.currentTimeMillis()+(60000)*10)) //10분
                .withClaim("id", principalDetails.getUser().getId())
                .withClaim("username", principalDetails.getUser().getUsername())
                .sign(Algorithm.HMAC512("cos")); //server만 알고있는 secret 값

        response.addHeader("Authorization", "Bearer "+jwtToken);
}
  • 여기서 인자값으로 받은 authResult는 JwtAuthenticationFilter에서 생성된 Authentication의 객체다.
  • 이제 다시 포스트맨으로 요청을 보내보자!

  • header에 Authorization의 value로 토큰이 들어가있다!
  • JWT 토큰 생성 완료

막간을 이용한 세션 방식 vs jwt방식

  1. 세션 방식
  • 로그인 시도 후 성공하면 서버쪽에서 세션을 생성하고 서버에 저장을 한 후, 세션 id를 쿠키에 담아 클라이언트에게 함께 보낸다
  • 이후 클라이언트가 다시 요청을 보내면 서버는 쿠키값의 세션 id가 유효한지 서버의 세션 id와 비교하고, 인증이 필요한 페이지로 접근하게 하면 된다.
  1. JWT방식
  • 로그인 시도 후 성공하면 서버쪽에서 jwt를 생성하고, jwt를 클라이언트에게 보낸다
  • 이후 클라이언트가 다시 요청을 할 때 jwt를 함께 보내고, 서버는 jwt가 유효한지 필터를 이용해 판단을 한다.