Framework/ReactJs
todo-java
bright jazz music
2023. 5. 24. 22:22
package com.example.reactspringtodo.controller;
import com.example.reactspringtodo.dto.ResponseDTO;
import com.example.reactspringtodo.dto.UserDTO;
import com.example.reactspringtodo.entity.UserEntity;
import com.example.reactspringtodo.security.TokenProvider;
import com.example.reactspringtodo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/auth")
public class UserController {
private UserService userService;
private TokenProvider tokenProvider;
private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
public UserController (UserService userService, TokenProvider tokenProvider) {
this.userService = userService;
this.tokenProvider = tokenProvider;
}
@PostMapping("/signup")
public ResponseEntity<?> registerUser(@RequestBody UserDTO userDTO) {
System.out.println("################################# signup#################################");
try {//DB는 nullable이기 때문에, 비밀번호가 존재하는지를 컨트롤러에서 반드시 확인해야 한다.
if(userDTO == null || userDTO.getPassword() == null) {
throw new RuntimeException("Invalid Password value");
}
UserEntity user = UserEntity.builder()
.username(userDTO.getUsername())
//.password(userDTO.getPassword()) 암호화 후에는 아래와 같이 변
.password(passwordEncoder.encode(userDTO.getPassword())) //패스워드를 암호화하여 저장
.build();
//UserEntity 값을 DB에 등록하고 등록한 값을 반환함.경
UserEntity registeredUser = userService.create(user);
UserDTO responseUserDTO = UserDTO.builder()
.id(registeredUser.getId())
.username(registeredUser.getUsername())
.build();
return ResponseEntity.ok().body(responseUserDTO);
}catch (Exception e) {
//유저 정보는 항상 하나이므로 리스트로 만들어야 하는 ResponseDTO를 사용하지 않는다.
//그냥 UserDTO를 리턴한다.
ResponseDTO responseDTO = ResponseDTO.builder()
.error(e.getMessage())
.build();
return ResponseEntity
.badRequest()
.body(responseDTO);
}
}
@PostMapping("/signin")
public ResponseEntity<?> authenticate(@RequestBody UserDTO userDTO) {
System.out.println("################################# signin#################################");
System.out.println(userDTO.getUsername());
System.out.println(userDTO.getPassword());
//DB에 등록되 값이 존재하는지 확인하고 존재하는 경우 UserEntity 값을 반환함.
UserEntity user = userService.getByCredentials(
userDTO.getUsername(),
userDTO.getPassword(),
//PasswordEncoder를 함께 넘긴다.
passwordEncoder
);
//값이 존재하는 경우 UserDTO를 만들어 반환
if(user != null) {
//토큰 생성
final String token = tokenProvider.create(user);
System.out.println("###created Token: " + token);
final UserDTO responseUserDTO = UserDTO.builder()
.username(user.getUsername())
.id(user.getId())
.token(token) //tokenProvider를 사용해 생성한 토큰을 UserDTO에 넣어준다.
.build();
return ResponseEntity.ok().body(responseUserDTO);
} else {
// 로그인 정보가 존재하지 않는 경우 responseDTO를 생성하여 ResponseEntity의 body 값에 담아 반환한다.
ResponseDTO<?> responseDTO = ResponseDTO.builder()
.error("Login failed.")
.build();
return ResponseEntity.badRequest().body(responseDTO);
}
}
}
UserEntity.java
package com.example.reactspringtodo.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "username")})
public class UserEntity {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
private String id; //유저에게 고유하게 부여되는 id.
@Column(nullable = false)
private String username;
// 패스워드. OAuth를 사용할 경우에는 null이다. null을 비허용할 경우 OAuth 구현에 문제가 발생한다.
// DB에 null이 입력되는 대신, 컨트롤러에서 password를 반드시 입력하도록 만들어야 한다.
private String password;
//사용자의 롤. 예: 어드민, 일반 사용자
private String role;
// 이후 OAuth에서 사용할 유저 정보 제공자: github
private String authProvider;
}
UserRepository.java
package com.example.reactspringtodo.persistence;
import com.example.reactspringtodo.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
UserEntity findByUsername(String username);
Boolean existsByUsername(String username);
UserEntity findByUsernameAndPassword(String username, String password);
}
UserService.java
package com.example.reactspringtodo.service;
import com.example.reactspringtodo.entity.UserEntity;
import com.example.reactspringtodo.persistence.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserEntity create(final UserEntity userEntity) {
if(userEntity == null || userEntity.getUsername() == null) {
throw new RuntimeException("Invalid argument");
}
final String username = userEntity.getUsername();
if(userRepository.existsByUsername(username)){ //존재한다면
log.warn("Username already exists {}", username);
throw new RuntimeException("Username already exists");
}
return userRepository.save(userEntity);
}
public UserEntity getByCredentials(final String username, final String password) {
return userRepository.findByUsernameAndPassword(username, password);
}
}
UserDto.java
package com.example.reactspringtodo.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private String token;
private String username;
private String password;
private String id;
}
UserController.java
package com.example.reactspringtodo.controller;
import com.example.reactspringtodo.dto.ResponseDTO;
import com.example.reactspringtodo.dto.UserDTO;
import com.example.reactspringtodo.entity.UserEntity;
import com.example.reactspringtodo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/auth")
public class UserController {
private UserService userService;
public UserController (UserService userService) {
this.userService = userService;
}
@PostMapping("/signup")
public ResponseEntity<?> registerUser(@RequestBody UserDTO userDTO) {
System.out.println("################################# signup#################################");
try {//DB는 nullable이기 때문에, 비밀번호가 존재하는지를 컨트롤러에서 반드시 확인해야 한다.
if(userDTO == null || userDTO.getPassword() == null) {
throw new RuntimeException("Invalid Password value");
}
UserEntity user = UserEntity.builder()
.username(userDTO.getUsername())
.password(userDTO.getPassword())
.build();
UserEntity registeredUser = userService.create(user);
UserDTO responseUserDTO = UserDTO.builder()
.id(registeredUser.getId())
.username(registeredUser.getUsername())
.build();
return ResponseEntity.ok().body(responseUserDTO);
}catch (Exception e) {
//유저 정보는 항상 하나이므로 리스트로 만들어야 하는 ResponseDTO를 사용하지 않는다.
//그냥 UserDTO를 리턴한다.
ResponseDTO responseDTO = ResponseDTO.builder()
.error(e.getMessage())
.build();
return ResponseEntity
.badRequest()
.body(responseDTO);
}
}
@PostMapping("/signin")
public ResponseEntity<?> authenticate(@RequestBody UserDTO userDTO) {
System.out.println("################################# signin#################################");
UserEntity user = userService.getByCredentials(
userDTO.getUsername(),
userDTO.getPassword()
);
if(user != null) {
UserDTO responseUserDTO = UserDTO.builder()
.username(user.getUsername())
.id(user.getId())
.build();
return ResponseEntity.ok().body(responseUserDTO);
} else {
ResponseDTO<?> responseDTO = ResponseDTO.builder()
.error("Login failed.")
.build();
return ResponseEntity.badRequest().body(responseDTO);
}
}
}
TokenProvider.java
package com.example.reactspringtodo.security;
import com.example.reactspringtodo.entity.UserEntity;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
@Slf4j
@Service
public class TokenProvider {
private static final String SECRET_KEY="FlRpX30pMqDbiAkmlfArbrmVkDD4RqISskGZmBFax5oGVxzXXWUz TR5JyskiHMIV9M1Oicegkpi46AdvrcX1E6CmTUBc6IFbTPiD";
public String create(UserEntity userEntity) {
//기한을 하루로 설정
Date expiryDate = Date.from(Instant.now().plus(1, ChronoUnit.DAYS));
return Jwts.builder()
//header에 들어갈 내용 및 서명을 기하 위한 Secret Key
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.setSubject(userEntity.getId()) //sub
.setIssuer("demo app") //iss
.setIssuedAt(new Date()) //iat
.setExpiration(expiryDate) //exp
.compact();
}
}