JWT Login 2

김창모·2023년 6월 1일
0

SpringBoot

목록 보기
15/19
post-thumbnail

INTRO

지난 글에서 JWT 로그인을 위한 로직 을 작성하였다.
이번시간엔 회원가입과 회원가입한 정보를 바탕으로 로그인 성공시 JWT 토큰을 발급해보자.

구현순서

  1. 회원가입 요청을 보낸다.( 기본 권한은 GUEST )
  2. 회원 가입한 아이디,패스워드 로 로그인한다.
  3. 로그인 성공시 발급된 JWT 토큰을 반환해준다.
  4. 로그인시 발급되는 토큰 정보를 헤더에 넣어 등업 신청을 한다.
  5. GUEST -> USER 로 권한을 변경해준다.

회원가입 요청

우리는 Email Name Password 세가지 필드를 가진 SignUpDto 로 회원가입 요청을 할것이다.

controller

    @PostMapping("/member")
    public Long save(@RequestBody @Valid SignUpDto signUpDto) {
    
    // 이미 가입한 email 일 경우 exception!
    	if (memberJpaRepository.findByEmail(signUpDto.getEmail()).isPresent()) {
            throw new IllegalArgumentException("이미 존재하는 회원 입니다.");
        }
    
        return memberService.saveMember(signUpDto);
    }

SignUpDto

package com.hello.hello.domain.dto.request;

import javax.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class SignUpDto {

// @NotBlank 는 Controller 에서 @RequestBody 로 json 객체를 받을때
// 공백을 검증할수 있게 해준다.
    @NotBlank
    private String email;
    @NotBlank
    private String name;
    @NotBlank
    private String password;

    @Builder
    public SignUpDto(String email, String name, String password) {
        this.email = email;
        this.name = name;
        this.password = password;
    }
}

MemberService

    public Long saveMember(SignUpDto signUpDto) {
        Member member = Member.builder().email(signUpDto.getEmail()).name(signUpDto.getName())
                .password(passwordEncoder.encode(signUpDto.getPassword())).build();

        Set<Authority> roles = new HashSet<>();

        roles.add(Authority.ROLE_GUEST);

        member.addRole(roles);

        memberJpaRepository.save(member);

        return member.getId();
    }

로그인 시도

Controller

@PostMapping("/login")
    public LoginMemberResponse login(@RequestBody(required = false) @Valid LoginMemberRequest loginMemberRequest,HttpServletRequest httpServletRequest) {
        return memberService.login(loginMemberRequest,httpServletRequest);
    }

로그인을 시도할때는 두가지 방법이 있다.
1. ID,Password
2. JWT 토큰
어떻게 처리할까 고민하다가 @RequestBody 의 required 옵션을 false 로 변경하여
LoginMemberRequest 를 받아도 받지 않아도 되게 하였다.

LoginMemberRequest

package com.hello.hello.domain.dto.request;

import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LoginMemberRequest {
    @NotBlank
    private String email;
    @NotBlank
    private String password;
}

LoginMemberResponse

package com.hello.hello.domain.dto.response;

import com.hello.hello.domain.Authority;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
public class LoginMemberResponse {
    private String email;
    private String name;
    private Set<Authority> roles;
    private String token;
}

MemberService

public LoginMemberResponse login(LoginMemberRequest loginMemberRequest, HttpServletRequest httpServletRequest) {


            String token = httpServletRequest.getHeader("Authorization");
// 1. JWT토큰을 통한 로그인 일때 
// 토큰이 Null 이 아니고 "Bearer" 로 시작한다면
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring("Bearer ".length()).trim();
            String memberEmail = jwtProvider.getMember(token);
            Member member = memberJpaRepository.findByEmail(memberEmail)
                    .orElseThrow(() -> new RuntimeException("토큰 정보가 올바르지 않습니다."));

            return LoginMemberResponse.builder().email(member.getEmail()).name(member.getName()).token(token)
                    .roles(member.getRoles()).build();
                    
// 2. JWT토큰을 이용한 로그인이 아닌
// ID,Password 를 이용한 로그인 일때.
        } else {
            Member member = memberJpaRepository.findByEmail(loginMemberRequest.getEmail())
                    .orElseThrow(() -> new UsernameNotFoundException("로그인 정보가 일치하지 않습니다."));

            if (!passwordEncoder.matches(loginMemberRequest.getPassword(), member.getPassword())) {
                throw new IllegalArgumentException("로그인 정보가 일치하지 않습니다.");
            }

            String createToken = jwtProvider.createToken(member.getEmail(), member.getRoles());

            return LoginMemberResponse.builder().email(member.getEmail()).name(member.getName()).token(createToken)
                    .roles(member.getRoles()).build();
        }

    }

case1 JWT

ID,Password 로그인으로 토큰값을 받은후 body 를 지우고 토큰값을 Authorization 에 넣어준후
로그인을 시도하였고 응답이잘 왔다.

case2 ID,Password

body 에 email,password 값을 넣어 요청을 전송하였고
응답이 잘 왔다.

등업신청

Controller

    @PutMapping("/member")
    public LoginMemberResponse updateMember(HttpServletRequest httpServletRequest) {

        return memberService.updateMember(httpServletRequest);
    }

Service

    @Transactional
    public LoginMemberResponse updateMember(HttpServletRequest httpServletRequest) {
        String token = httpServletRequest.getHeader("Authorization");
String getToken = token.replace("Bearer ", "");
        

        String memberEmail = jwtProvider.getMember(getToken);

Member member = memberJpaRepository.findByEmail(memberEmail)
                .orElseThrow(() -> new UsernameNotFoundException(memberEmail + " 유저를 찾을수 없습니다."));

        Set<Authority> roles = new HashSet<>();

        roles.add(Authority.ROLE_GUEST);
        roles.add(Authority.ROLE_USER);

        member.addRole(roles);

        String newToken = jwtProvider.createToken(memberEmail, member.getRoles());

        return LoginMemberResponse.builder().email(member.getEmail()).name(member.getName()).roles(member.getRoles())
                .token(newToken).build();
    }

실행

roles 에 ROLE_USER 가 추가된것을 볼수있다.

추가적으로 Exception 을 더 구체적으로 명시해야 하지만 이번 글은 JWT 로그인 관련 이기 때문에
RuntimeException 을 사용하는등 다소 아쉬운 부분이 존재한다.
또 내가 처리한 로직이 맞는것인지 조금더 나은 방법은 없는지 더 알아볼 예정이다.

0개의 댓글