REST Security 구현을 위해 클래스를 작성하고 스프링 시큐리티를 이용해 인증과 인가를 구현한다.
사용자 관리를 위해 사용자와 관련된 모델, 서비스, repository, 컨트롤러가 필요하다.
TODO 레이어 구성했던 것 처럼, TODO 말고 USER를 저장하면 됨
- UserEntity
- UserRepository
- UserService
- UserDTO / UserController
스프링 시큐리티 사용하지 않아도 사용자 기능 구현에 필요한 클래스들이라서 독립적인 사용이 가능하다.
그래도 쓸거임 스프링 시큐리티 🥴
package com.example.demo.model;
// import 생략
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")})
public class UserEntity {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String email;
}
자바 클래스를 엔티티로 정의할 때 주의점
1. 클래스에 매개변수가 없는 생성자가 필요
2. Getter / Setter 필요
3. 기본 키 설정 해줘야함
함수 작성 시 메소드 이름 명명 규칙 따라야함 (JPA 사용하니까)
package com.example.demo.persistence;
// import ...
@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
UserEntity findByEmail(String email);
Boolean existsByEmail(String email);
UserEntity findByEmailAndPassword(String email, String password);
}
package com.example.demo.services;
// import ...
@Slf4j
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public UserEntity create(final UserEntity userEntity) {
if (userEntity == null || userEntity.getEmail() == null) {
throw new RuntimeException("Invalid args");
}
final String email = userEntity.getEmail();
if (userRepository.existsByEmail(email)) {
log.warn("{} already exists",email);
throw new RuntimeException("Email already exists");
}
return userRepository.save(userEntity);
}
public UserEntity getByCredentials(final String email, final String password) {
return userRepository.findByEmailAndPassword(email, password);
}
}
create
: UserRepository를 사용하여 사용자를 생성한다.getByCredentials
: 이메일과 패스워드를 이용하여 로그인 시 인증에 사용한다.package com.example.demo.dto;
//import ...
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private String token;
private String email;
private String id;
private String username;
private String password;
}
현재 사용자 가져오기/ 회원가입 기능 구현
package com.example.demo.controller;
// import ...
@Slf4j
@RestController
@RequestMapping("/auth")
public class UserController {
@Autowired
private UserService userService;
}
registerUser()
@PostMapping("/signup")
public ResponseEntity<?> registerUser(@RequestBody UserDTO userDTO) {
try {
// 요청으로 받은 사용자 정보 (userDTO) 사용하여 엔티티 생성
UserEntity user = UserEntity.builder()
.username(userDTO.getUsername())
.email(userDTO.getEmail())
.password(userDTO.getPassword())
.build();
// 서비스 사용하여 생성한 사용자를 레포지터리에 저장한다
UserEntity registeredUser = userService.create(user);
// 응답으로 보낼 DTO 생성
UserDTO responseUserDTO = UserDTO.builder()
.id(registeredUser.getId())
.email(registeredUser.getEmail())
.username(registeredUser.getUsername())
.build();
return ResponseEntity.ok().body(responseUserDTO);
} catch (Exception e) {
// 사용자 정보는 항상 하나 -> 리스트x
ResponseDTO responseDTO = ResponseDTO.builder().error(e.getMessage()).build();
return ResponseEntity.badRequest().body(responseDTO);
}
}
authenticate()
@PostMapping("/signin")
public ResponseEntity<?> authenticate(@RequestBody UserDTO userDTO) {
UserEntity user = userService.getByCredentials(
userDTO.getEmail(), userDTO.getPassword());
if (user != null) {
final UserDTO responseUserDTO = UserDTO.builder()
.email(user.getEmail())
.id(userDTO.getId())
.build();
return ResponseEntity.ok().body(responseUserDTO);
} else {
ResponseDTO responseDTO = ResponseDTO.builder()
.error("Fail to Login").build();
return ResponseEntity
.badRequest()
.body(responseDTO);
}
}
그냥 로그인...
signup 테스트 화면 ↓
signin 테스트 화면 ↓
현재 시점에서의 문제점
으아아~