Skip to content

Commit

Permalink
Merge pull request #52 from PowerSupply-ES/chore-swagger
Browse files Browse the repository at this point in the history
fix: swagger 되돌리기
  • Loading branch information
byeon22 authored Aug 3, 2024
2 parents af1f80c + 7f42e53 commit 2fd0620
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 466 deletions.
5 changes: 1 addition & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,12 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.mysql:mysql-connector-j'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,40 @@
import com.powersupply.PES.answer.dto.AnswerDTO;
import com.powersupply.PES.answer.service.AnswerService;
import com.powersupply.PES.utils.ResponseUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@Tag(name = "Answer", description = "답변 관련 API")
public class AnswerController {

private final AnswerService answerService;

@Operation(summary = "답변 생성", description = "사용자가 답변을 생성합니다.")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "답변 생성 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
// answerEntity 만들기
@PostMapping("/api/answer")
public ResponseEntity<AnswerDTO.GetAnswerId> createAnswer(
@Parameter(description = "회원 이메일", example = "[email protected]") @RequestParam("memberEmail") String email,
@Parameter(description = "문제 ID", example = "1") @RequestParam("problemId") Long problemId) {
public ResponseEntity<AnswerDTO.GetAnswerId> createAnswer(@RequestParam("memberEmail") String email, @RequestParam("problemId") Long problemId) {
return ResponseEntity.status(HttpStatus.CREATED).body(answerService.createAnswer(email, problemId));
}

@Operation(summary = "질문 및 답변 조회", description = "특정 답변 ID에 대한 질문과 답변을 조회합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공(토큰에 문제가 없고 유저를 DB에서 찾을 수 있는 경우)"),
@ApiResponse(responseCode = "404", description = "해당 answerId를 찾을 수 없는 경우"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
// 질문, 답변 가져오기
@GetMapping("/api/answer/{answerId}")
public ResponseEntity<AnswerDTO.GetAnswer> getAnswer(
@Parameter(description = "답변 ID", example = "1") @PathVariable Long answerId) {
public ResponseEntity<AnswerDTO.GetAnswer> getAnswer(@PathVariable Long answerId) {
return ResponseEntity.ok().body(answerService.getAnswer(answerId));
}

@Operation(summary = "답변하기", description = "사용자가 특정 답변 ID에 대해 답변을 작성합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "답변 작성 성공"),
@ApiResponse(responseCode = "400", description = "answer 내용이 null인 경우 or 이미 댓글이 있어 수정이 불가능 한 경우"),
@ApiResponse(responseCode = "404", description = "answer의 주인과 토큰이 다른 경우"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
// 답변하기
@PostMapping("/api/answer/{answerId}")
public ResponseEntity<?> postAnswer(
@Parameter(description = "답변 ID", example = "1") @PathVariable Long answerId,
@RequestBody AnswerDTO.AnswerContent dto) {
public ResponseEntity<?> postAnswer(@PathVariable Long answerId,
@RequestBody AnswerDTO.AnswerContent dto) {
answerService.postAnswer(answerId, dto);
return ResponseUtil.successResponse("");
}

@Operation(summary = "풀이 보기", description = "특정 문제 ID에 대한 풀이 목록을 조회합니다.")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "토큰에 문제가 없고 유저를 DB에서 찾을 수 있는 경우"),
@ApiResponse(responseCode = "204", description = "아직 푼 사람이 없는 경우"),
@ApiResponse(responseCode = "404", description = "problemId가 잘못된 경우"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
// 풀이 보기
@GetMapping("/api/answerlist/{problemId}")
public ResponseEntity<?> getAnswerList(
@Parameter(description = "문제 ID", example = "1") @PathVariable Long problemId) {
public ResponseEntity<?> getAnswerList(@PathVariable Long problemId) {
return answerService.getAnswerList(problemId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,26 @@

import com.powersupply.PES.comment.dto.CommentDTO;
import com.powersupply.PES.comment.service.CommentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@Tag(name = "Comment", description = "댓글 관련 API")
public class CommentController {

private final CommentService commentService;

@Operation(summary = "댓글 조회", description = "특정 답변 ID에 대한 댓글을 조회합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "204", description = "댓글이 없는 경우"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
// 댓글 가져오기
@GetMapping("/api/comment/{answerId}")
public ResponseEntity<?> getComment(
@Parameter(description = "답변 ID", example = "1") @PathVariable Long answerId) {
public ResponseEntity<?> getComment(@PathVariable Long answerId) {
return commentService.getComment(answerId);
}

@Operation(summary = "댓글 달기", description = "특정 답변 ID에 대해 댓글을 작성합니다.")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "토큰에 문제가 없고 유저를 DB에서 찾아 정상적으로 댓글을 생성한 경우"),
@ApiResponse(responseCode = "400", description = "이미 자신의 댓글이 있는 경우"),
@ApiResponse(responseCode = "403", description = "재학생이 아닌 경우 / jwt 문제 / 자신의 답변에 댓글을 단 경우 / 최대 댓글 수 도달"),
@ApiResponse(responseCode = "404", description = "answerId가 잘못된 경우"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
// 댓글 달기
@PostMapping("/api/comment/{answerId}")
public ResponseEntity<?> createComment(
@Parameter(description = "답변 ID", example = "1") @PathVariable Long answerId,
@RequestBody CommentDTO.CreateComment dto) {
public ResponseEntity<?> createComment(@PathVariable Long answerId,
@RequestBody CommentDTO.CreateComment dto) {
return commentService.createComment(answerId, dto);
}
}
21 changes: 3 additions & 18 deletions src/main/java/com/powersupply/PES/configuration/JwtFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,10 @@ public class JwtFilter extends OncePerRequestFilter {
private final String secretKey;

// 인증이 필요없는 경로 URL 목록
private final List<String> skipUrls = Arrays.asList(
"/api/signin",
"/api/signup",
"/api/finduser",
"/swagger-ui/**",
"/v3/api-docs/**",
"/swagger-resources/**",
"/webjars/**"
);
private final List<String> skipUrls = Arrays.asList("/api/signin", "/api/signup", "/api/finduser");

// 인증이 필요 없는 GET 요청의 URL 목록
private final List<String> skipGetUrls = Arrays.asList(
"/api/problemlist/",
"/api/answer/**",
"/api/comment/**",
"/api/answerlist/**",
"/api/rank/**",
"/api/admin/**"
);
private final List<String> skipGetUrls = Arrays.asList("/api/problemlist/" , "/api/answer/**", "/api/comment/**", "/api/answerlist/**", "/api/rank/**", "/api/admin/**");

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Expand All @@ -61,7 +46,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
}

// 인증이 필요없는 경로는 바로 통과
if (skipUrls.stream().anyMatch(uri -> pathMatcher.match(uri, requestURI))) {
if (skipUrls.contains(requestURI)) {
log.info("인증 필요 없음 : {}", requestURI);
filterChain.doFilter(request, response);
return;
Expand Down
70 changes: 28 additions & 42 deletions src/main/java/com/powersupply/PES/configuration/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
Expand All @@ -28,25 +28,18 @@ public class SecurityConfig {
@Value("${jwt.secret}")
private String secretKey;

@Value("${admin.username}")
private String adminUsername;

@Value("${admin.password}")
private String adminPassword;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
return httpSecurity
.httpBasic().and() // 기본 인증 활성화
.csrf().disable() // CSRF 비활성화
.cors().configurationSource(corsConfigurationSource()).and() // CORS 설정
.httpBasic().disable() // UI쪽 들어오는거 disable
.csrf().disable() // cross site 기능
.cors().configurationSource(corsConfigurationSource()).and() // cross site 도메인 다른 경우 허용
.authorizeRequests()
.antMatchers("/api/signin", "/api/signup").permitAll() // 기본 요청 허용
.antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").authenticated() // Swagger 경로에 대한 접근 제한
.antMatchers(HttpMethod.GET, "/api/problemlist", "/api/answer/**", "/api/comment/**", "/api/answerlist/**", "/api/rank/**", "/api/notice/**", "/api/admin/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/comment/**").hasRole("REGULAR_STUDENT")
.antMatchers(HttpMethod.POST, "/api/notice/**").hasRole("MANAGER")
.antMatchers(HttpMethod.PATCH, "/api/notice/**").hasRole("MANAGER")
.antMatchers("/api/signin","/api/signup").permitAll() // 기본 요청 언제나 접근 가능
.antMatchers(HttpMethod.GET, "/api/problemlist" , "/api/answer/**", "/api/comment/**", "/api/answerlist/**", "/api/rank/**", "/api/notice/**", "/api/admin/**").permitAll()
.antMatchers(HttpMethod.POST,"/api/comment/**").hasRole("REGULAR_STUDENT")
.antMatchers(HttpMethod.POST,"/api/notice/**").hasRole("MANAGER")
.antMatchers(HttpMethod.PATCH,"/api/notice/**").hasRole("MANAGER")
.anyRequest().hasRole("USER")
.and()
.sessionManagement()
Expand All @@ -60,38 +53,31 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://www.pes23.com", "https://pes23.com", "http://localhost:8080"));
configuration.setAllowedOrigins(Arrays.asList("https://www.pes23.com", "https://pes23.com")); // 여기에 IP 추가
configuration.setAllowedMethods(Arrays.asList("POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
configuration.setAllowCredentials(true);
configuration.setAllowCredentials(true); // 필요한 경우, 쿠키와 함께 요청을 보내는 것을 허용

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
source.registerCorsConfiguration("/**", configuration); // 모든 URL에 대해 설정 적용

return source;
}

@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername(adminUsername).password("{noop}" + adminPassword).roles("ADMIN").build());
return manager;
public SecurityExpressionHandler customWebSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
return defaultWebSecurityExpressionHandler;
}

@Bean
public PasswordEncoder passwordEncoder() {
return new NoOpPasswordEncoder();
}
}

class NoOpPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}

@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
//roleHierarchy.setHierarchy("ROLE_MANAGER > ROLE_REGULAR_STUDENT > ROLE_USER and ROLE_NEW_STUDENT > ROLE_USER");
String hierarchy = "ROLE_ADMIN > ROLE_MANAGER > ROLE_REGULAR_STUDENT > ROLE_USER\n" +
"ROLE_NEW_STUDENT > ROLE_USER";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
}
}
Loading

0 comments on commit 2fd0620

Please sign in to comment.