diff --git a/build.gradle b/build.gradle index 634cbca8..192a1bc2 100644 --- a/build.gradle +++ b/build.gradle @@ -32,13 +32,13 @@ dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' -// // DB 관련 라이브러리 -// implementation 'org.springframework.boot:spring-boot-starter-data-jpa' -// runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' -// -// // SpringSecurity 라이브러리 -// implementation 'org.springframework.boot:spring-boot-starter-security' -// testImplementation 'org.springframework.security:spring-security-test' + // DB 관련 라이브러리 + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' + + // SpringSecurity 라이브러리 + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' // JWT implementation 'io.jsonwebtoken:jjwt-api:0.12.3' diff --git a/src/main/java/org/triumers/kmsback/auth/command/Application/controller/AuthController.java b/src/main/java/org/triumers/kmsback/auth/command/Application/controller/AuthController.java new file mode 100644 index 00000000..bc9ad732 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/Application/controller/AuthController.java @@ -0,0 +1,56 @@ +package org.triumers.kmsback.auth.command.Application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +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; +import org.triumers.kmsback.auth.command.Application.dto.AuthDTO; +import org.triumers.kmsback.auth.command.Application.service.AuthService; +import org.triumers.kmsback.auth.command.domain.aggregate.vo.CmdRequestAuthVO; +import org.triumers.kmsback.auth.command.domain.aggregate.vo.CmdResponseMessageVO; + +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final AuthService authService; + + @Autowired + public AuthController(AuthService authService) { + this.authService = authService; + } + + @PostMapping("/signup") + public ResponseEntity signup(@RequestBody CmdRequestAuthVO request) { + AuthDTO authDTO = authDtoMapper(request); + try { + authService.signup(authDTO); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new CmdResponseMessageVO(e.getMessage())); + } + + return ResponseEntity.status(HttpStatus.OK).body( + new CmdResponseMessageVO(authDTO.getName() + " 회원가입 성공")); + } + + private AuthDTO authDtoMapper(CmdRequestAuthVO request) { + AuthDTO auth = new AuthDTO(); + + auth.setEmail(request.getEmail()); + auth.setPassword(request.getPassword()); + auth.setName(request.getName()); + auth.setProfileImg(request.getProfileImg()); + auth.setRole(request.getRole()); + auth.setStartDate(request.getStartDate()); + auth.setEndDate(request.getEndDate()); + auth.setPhoneNumber(request.getPhoneNumber()); + auth.setTeamId(request.getTeamId()); + auth.setPositionId(request.getPositionId()); + auth.setRankId(request.getRankId()); + + return auth; + } +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/Application/dto/AuthDTO.java b/src/main/java/org/triumers/kmsback/auth/command/Application/dto/AuthDTO.java new file mode 100644 index 00000000..4a41a287 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/Application/dto/AuthDTO.java @@ -0,0 +1,24 @@ +package org.triumers.kmsback.auth.command.Application.dto; + +import lombok.*; + +import java.time.LocalDate; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class AuthDTO { + private String email; + private String password; + private String name; + private String profileImg; + private String role; + private LocalDate startDate; + private LocalDate endDate; + private String phoneNumber; + private int teamId; + private int positionId; + private int rankId; +} \ No newline at end of file diff --git a/src/main/java/org/triumers/kmsback/auth/command/Application/service/AuthService.java b/src/main/java/org/triumers/kmsback/auth/command/Application/service/AuthService.java new file mode 100644 index 00000000..e2adb905 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/Application/service/AuthService.java @@ -0,0 +1,7 @@ +package org.triumers.kmsback.auth.command.Application.service; + +import org.triumers.kmsback.auth.command.Application.dto.AuthDTO; + +public interface AuthService { + void signup(AuthDTO authDTO); +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/Application/service/AuthServiceImpl.java b/src/main/java/org/triumers/kmsback/auth/command/Application/service/AuthServiceImpl.java new file mode 100644 index 00000000..f9a5481f --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/Application/service/AuthServiceImpl.java @@ -0,0 +1,54 @@ +package org.triumers.kmsback.auth.command.Application.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.triumers.kmsback.auth.command.Application.dto.AuthDTO; +import org.triumers.kmsback.auth.command.domain.aggregate.entity.Auth; +import org.triumers.kmsback.auth.command.domain.aggregate.enums.UserRole; +import org.triumers.kmsback.auth.command.domain.repository.AuthRepository; + +@Service +public class AuthServiceImpl implements AuthService { + private final String DEFAULT_PASSWORD; + + private final AuthRepository authRepository; + private final BCryptPasswordEncoder bCryptPasswordEncoder; + + @Autowired + public AuthServiceImpl(@Value("${password}") String password, AuthRepository authRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { + this.DEFAULT_PASSWORD = password; + this.authRepository = authRepository; + this.bCryptPasswordEncoder = bCryptPasswordEncoder; + } + + @Override + public void signup(AuthDTO authDTO) { + + authRepository.save(authMapper(authDTO)); + } + + private Auth authMapper(AuthDTO authDTO) { + Auth auth = new Auth(); + + auth.setEmail(authDTO.getEmail()); + auth.setName(authDTO.getName()); + auth.setProfileImg(authDTO.getProfileImg()); + auth.setUserRole(UserRole.valueOf(authDTO.getRole())); + auth.setStartDate(authDTO.getStartDate()); + auth.setEndDate(authDTO.getEndDate()); + auth.setPhoneNumber(authDTO.getPhoneNumber()); + auth.setTeamId(authDTO.getTeamId()); + auth.setPositionId(authDTO.getPositionId()); + auth.setRankId(authDTO.getRankId()); + + if (authDTO.getPassword() != null) { + auth.setPassword(bCryptPasswordEncoder.encode(authDTO.getPassword())); + return auth; + } + auth.setPassword(bCryptPasswordEncoder.encode(DEFAULT_PASSWORD)); + + return auth; + } +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/entity/Auth.java b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/entity/Auth.java new file mode 100644 index 00000000..ca12be46 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/entity/Auth.java @@ -0,0 +1,59 @@ +package org.triumers.kmsback.auth.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.triumers.kmsback.auth.command.domain.aggregate.enums.UserRole; +import org.triumers.kmsback.auth.command.domain.aggregate.enums.UserRoleConverter; + +import java.time.LocalDate; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@Table(name = "tbl_employee") +public class Auth { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private int id; + + @Column(name = "EMAIL") + private String email; + + @Column(name = "PASSWORD") + private String password; + + @Column(name = "NAME") + private String name; + + @Column(name = "PROFILE_IMG") + private String profileImg; + + @Column(name = "ROLE") + @Convert(converter = UserRoleConverter.class) + private UserRole userRole; + + @CreatedDate + @Column(name = "START_DATE") + private LocalDate startDate; + + @Column(name = "END_DATE") + private LocalDate endDate; + + @Column(name = "PHONE") + private String phoneNumber; + + @Column(name = "TEAM_ID") + private int teamId; + + @Column(name = "POSITION_ID") + private int positionId; + + @Column(name = "RANK_ID") + private int rankId; +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/enums/UserRole.java b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/enums/UserRole.java new file mode 100644 index 00000000..f95fd3e2 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/enums/UserRole.java @@ -0,0 +1,7 @@ +package org.triumers.kmsback.auth.command.domain.aggregate.enums; + +public enum UserRole { + ADMIN, + MANAGER, + NORMAL +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/enums/UserRoleConverter.java b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/enums/UserRoleConverter.java new file mode 100644 index 00000000..d7f699e5 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/enums/UserRoleConverter.java @@ -0,0 +1,16 @@ +package org.triumers.kmsback.auth.command.domain.aggregate.enums; + +import jakarta.persistence.AttributeConverter; + +public class UserRoleConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(UserRole userRole) { + return userRole.name(); + } + + @Override + public UserRole convertToEntityAttribute(String dbData) { + return UserRole.valueOf(dbData); + } +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/vo/CmdRequestAuthVO.java b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/vo/CmdRequestAuthVO.java new file mode 100644 index 00000000..9d359567 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/vo/CmdRequestAuthVO.java @@ -0,0 +1,24 @@ +package org.triumers.kmsback.auth.command.domain.aggregate.vo; + +import lombok.*; + +import java.time.LocalDate; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class CmdRequestAuthVO { + private String email; + private String password; + private String name; + private String profileImg; + private String role; + private LocalDate startDate; + private LocalDate endDate; + private String phoneNumber; + private int teamId; + private int positionId; + private int rankId; +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/vo/CmdResponseMessageVO.java b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/vo/CmdResponseMessageVO.java new file mode 100644 index 00000000..be04dff6 --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/domain/aggregate/vo/CmdResponseMessageVO.java @@ -0,0 +1,12 @@ +package org.triumers.kmsback.auth.command.domain.aggregate.vo; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class CmdResponseMessageVO { + private String message; +} diff --git a/src/main/java/org/triumers/kmsback/auth/command/domain/repository/AuthRepository.java b/src/main/java/org/triumers/kmsback/auth/command/domain/repository/AuthRepository.java new file mode 100644 index 00000000..6091862c --- /dev/null +++ b/src/main/java/org/triumers/kmsback/auth/command/domain/repository/AuthRepository.java @@ -0,0 +1,9 @@ +package org.triumers.kmsback.auth.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.kmsback.auth.command.domain.aggregate.entity.Auth; + +@Repository +public interface AuthRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/org/triumers/kmsback/common/config/SecurityConfig.java b/src/main/java/org/triumers/kmsback/common/config/SecurityConfig.java index a0264b6a..058e03e2 100644 --- a/src/main/java/org/triumers/kmsback/common/config/SecurityConfig.java +++ b/src/main/java/org/triumers/kmsback/common/config/SecurityConfig.java @@ -1,4 +1,85 @@ package org.triumers.kmsback.common.config; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Collections; +import java.util.List; + +@Configuration +@EnableWebSecurity public class SecurityConfig { + + private final AuthenticationConfiguration authenticationConfiguration; + + public SecurityConfig(AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = authenticationConfiguration; + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { + + return configuration.getAuthenticationManager(); + } + + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + // CORS 설정 + http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + + List allowStringList = Collections.singletonList("*"); + List exposedHeaders = List.of("Authorization", "UserRole"); + CorsConfiguration configuration = new CorsConfiguration(); + + configuration.setAllowedOriginPatterns(allowStringList); + configuration.setAllowedMethods(allowStringList); + configuration.setAllowedHeaders(allowStringList); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + configuration.setExposedHeaders(exposedHeaders); + + return configuration; + } + })); + + // csrf disable + http.csrf(AbstractHttpConfigurer::disable); + + //From 로그인 방식 disable + http.formLogin(AbstractHttpConfigurer::disable); + + //http basic 인증 방식 disable + http.httpBasic(AbstractHttpConfigurer::disable); + + //경로별 인가 작업 + http.authorizeHttpRequests((auth) -> auth + .requestMatchers("/**").permitAll() +// .requestMatchers("/auth/signup").permitAll() + .anyRequest().authenticated()); + + //세션 설정 + http.sessionManagement((session) -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + + return http.build(); + } } diff --git a/src/test/java/org/triumers/kmsback/auth/command/Application/service/AuthServiceTest.java b/src/test/java/org/triumers/kmsback/auth/command/Application/service/AuthServiceTest.java new file mode 100644 index 00000000..113f116b --- /dev/null +++ b/src/test/java/org/triumers/kmsback/auth/command/Application/service/AuthServiceTest.java @@ -0,0 +1,33 @@ +package org.triumers.kmsback.auth.command.Application.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import org.triumers.kmsback.auth.command.Application.dto.AuthDTO; +import org.triumers.kmsback.auth.command.domain.repository.AuthRepository; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +class AuthServiceTest { + + @Autowired + private AuthService authService; + + @Autowired + private AuthRepository authRepository; + + @Test + void signup() { + + // given + AuthDTO newEmployee = + new AuthDTO("test@gmail.com", "12341234", "테스트유저", null, "NORMAL", null, null, "010-1234-5678", 1, 1, 1); + + // when, then + assertDoesNotThrow(() -> authService.signup(newEmployee)); + } +} \ No newline at end of file