Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자료구조와 함께 배우는 알고리즘 입문
- 처음 만나는 AI 수학 with Python
- 처음 만나는 AI수학 with Python
- 티스토리 쿠키 삭제
- 스프링 시큐리티
- GIT
- Kernighan의 C언어 프로그래밍
- 네트워크 설정
- 선형대수
- ㅒ
- 페이징
- 구멍가게코딩단
- /etc/network/interfaces
- resttemplate
- baeldung
- 스프링부트핵심가이드
- 서버설정
- 알파회계
- 코드로배우는스프링부트웹프로젝트
- 이터레이터
- iterator
- 코드로배우는스프링웹프로젝트
- 친절한SQL튜닝
- 데비안
- 리눅스
- 목록처리
- network configuration
- 자료구조와함께배우는알고리즘입문
- 자바편
- d
Archives
- Today
- Total
bright jazz music
blog 31 : 세션 토큰 발급기능 추가 2 본문
클라이언트 입장에서는 전송하기 위해 세션(=accessToken)이 필요하다.
현재는 AuthService에서 user.addSession()만 할 뿐 그 세션 값을 가져오지는 않고 있다.
1. User 클래스의 addSession() 메서드가 세션을 반환하도록 설정
//User.java
package com.endofma.blog.domain;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name="users") //테이블 명 설정 user는 예약어라 오류발생
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String password;
private LocalDateTime createdAt;
//연관관계 맺기
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user") //한 명의 유저가 여러 세션을 가질 수 있기 때문에 OneToMany
private List<Session> sessions = new ArrayList<>();
@Builder
public User(String name, String email, String password){
this.name = name;
this.email = email;
this. password = password;
this.createdAt = LocalDateTime.now(); //정책상 가입하여 생성된 시간을 넣어준다.
//세션은 넣지 않는다. 가입을 한다고 해서 세션을 추가해 줄 필요는 없다.
}
public Session addSession() {
Session session = Session.builder()
.user(this)
.build();
sessions.add(session);
return session;
}
}
2. AuthService 수정
//AuthService.java
package com.endofma.blog.service;
import com.endofma.blog.domain.Session;
import com.endofma.blog.domain.User;
import com.endofma.blog.exception.InvalidSigninInformation;
import com.endofma.blog.repository.UserRepository;
import com.endofma.blog.request.Login;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
@RequiredArgsConstructor
public class AuthService {
private final UserRepository userRepository;
@Transactional
public String signin(Login login){
User user = userRepository.findByEmailAndPassword(login.getEmail(), login.getPassword())
.orElseThrow(InvalidSigninInformation::new);
//세션 발급
Session session = user.addSession();
return session.getAccessToken();
}
}
3. AuthController 수정
//AuthController.java
package com.endofma.blog.controller;
import com.endofma.blog.domain.User;
import com.endofma.blog.exception.InvalidRequest;
import com.endofma.blog.exception.InvalidSigninInformation;
import com.endofma.blog.repository.UserRepository;
import com.endofma.blog.request.Login;
import com.endofma.blog.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequiredArgsConstructor
public class AuthController {
//생성자를 통한 주입
//private final UserRepository userRepository;
//로그인 및 인증 절차를 서비스에 위임
private final AuthService authService;
@PostMapping("/auth/login")
public String login(@RequestBody Login login){
String accessToken = authService.signin(login);
return accessToken;
}
}
서버 재기동
4. http 요청
### auth.http
POST http://localhost:8080/auth/login
Content-Type: application/json
{
"email" : "catnails@gmail.com",
"password" : "1234"
}
따라서 이를 json타입으로 리턴하도록 만들어준다.
5. 응답 클래스 생성: SessionReponse.java
//SesseionResponse.java
package com.endofma.blog.response;
import lombok.Getter;
@Getter
public class SessionResponse {
private final String accessToken;
public SessionResponse(String accessToken){
this.accessToken = accessToken;
}
}
6. AuthController 수정
//AuthController.java
package com.endofma.blog.controller;
import com.endofma.blog.request.Login;
import com.endofma.blog.response.SessionResponse;
import com.endofma.blog.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequiredArgsConstructor
public class AuthController {
//생성자를 통한 주입
//private final UserRepository userRepository;
//로그인 및 인증 절차를 서비스에 위임
private final AuthService authService;
// @ResponseBody
@PostMapping("/auth/login")
public SessionResponse login(@RequestBody Login login){
String accessToken = authService.signin(login);
return new SessionResponse(accessToken);
}
}
? @ResponseBody 안 붙여도 되네
### auth.http
POST http://localhost:8080/auth/login
Content-Type: application/json
{
"email" : "catnails@gmail.com",
"password" : "1234"
}
7. 테스트 코드 작성
test3
package com.endofma.blog.controller;
import com.endofma.blog.domain.User;
import com.endofma.blog.repository.SessionRepository;
import com.endofma.blog.repository.UserRepository;
import com.endofma.blog.request.Login;
import com.endofma.blog.request.PostCreate;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.juli.logging.Log;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
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.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import javax.print.attribute.standard.Media;
import javax.transaction.Transactional;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class AuthControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private UserRepository userRepository;
@Autowired
private SessionRepository sessionRepository;
@BeforeEach
void clean(){
userRepository.deleteAll();
}
@Test
@DisplayName("로그인 성공 ")
void test1() throws Exception {
//given
//테스트에서는 data.sql이 실행되지 않으므로, 또 실행된다고 해도 테스트와 독립된 것이 권장되므로 값을 넣어준다.
userRepository.save(User.builder()
.name("catnails")
.email("catnails@gmail.com")
.password("1234") //추후 scrypt, bcrypt 적용
.build());
Login login = Login.builder()
.email("catnails@gmail.com")
.password("1234")
.build();
String json = objectMapper.writeValueAsString(login);
//expected
mockMvc.perform(post("/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
.andDo(print());
}
@Test
@Transactional
@DisplayName("로그인 성공 후 세션 추가")
void test2() throws Exception {
//given
User user = userRepository.save(User.builder()
.name("catnails")
.email("catnails@gmail.com")
.password("1234") //추후 scrypt, bcrypt 적용
.build());
Login login = Login.builder()
.email("catnails@gmail.com")
.password("1234")
.build();
String json = objectMapper.writeValueAsString(login);
//expected
mockMvc.perform(post("/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
.andDo(print());
User loggedInUser = userRepository.findById(user.getId())
.orElseThrow(RuntimeException::new);
Assertions.assertEquals(1L, user.getSessions().size());
}
@Test
@DisplayName("로그인 성공 세션 응답")
void test3() throws Exception {
//given
User user = userRepository.save(User.builder()
.name("catnails")
.email("catnails@gmail.com")
.password("1234") //추후 scrypt, bcrypt 적용
.build());
Login login = Login.builder()
.email("catnails@gmail.com")
.password("1234")
.build();
String json = objectMapper.writeValueAsString(login);
//expected
mockMvc.perform(post("/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
// .andExpect(MockMvcResultMatchers.jsonPath("$.accessToken", Matchers.nullValue()))
.andExpect(MockMvcResultMatchers.jsonPath("$.accessToken", Matchers.notNullValue()))
.andDo(print());
}
}
성공
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.6)
2023-02-15 23:18:22.533 INFO 11192 --- [ Test worker] c.e.blog.controller.AuthControllerTest : Starting AuthControllerTest using Java 11.0.12 on DESKTOP-Q7HBM41 with PID 11192 (started by user in D:\personal\blog)
2023-02-15 23:18:22.535 INFO 11192 --- [ Test worker] c.e.blog.controller.AuthControllerTest : No active profile set, falling back to 1 default profile: "default"
2023-02-15 23:18:23.562 INFO 11192 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-02-15 23:18:23.670 INFO 11192 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 96 ms. Found 3 JPA repository interfaces.
2023-02-15 23:18:24.426 INFO 11192 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-02-15 23:18:24.769 INFO 11192 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-02-15 23:18:24.881 INFO 11192 --- [ Test worker] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-02-15 23:18:24.972 INFO 11192 --- [ Test worker] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.14.Final
2023-02-15 23:18:25.267 INFO 11192 --- [ Test worker] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-02-15 23:18:25.503 INFO 11192 --- [ Test worker] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2023-02-15 23:18:26.591 INFO 11192 --- [ Test worker] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-02-15 23:18:26.603 INFO 11192 --- [ Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-02-15 23:18:27.727 WARN 11192 --- [ Test worker] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2023-02-15 23:18:28.221 INFO 11192 --- [ Test worker] o.s.b.a.h2.H2ConsoleAutoConfiguration : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:blog'
2023-02-15 23:18:28.757 INFO 11192 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2023-02-15 23:18:28.757 INFO 11192 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
2023-02-15 23:18:28.758 INFO 11192 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 0 ms
2023-02-15 23:18:28.793 INFO 11192 --- [ Test worker] c.e.blog.controller.AuthControllerTest : Started AuthControllerTest in 6.806 seconds (JVM running for 10.206)
MockHttpServletRequest:
HTTP Method = POST
Request URI = /auth/login
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"48"]
Body = {"email":"catnails@gmail.com","password":"1234"}
Session Attrs = {}
Handler:
Type = com.endofma.blog.controller.AuthController
Method = com.endofma.blog.controller.AuthController#login(Login)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"accessToken":"53f70d4c-0792-4cb0-8ecb-f362a3bf49d4"}
Forwarded URL = null
Redirected URL = null
Cookies = []
2023-02-15 23:18:29.775 INFO 11192 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-02-15 23:18:29.776 INFO 11192 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2023-02-15 23:18:29.782 WARN 11192 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 90121, SQLState: 90121
2023-02-15 23:18:29.782 ERROR 11192 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-214]
2023-02-15 23:18:29.784 WARN 11192 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 90121, SQLState: 90121
2023-02-15 23:18:29.784 ERROR 11192 --- [ionShutdownHook] o.h.engine.jdbc.spi.SqlExceptionHelper : Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-214]
2023-02-15 23:18:29.784 WARN 11192 --- [ionShutdownHook] o.s.b.f.support.DisposableBeanAdapter : Invocation of destroy method failed on bean with name 'entityManagerFactory': org.hibernate.exception.JDBCConnectionException: Unable to release JDBC Connection used for DDL execution
2023-02-15 23:18:29.784 INFO 11192 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2023-02-15 23:18:29.790 INFO 11192 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
BUILD SUCCESSFUL in 14s
5 actionable tasks: 2 executed, 3 up-to-date
PM 11:18:30: Execution finished ':test --tests "com.endofma.blog.controller.AuthControllerTest.test3"'.
'Projects > blog' 카테고리의 다른 글
blog33 : 쿠키를 통한 인증 및 검증 (0) | 2023.02.16 |
---|---|
blog32 : 데이터베이스를 통한 토큰 검증 (0) | 2023.02.16 |
blog 31 : 세션 토큰 발급기능 추가 1 (0) | 2023.02.15 |
blog 30 : 고정인증 로그인 기능 구현 2 (0) | 2023.02.14 |
blog 30 : 고정인증 로그인 기능 구현 1 (0) | 2023.02.14 |
Comments