Skip to content
Snippets Groups Projects
Commit fb73e1f5 authored by donghyun's avatar donghyun
Browse files

Merge branch 'kkj' into 'master'

게시글 작성, 댓글 작성, 유저 관리 기능 추가

See merge request !1
parents 2c1f059a ea0d614a
No related branches found
No related tags found
1 merge request!1게시글 작성, 댓글 작성, 유저 관리 기능 추가
Showing
with 434 additions and 180 deletions
...@@ -24,8 +24,10 @@ repositories { ...@@ -24,8 +24,10 @@ repositories {
} }
dependencies { dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
//H2 DB 추가 //H2 DB 추가
runtimeOnly 'com.h2database:h2' runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok'
...@@ -33,6 +35,13 @@ dependencies { ...@@ -33,6 +35,13 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
} }
tasks.named('test') { tasks.named('test') {
......
package umc.spring.board.conroller;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import umc.spring.board.domain.Board;
import umc.spring.board.dto.BoardRequestDto;
import umc.spring.board.dto.BoardResponseDto;
import umc.spring.board.repository.BoardRepository;
import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/posts")
public class BoardController {
private final BoardRepository boardRepository;
@PostMapping("/upload")
public void upload(@RequestBody BoardRequestDto boardDto){
System.out.println(boardDto);
Board board = boardDto.toBoard();
boardRepository.save(board);
}
@GetMapping("/")
public List<BoardResponseDto> getPosts(){
return boardRepository.findAll().stream().map(BoardResponseDto::toDto).toList();
}
@GetMapping("/{id}")
public BoardResponseDto getPost(@PathVariable(name="id") Long boardId){
return BoardResponseDto.toDto(boardRepository.findById(boardId).orElseThrow(RuntimeException::new));
}
}
package umc.spring.board.domain;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import umc.spring.member.domain.Member;
import java.sql.Date;
@Entity
@Getter
@Setter
public class Board {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private String body;
@CreationTimestamp
private Date date;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
Member member;
protected Board(){}
@Builder
public Board(String title, String author, String body) {
this.title = title;
this.author = author;
this.body = body;
}
}
package umc.spring.board.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import umc.spring.board.domain.Board;
@Getter
@Setter
public class BoardRequestDto {
private String title;
private String author;
private String body;
@Builder
public BoardRequestDto(String title, String author, String body) {
this.title = title;
this.author = author;
this.body = body;
}
public Board toBoard() {
return Board.builder()
.author(this.getAuthor())
.title(this.getTitle())
.body(this.getBody())
.build();
}
}
package umc.spring.board.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import umc.spring.board.domain.Board;
@Getter
@Setter
public class BoardResponseDto {
private Long id;
private String title;
private String author;
private String body;
@Builder
public BoardResponseDto(Long id, String title, String author, String body) {
this.id = id;
this.title = title;
this.author = author;
this.body = body;
}
public static BoardResponseDto toDto(Board board){
return BoardResponseDto.builder()
.id(board.getId())
.body(board.getBody())
.title(board.getTitle())
.author(board.getAuthor())
.build();
}
}
package umc.spring.board.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(readOnly = true)
public class BoardService {
}
package umc.spring.member.controller;
public class MemberController {
}
package umc.spring.member.domain;
import jakarta.persistence.*;
import lombok.Getter;
@Entity
@Getter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
}
package umc.spring.member.dto;
public class MemberRequestDto {
}
package umc.spring.member.service;
public class MemberService {
}
package umc.spring.post.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "DELETE")
.allowCredentials(false)
.maxAge(3000);
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@RequiredArgsConstructor
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = resolveToken((HttpServletRequest) request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
System.out.println(authentication);
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println(authentication);
}
chain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) {
return bearerToken.substring(7);
}
return null;
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;
@Component
public class JwtTokenProvider {
private final String secretKey;
public JwtTokenProvider( @Value("${jwt.secret}") final String secretKey) {
this.secretKey = secretKey;
}
public TokenInfo generateToken(Authentication authentication) {
System.out.println(authentication);
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
System.out.println("auth " + authorities);
long now = (new Date()).getTime();
Date accessTokenExpiration = new Date(now + 3600000); // 1h
Date refreshTokenExpiration = new Date(now + 1209600000); // 14d
String accessToken = Jwts.builder()
.setSubject(authentication.getName())
.claim("auth", authorities)
.setExpiration(accessTokenExpiration)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
String refreshToken = Jwts.builder()
.setExpiration(refreshTokenExpiration)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
TokenInfo tokenInfo = new TokenInfo();
tokenInfo.setGrantType("Bearer");
tokenInfo.setAccessToken(accessToken);
tokenInfo.setRefreshToken(refreshToken);
return tokenInfo;
}
public Authentication getAuthentication(String accessToken) {
Claims claims = parseClaims(accessToken);
System.out.println("log" + claims);
if (claims.get("auth") == null) {
throw new RuntimeException("권한 정보가 없는 토큰입니다.");
}
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get("auth").toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
UserDetails principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
System.out.println("Invalid JWT Token" + e);
} catch (ExpiredJwtException e) {
System.out.println("Expired JWT Token" + e);
} catch (UnsupportedJwtException e) {
System.out.println("Unsupported JWT Token" + e);
} catch (IllegalArgumentException e) {
System.out.println("JWT claims string is empty." + e);
}
return false;
}
private Claims parseClaims(String accessToken) {
try {
return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(accessToken).getBody();
} catch (ExpiredJwtException e) {
return e.getClaims();
}
}
}
\ No newline at end of file
package umc.spring.post.config.security;
public enum Role {
USER("ROLE_USER"),
ADMIN("ROLE_ADMIN");
private final String value;
public String getValue() {
return value;
}
Role(String value) {
this.value = value;
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
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.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@EnableWebSecurity
@Configuration
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.httpBasic(HttpBasicConfigurer::disable)
.csrf(CsrfConfigurer::disable)
.cors(Customizer.withDefaults())
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize.requestMatchers("/**").permitAll())
.addFilterBefore(jwtAuthenticationFilter, BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import umc.spring.post.data.dto.UserInfoDto;
public class SecurityUtil {
public static UserInfoDto getCurrentMemberId() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getName() == null) {
throw new RuntimeException("No authentication information.");
}
UserInfoDto userInfoDto = new UserInfoDto();
userInfoDto.setUserId(authentication.getName());
userInfoDto.setMemberRole(authentication.getAuthorities().stream().toList().get(0).toString().replaceAll("ROLE_", ""));
return userInfoDto;
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import lombok.Data;
@Data
public class TokenInfo {
private String grantType;
private String accessToken;
private String refreshToken;
private String email;
private String memberRole;
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMemberRole() {
return memberRole;
}
public void setMemberRole(String memberRole) {
this.memberRole = memberRole;
}
}
\ No newline at end of file
package umc.spring.post.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import umc.spring.post.config.security.TokenInfo;
import umc.spring.post.data.dto.UserInfoDto;
import umc.spring.post.data.dto.UserJoinDto;
import umc.spring.post.data.dto.UserLoginDto;
import umc.spring.post.service.AuthService;
@RestController
@RequestMapping("/user")
public class AuthController {
@Autowired
private final AuthService authService;
public AuthController(AuthService authService) {
this.authService = authService;
}
@PostMapping("/login")
public TokenInfo login(@RequestBody UserLoginDto userLoginDto) {
return authService.login(userLoginDto);
}
@PostMapping("/register")
public void register(@RequestBody UserJoinDto userJoinDto) {
authService.join(userJoinDto);
}
@GetMapping("/info")
public UserInfoDto info() {
return authService.info();
}
}
package umc.spring.post.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import umc.spring.post.data.dto.CommentDto;
import umc.spring.post.data.dto.PostDto;
import umc.spring.post.data.dto.PostResDto;
import umc.spring.post.data.entity.Post;
import umc.spring.post.service.PostService;
import java.util.List;
import static org.springframework.data.jpa.domain.AbstractPersistable_.id;
@RestController
public class PostController {
@Autowired
private final PostService postService;
public PostController(PostService postService) {
this.postService = postService;
}
@ResponseStatus(HttpStatus.OK)
@GetMapping("/posts")
public List<PostResDto> getAllPost(){
return postService.getAllPost();
}
@ResponseStatus(HttpStatus.OK)
@PostMapping("/post/upload")
public void upload(@RequestBody PostDto postDto){
postService.upload(postDto);
}
@GetMapping("/post/{id}")
public PostResDto getPostById(@PathVariable Long id){
return postService.getPostById(id);
}
@DeleteMapping("/post/{id}")
@ResponseStatus(HttpStatus.OK)
public void deletePost(@PathVariable Long id){
if(!postService.deletePost(id)){
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Post not found");
}
}
@PutMapping("/post/{id}")
@ResponseStatus(HttpStatus.OK)
public void editPost(@RequestBody PostDto postDto,@PathVariable Long id){
if(!postService.editPost(postDto,id)){
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Post not found");
}
}
@PostMapping("/post/likes")
@ResponseStatus(HttpStatus.OK)
public void likeCrew(@RequestParam Long id){
postService.likeCrew(id);
}
@DeleteMapping ("/post/likes")
@ResponseStatus(HttpStatus.OK)
public void dislikeCrew(@RequestParam Long id){
postService.dislikeCrew(id);
}
@PostMapping("/post/search")
public List<PostResDto> search(@RequestBody PostDto postDto){
return postService.search(postDto.getTitle());
}
@PostMapping("/post/comments")
@ResponseStatus(HttpStatus.OK)
public void addComment(@RequestBody CommentDto commentDto){
postService.addComment(commentDto);
}
@DeleteMapping("/post/comments")
@ResponseStatus(HttpStatus.OK)
public void deleteComment(@RequestParam Long id){
if(!postService.deleteComment(id)){
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Post not found");
}
}
}
package umc.spring.post.data.dto;
import lombok.Data;
@Data
public class CommentDto {
private Long postId;
private Long userId;
private String author;
private String text;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment