빙응의 공부 블로그
[Spring REST API]로그인 구현하기 - 기본 본문
📝실습 전 세팅
JWT + Scurity를 사용하여 REST API의 로그인을 구현해보려 한다.
또한 예외처리까지 실습할 예정이다.
구현 목록
기능 | URL |
회원가입 | /users/join |
로그인 | /users/login |
의존성
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//Security
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
//JWT
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
//Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
//MySQL_Connector
runtimeOnly 'com.mysql:mysql-connector-j'
}
📝 Entity 설계하기
단순 회원가입이기 때문에 단순하게 설계했다.
또한 역할을 추가하여 나중에 있을 개인프로젝트용 템플릿으로 만들 것이다!
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userName;
private String password;
@ElementCollection(fetch = FetchType.LAZY)
@Enumerated(EnumType.STRING)
private List<Role> roles = new ArrayList<>();
@Builder
public User(String userName, String password, List<Role> roles) {
this.userName = userName;
this.password = password;
this.roles = Collections.singletonList(Role.MEMBER);
}
public void addRole(Role role) {
this.roles.add(role);
}
}
- 회원은 권한들을 가지며 아이디와 비밀번호를 가진다.
- Mysql이기에 IDENTITY를 사용
📝기초적인 로그인 서비스
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final UserRepository userRepository;
@Transactional
public void join(String userName, String password){
// USERNAME 중복체크
userRepository.findByUserName(userName)
.ifPresent(user -> {
throw new AppException(UserErrorCode.USERNAME_DUPLICATED);
});
// 저장
userRepository.save(User.builder()
.userName(userName)
.password(bCryptPasswordEncoder.encode(password))
.build());
}
public String login(String userName, String password){
//userName 없음
User user = userRepository.findByUserName(userName)
.orElseThrow(() ->
new AppException(UserErrorCode.FAILD_LOGIN));
//password 틀림
if(!bCryptPasswordEncoder.matches(password, user.getPassword())){
throw new AppException(UserErrorCode.FAILD_LOGIN);
}
return "token";
}
}
간단한 로그인, 회원가입 로직이다. 또한 사용자 오류 처리를 사용하여 더 가독성 있게 구현하였다.
커스텀 Exception 정의
@AllArgsConstructor
@Getter
public class AppException extends RuntimeException {
private ErrorCode errorCode;
}
오류 처리 객체
public interface ErrorCode {
HttpStatus getHttpStatus();
String getMessage();
}
@AllArgsConstructor
@Getter
public enum UserErrorCode implements ErrorCode {
USERNAME_DUPLICATED(HttpStatus.CONFLICT, "유저 중복"),
FAILD_LOGIN(HttpStatus.NOT_FOUND, "유저가 없거나 비밀번호가 틀립니다.");
private HttpStatus httpStatus;
private String message;
}
RestControllerAdivce
RestController에서 들어오는 오류를 여기서 처리할 수 있게 해준다.
@RestControllerAdvice
public class ExceptionManager {
@ExceptionHandler(AppException.class)
public ResponseEntity<ResponseDto<?>> appExceptionHandler(AppException e) {
ErrorCode errorCode = e.getErrorCode();
ResponseDto<?> response = ResponseDto.fail(errorCode.getHttpStatus().value(), errorCode.getMessage() + " " + e.getMessage());
return ResponseEntity.status(errorCode.getHttpStatus()).body(response);
}
}
커스텀 responseDto
커스텀 responseDto는 성공, 실패때 body로 보낼 데이터를 정의한 것이다.
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ResponseDto<T> {
private int code;
private String message;
private T data;
private LocalDateTime timestamp;
public static <T> ResponseDto<T> of(T data){
return new ResponseDto<>(HttpStatus.OK.value(), null, data, LocalDateTime.now());
}
public static <T> ResponseDto<T> fail(Integer status, String message) {
return new ResponseDto<>(status, message, null, LocalDateTime.now());
}
}
'Spring > 개인공부_실습' 카테고리의 다른 글
[Spring REST API]로그인 구현하기 - 2개의 토큰 (0) | 2024.06.04 |
---|---|
[Spring REST API]로그인 구현하기 - JWT + Security (1) | 2024.06.03 |
[Spring]WebClient (0) | 2024.05.24 |
[React + Spring]리액트와 스프링으로 API 받아서 처리하기 (0) | 2024.05.20 |
[Spring]Spring - RestTemplate (0) | 2024.05.19 |