diff --git a/build.gradle b/build.gradle index 19506e7..bd8e75d 100644 --- a/build.gradle +++ b/build.gradle @@ -22,9 +22,12 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' - + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.session:spring-session-data-redis' + implementation 'org.springframework.session:spring-session-core' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + runtimeOnly 'com.h2database:h2' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/com/example/oneul/DTO/LoginDTO.java b/src/main/java/com/example/oneul/DTO/LoginDTO.java index af0f140..a1be3ba 100644 --- a/src/main/java/com/example/oneul/DTO/LoginDTO.java +++ b/src/main/java/com/example/oneul/DTO/LoginDTO.java @@ -2,10 +2,13 @@ package com.example.oneul.DTO; import com.example.oneul.model.UserEntity; +import lombok.Getter; + +@Getter public class LoginDTO { private String username; private String password; - + public LoginDTO(String username, String password){ this.username = username; this.password = password; diff --git a/src/main/java/com/example/oneul/DTO/SignUpDTO.java b/src/main/java/com/example/oneul/DTO/SignUpDTO.java new file mode 100644 index 0000000..30113df --- /dev/null +++ b/src/main/java/com/example/oneul/DTO/SignUpDTO.java @@ -0,0 +1,39 @@ +package com.example.oneul.DTO; + +import com.example.oneul.model.UserEntity; + +import lombok.Getter; + +@Getter +public class SignUpDTO { + private String username; + private String password1; + private String password2; + + public void setUsername(String username ){ + this.username = username; + } + + public void setPassword1(String password1){ + this.password1 = password1; + } + + public void setPassword2(String password2){ + this.password2 = password2; + } + + public SignUpDTO() {} + + public SignUpDTO(String username, String password1, String password2){ + this.username = username; + this.password1 = password1; + this.password2 = password2; + } + + public UserEntity toEntity(){ + return UserEntity.builder() + .username(this.username) + .password(this.password1) + .build(); + } +} diff --git a/src/main/java/com/example/oneul/OneulApplication.java b/src/main/java/com/example/oneul/OneulApplication.java index 86285f4..ac1c8f7 100644 --- a/src/main/java/com/example/oneul/OneulApplication.java +++ b/src/main/java/com/example/oneul/OneulApplication.java @@ -2,10 +2,14 @@ package com.example.oneul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; + @SpringBootApplication +@EnableJpaAuditing +@EnableRedisHttpSession public class OneulApplication { - public static void main(String[] args) { SpringApplication.run(OneulApplication.class, args); } diff --git a/src/main/java/com/example/oneul/config/RedisConfig.java b/src/main/java/com/example/oneul/config/RedisConfig.java new file mode 100644 index 0000000..1678826 --- /dev/null +++ b/src/main/java/com/example/oneul/config/RedisConfig.java @@ -0,0 +1,37 @@ +package com.example.oneul.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + @Value("${spring.redis.host}") + private String hostName; + @Value("${spring.redis.port}") + private int port; + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory){ + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + redisTemplate.setConnectionFactory(connectionFactory); + return redisTemplate; + } + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(hostName); + redisStandaloneConfiguration.setPort(port); + LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration); + return lettuceConnectionFactory; + } +} diff --git a/src/main/java/com/example/oneul/config/WebSecurityConfig.java b/src/main/java/com/example/oneul/config/WebSecurityConfig.java new file mode 100644 index 0000000..bc867ae --- /dev/null +++ b/src/main/java/com/example/oneul/config/WebSecurityConfig.java @@ -0,0 +1,26 @@ +package com.example.oneul.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Bean + public PasswordEncoder getPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + httpSecurity.cors().disable() + .csrf().disable() + .formLogin().disable() + .headers().frameOptions().disable(); + } +} diff --git a/src/main/java/com/example/oneul/controller/UserController.java b/src/main/java/com/example/oneul/controller/UserController.java new file mode 100644 index 0000000..7036d51 --- /dev/null +++ b/src/main/java/com/example/oneul/controller/UserController.java @@ -0,0 +1,34 @@ +package com.example.oneul.controller; + + +import javax.servlet.http.HttpSession; + +import com.example.oneul.DTO.SignUpDTO; +import com.example.oneul.model.UserEntity; +import com.example.oneul.service.command.UserCommandService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping(value = "/user") +public class UserController { + private final UserCommandService userCommandService; + + private final Logger log = LoggerFactory.getLogger(UserController.class); + + public UserController(UserCommandService userCommandService){ + this.userCommandService = userCommandService; + } + + @RequestMapping(value="/", method=RequestMethod.POST) + public UserEntity signUp(@RequestBody SignUpDTO signUpDTO, HttpSession httpSession) { + UserEntity user = userCommandService.signUp(signUpDTO.toEntity(), httpSession); + return user; + } +} diff --git a/src/main/java/com/example/oneul/model/Post.java b/src/main/java/com/example/oneul/model/Post.java index cd85780..145e05b 100644 --- a/src/main/java/com/example/oneul/model/Post.java +++ b/src/main/java/com/example/oneul/model/Post.java @@ -1,19 +1,23 @@ package com.example.oneul.model; import java.time.LocalDateTime; +import java.util.Objects; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity +@EntityListeners(AuditingEntityListener.class) public class Post { @Id @GeneratedValue private Long id; @@ -66,6 +70,23 @@ public class Post { this.writer = writer; } + @Override + public boolean equals(Object object){ + if(this == object) { + return true; + } + if(object == null || getClass() != object.getClass()) { + return false; + } + Post that = (Post) object; + return this.id == that.id; + } + + @Override + public int hashCode() { + return Objects.hash(this.id); + } + @Override public String toString(){ return "Post[" diff --git a/src/main/java/com/example/oneul/model/UserEntity.java b/src/main/java/com/example/oneul/model/UserEntity.java index 7fdf5e9..649aef0 100644 --- a/src/main/java/com/example/oneul/model/UserEntity.java +++ b/src/main/java/com/example/oneul/model/UserEntity.java @@ -1,19 +1,24 @@ package com.example.oneul.model; +import java.io.Serializable; import java.time.LocalDateTime; +import java.util.Objects; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EntityListeners; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Entity -public class UserEntity { +@EntityListeners(AuditingEntityListener.class) +public class UserEntity implements Serializable { @Id @GeneratedValue private Long id; @Column(nullable = false, unique = true) @@ -71,6 +76,23 @@ public class UserEntity { this.createdAt = user.getCreatedAt(); } + @Override + public boolean equals(Object object){ + if(this == object) { + return true; + } + if(object == null || getClass() != object.getClass()) { + return false; + } + UserEntity that = (UserEntity) object; + return this.id == that.id; + } + + @Override + public int hashCode() { + return Objects.hash(this.id); + } + @Override public String toString(){ return "userEntity[" diff --git a/src/main/java/com/example/oneul/repository/UserCommandRepository.java b/src/main/java/com/example/oneul/repository/UserCommandRepository.java index 43590f3..06353d5 100644 --- a/src/main/java/com/example/oneul/repository/UserCommandRepository.java +++ b/src/main/java/com/example/oneul/repository/UserCommandRepository.java @@ -4,9 +4,11 @@ import java.util.Optional; import com.example.oneul.model.UserEntity; +import org.springframework.data.redis.core.RedisHash; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; +@RedisHash @Repository public interface UserCommandRepository extends CrudRepository { Optional findByUsername(String username); diff --git a/src/main/java/com/example/oneul/service/command/UserCommandService.java b/src/main/java/com/example/oneul/service/command/UserCommandService.java index fe17782..e3ab398 100644 --- a/src/main/java/com/example/oneul/service/command/UserCommandService.java +++ b/src/main/java/com/example/oneul/service/command/UserCommandService.java @@ -1,10 +1,12 @@ package com.example.oneul.service.command; +import javax.servlet.http.HttpSession; + import com.example.oneul.model.UserEntity; import org.springframework.stereotype.Service; @Service public interface UserCommandService { - UserEntity signUp(UserEntity userEntity); + UserEntity signUp(UserEntity userEntity, HttpSession httpSession); } diff --git a/src/main/java/com/example/oneul/service/command/UserCommandServiceImpl.java b/src/main/java/com/example/oneul/service/command/UserCommandServiceImpl.java index 84c3f63..c068879 100644 --- a/src/main/java/com/example/oneul/service/command/UserCommandServiceImpl.java +++ b/src/main/java/com/example/oneul/service/command/UserCommandServiceImpl.java @@ -1,6 +1,7 @@ package com.example.oneul.service.command; -import com.example.oneul.exception.UserAlreadyExistException; +import javax.servlet.http.HttpSession; + import com.example.oneul.model.UserEntity; import com.example.oneul.repository.UserCommandRepository; @@ -25,15 +26,15 @@ public class UserCommandServiceImpl implements UserCommandService { @Override @Transactional(isolation = Isolation.SERIALIZABLE) - public UserEntity signUp(UserEntity userEntity){ - userCommandRepository.findByUsername(userEntity.getUsername()).ifPresent(user -> { - log.debug(user.getUsername() + " is present"); - throw new UserAlreadyExistException(userEntity.getUsername() + " is already exist."); - }); + public UserEntity signUp(UserEntity userEntity, HttpSession httpSession){ // ANCHOR: 이게 맞나..? userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword())); - UserEntity user = new UserEntity(userEntity); - return userCommandRepository.save(user); + UserEntity user = userCommandRepository.save(new UserEntity(userEntity)); + + httpSession.setAttribute("user",user); + log.info("session id: " + httpSession.getId()); + log.info("session value: " + httpSession.getAttribute("user")); + return user; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 719ca36..b223d5b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,6 +11,14 @@ spring: url: jdbc:h2:tcp://localhost/~/test driver-class-name: org.h2.Driver username: "sa" + redis: + host: 127.0.0.1 + port: 6379 + +server: + servlet: + session: + timeout: 60 logging: level: diff --git a/src/test/java/com/example/oneul/controller/UserControllerTest.java b/src/test/java/com/example/oneul/controller/UserControllerTest.java new file mode 100644 index 0000000..91f06f3 --- /dev/null +++ b/src/test/java/com/example/oneul/controller/UserControllerTest.java @@ -0,0 +1,62 @@ +package com.example.oneul.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.HashMap; +import java.util.Map; + +import com.example.oneul.service.command.UserCommandService; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.filter.CharacterEncodingFilter; + +@SpringBootTest +@AutoConfigureMockMvc +public class UserControllerTest { + private MockMvc mvc; + @Autowired + private UserCommandService userCommandService; + private MockHttpSession httpSession = new MockHttpSession(); + + @BeforeEach + public void setUp(){ + mvc = MockMvcBuilders.standaloneSetup(new UserController(userCommandService)) + .addFilters(new CharacterEncodingFilter("UTF-8", true)) + .build(); + } + + @Test + @DisplayName("signUp test") + public void signUpTest() throws Exception { + Map requestBody = new HashMap<>(); + requestBody.put("username", "zzzinho"); + requestBody.put("password1", "password"); + requestBody.put("password2", "password"); + + String json = new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(requestBody); + + final ResultActions actions = mvc.perform( + post("/user/") + .contentType(MediaType.APPLICATION_JSON) + .session(httpSession) + .accept(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(json) + ); + + actions.andExpectAll(status().isOk()); + } +} diff --git a/src/test/java/com/example/oneul/service/UserCommandServiceTest.java b/src/test/java/com/example/oneul/service/UserCommandServiceTest.java index ed983c1..239cc99 100644 --- a/src/test/java/com/example/oneul/service/UserCommandServiceTest.java +++ b/src/test/java/com/example/oneul/service/UserCommandServiceTest.java @@ -1,5 +1,7 @@ package com.example.oneul.service; +import static org.junit.jupiter.api.Assertions.assertEquals; + import com.example.oneul.DTO.LoginDTO; import com.example.oneul.model.UserEntity; import com.example.oneul.service.command.UserCommandService; @@ -9,18 +11,24 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.crypto.password.PasswordEncoder; -@Transactional @SpringBootTest @ExtendWith(MockitoExtension.class) public class UserCommandServiceTest { @Autowired private UserCommandService userCommandService; - + @Autowired + private PasswordEncoder passwordEncoder; + protected MockHttpSession httpSession; + @Test public void signUpTest(){ + httpSession = new MockHttpSession(); LoginDTO loginDTO = new LoginDTO("zzzinho", "password"); - UserEntity user = userCommandService.signUp(loginDTO.toEntity()); + UserEntity user = userCommandService.signUp(loginDTO.toEntity(), httpSession); + assertEquals(loginDTO.getUsername(), user.getUsername()); + assertEquals(true, passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())); } }