이제 우리는 회원가입과 로그인을 할수 있다.
로그인한 유저는 GUEST -> USER 로 등업 신청을 할수 있으니
글 등록 기능을 만들어보자.
.antMatchers("/post/**")
.hasRole("USER")
SpringSecurityConfig 에 위의 설정을 추가하여
글 등록 관련 기능들은 USER 권한이 있는 유저만 사용할수 있도록 하겠다.
현재 우리가 사용하는 Entity 는 Member 와 Post 이다.
이 두가지 Entity 에 대해 공통으로 필요한 로직중 하나는 언제 생성된 정보인가
언제 수정되었는가 등의 데이터가 필요하다.
이 공통된 기능들을 BaseTimeEntity 라는 추상 클래스를 만들어 적용해보자.
package com.hello.hello.domain;
import java.time.LocalDateTime;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
@CreatedDate
private LocalDateTime createDate;
}
JPA 매핑 정보를 상속할수 있는 클래스 임을 나타내는 어노테이션이다.
이 클래스를 상속받은 엔티티 클래스들은 해당 클래스의 멤버 변수를 상속받게 된다.
해당 클래스에 JPA 엔티티 리스너를 등록한다.
AuditingEntityListener.class 는 생성일자 등의 감사 정보를 처리하기 위한 리스너이다.
해당 필드가 생성일자를 관리하는 필드임을 나타내는 어노테이션이다.
Spring Data Jpa 가 해당 필드에 생성일을 자동으로 주입한다.
package com.hello.hello.domain.entity;
import com.hello.hello.domain.Authority;
import com.hello.hello.domain.BaseTimeEntity;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Member extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String name;
private String password;
@Enumerated(EnumType.STRING)
@ElementCollection(fetch = FetchType.EAGER)
private Set<Authority> roles = new HashSet<>();
@OneToMany(mappedBy = "member",fetch = FetchType.LAZY)
private List<Post> posts = new ArrayList<>();
public void updateMember(String email,String name,String password) {
this.email = email;
this.name = name;
this.password = password;
}
public void addRole(Set<Authority> roles) {
this.roles = roles;
}
}
package com.hello.hello.domain.entity;
import com.hello.hello.domain.BaseTimeEntity;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Post extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;
}
두 Entity 모두 BaseTimeEntity 를 상속하게 하였다.
package com.hello.hello.controller;
import com.hello.hello.domain.dto.request.NewPostRequest;
import com.hello.hello.service.PostService;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
@PostMapping("/post")
public Long newPost(@RequestBody @Valid NewPostRequest newPostRequest, HttpServletRequest httpServletRequest) {
return postService.save(newPostRequest, httpServletRequest);
}
}
package com.hello.hello.domain.dto.request;
import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class NewPostRequest {
@NotBlank
private String title;
private String content;
}
package com.hello.hello.service;
import com.hello.hello.domain.dto.request.NewPostRequest;
import com.hello.hello.domain.entity.Member;
import com.hello.hello.domain.entity.Post;
import com.hello.hello.repository.MemberJpaRepository;
import com.hello.hello.repository.PostJpaRepository;
import com.hello.hello.utils.JwtProvider;
import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class PostService {
private final PostJpaRepository postJpaRepository;
private final MemberJpaRepository memberJpaRepository;
private final JwtProvider jwtProvider;
public Long save(NewPostRequest newPostRequest, HttpServletRequest httpServletRequest) {
String token = httpServletRequest.getHeader("Authorization");
String getToken = token.replace("Bearer ", "");
String memberEmail = jwtProvider.getMember(getToken);
Member member = memberJpaRepository.findByEmail(memberEmail)
.orElseThrow(() -> new UsernameNotFoundException(memberEmail + " 유저를 찾을수 없습니다."));
Post post = Post.builder().title(newPostRequest.getTitle()).content(newPostRequest.getContent()).member(member)
.build();
postJpaRepository.save(post);
return post.getId();
}
}
로그인 후 등급을 GUEST -> USER 로 등업신청을 하고
Authorization 에 로그인시 발급받은 토큰을 넣어주었다.
"/post" URL로 POST 요청을 보냈더니 원하는대로 POST 가 저장되었고
POST 의 ID 를 전달받았다.
글 조회를 하기 전에 필요한 데이터를 미리 넣어놓았다.
package com.hello.hello.service;
import com.hello.hello.domain.Authority;
import com.hello.hello.domain.entity.Member;
import com.hello.hello.domain.entity.Post;
import com.hello.hello.repository.MemberJpaRepository;
import com.hello.hello.repository.PostJpaRepository;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class InitService {
private final MemberJpaRepository memberJpaRepository;
private final PostJpaRepository postJpaRepository;
private final PasswordEncoder passwordEncoder;
@PostConstruct
@Transactional
public void init() {
memberInit();
postInit();
}
public void memberInit() {
for (int i = 0; i < 3; i++) {
Set<Authority> roles = new HashSet<>();
roles.add(Authority.ROLE_USER);
Member member = Member.builder().email("test" + i + "@gmail.com").name("tester" + i).password(
passwordEncoder.encode("123123"))
.build();
member.addRole(roles);
memberJpaRepository.save(member);
}
}
public void postInit() {
for (int i = 0; i < 3; i++) {
postJpaRepository.save(Post.builder().title("title").content("content")
.member(memberJpaRepository.findByEmail("test"+i+"@gmail.com")
.orElseThrow(() -> new UsernameNotFoundException("유저를 찾을수 없습니다.")))
.build());
}
}
}
@GetMapping("/post")
public List<PostResponse> allPost() {
return postService.findAll();
}
public List<PostResponse> findAll() {
return postJpaRepository.findAll().stream()
.map(p -> PostResponse.builder().id(p.getId()).title(p.getTitle()).content(p.getContent()).author(p.getMember().getEmail()).build())
.collect(
Collectors.toList());
}
test0 , test1 , test2 셋중 하나의 이메일로 로그인 하여 토큰정보를 획득한후
이 토큰을 Authorization 에 추가해준후 "/post" URL 로 Get 요청을 보냈다.
세건의 게시글이 정상적으로 조회되는걸 확인하였다.
단건조회를 하기 위해서는 글의 유니크한 부분이 존재해야 하며
ID를 이용하여 조회해 보겠다.
@GetMapping("/post/{id}")
public PostResponse findOnePost(@PathVariable Long id) {
return postService.findOne(id);
}
public PostResponse findOne(Long id) {
return new PostResponse(postJpaRepository.findById(id).orElseThrow(RuntimeException::new));
}
public PostResponse(Post post) {
this.id = post.getId();
this.title = post.getTitle();
this.content = post.getContent();
this.author = post.getMember().getEmail();
}
로그인 후 Authorization 입력을 해주고 "/post/2" URL로 GET 요청을 보냈다.
(ID 가 2 인 Post 를 조회해줘)
ID 가 2 인 Post 의 정보가 잘 전송되었다.