지난 글에서 JWT 로그인을 위한 로직 을 작성하였다.
이번시간엔 회원가입과 회원가입한 정보를 바탕으로 로그인 성공시 JWT 토큰을 발급해보자.
- 회원가입 요청을 보낸다.( 기본 권한은 GUEST )
- 회원 가입한 아이디,패스워드 로 로그인한다.
- 로그인 성공시 발급된 JWT 토큰을 반환해준다.
- 로그인시 발급되는 토큰 정보를 헤더에 넣어 등업 신청을 한다.
- GUEST -> USER 로 권한을 변경해준다.
우리는 Email Name Password 세가지 필드를 가진 SignUpDto 로 회원가입 요청을 할것이다.
@PostMapping("/member")
public Long save(@RequestBody @Valid SignUpDto signUpDto) {
// 이미 가입한 email 일 경우 exception!
if (memberJpaRepository.findByEmail(signUpDto.getEmail()).isPresent()) {
throw new IllegalArgumentException("이미 존재하는 회원 입니다.");
}
return memberService.saveMember(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;
}
}
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();
}
@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 를 받아도 받지 않아도 되게 하였다.
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;
}
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;
}
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();
}
}
ID,Password 로그인으로 토큰값을 받은후 body 를 지우고 토큰값을 Authorization 에 넣어준후
로그인을 시도하였고 응답이잘 왔다.
body 에 email,password 값을 넣어 요청을 전송하였고
응답이 잘 왔다.
@PutMapping("/member")
public LoginMemberResponse updateMember(HttpServletRequest httpServletRequest) {
return memberService.updateMember(httpServletRequest);
}
@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 을 사용하는등 다소 아쉬운 부분이 존재한다.
또 내가 처리한 로직이 맞는것인지 조금더 나은 방법은 없는지 더 알아볼 예정이다.