Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import dev.codehouse.backend.global.response.ApiResponse;
import dev.codehouse.backend.global.response.ApiResponseFactory;
import dev.codehouse.backend.global.response.ResponseCode;
import dev.codehouse.backend.user.dto.UserResponse;
import dev.codehouse.backend.user.service.UserFindService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -21,6 +23,7 @@ public class AdminController {
private final AdminNoticeService noticeService;
private final AdminPointService pointService;
private final AdminProblemService problemService;
private final UserFindService userFindService;

@GetMapping("/notice")
public ResponseEntity<ApiResponse<NoticeResponse>> getNotice() {
Expand All @@ -38,17 +41,17 @@ public ResponseEntity<ApiResponse<UserPointResponse>> adjustPoint(@RequestBody U
return ApiResponseFactory.success(ResponseCode.USER_POINT_UPDATED, pointService.adjustUserPoint(dto.getUsername(), dto.getDelta()));
}

@GetMapping("/user")
public ResponseEntity<ApiResponse<List<UserResponse>>> getAllUsers() {
return ApiResponseFactory.success(ResponseCode.USER_FOUND, userFindService.getAllUsers());
}

@PostMapping("/problem")
public ResponseEntity<ApiResponse<Void>> register(@RequestBody AdminProblemRequest request) {
problemService.saveProblem(request);
return ApiResponseFactory.success(ResponseCode.PROBLEM_REGISTERED);
}

@GetMapping("/problem/{day}")
public ResponseEntity<ApiResponse<List<AdminProblemResponse>>> getByDay(@PathVariable String day) {
return ApiResponseFactory.success(ResponseCode.PROBLEM_FOUND, problemService.getProblems(day));
}

@DeleteMapping("/problem/{number}")
public ResponseEntity<ApiResponse<Void>> deleteProblem(@PathVariable String number) {
problemService.deleteProblem(number);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.codehouse.backend.global.config;

import dev.codehouse.backend.global.exception.CustomAccessDeniedHandler;
import dev.codehouse.backend.global.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -24,6 +25,7 @@ public class SecurityConfig {
@Value("${cors.allowed-origins}")
private String allowedOrigins;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomAccessDeniedHandler customAccessDeniedHandler; // 권한 예외 처리 핸들러

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
Expand All @@ -32,6 +34,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/admin/notice").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers(
"/api/auth/**",
"/api/user/ranking/**",
Expand All @@ -40,6 +43,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
).permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(exception -> exception
.accessDeniedHandler(customAccessDeniedHandler))
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.codehouse.backend.global.exception;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_FORBIDDEN); // 403 Forbidden
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"message\": \"관리자 권한이 필요합니다.\"}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum ResponseCode {
DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "데이터베이스 오류가 발생했습니다"),
EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "외부 API 호출 중 오류가 발생했습니다."),
PROBLEM_ALREADY_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR, "이미 존재하는 문제입니다"),
USER_ALREADY_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR, "이미 존재하는 문제입니다"),

//200 OK
USER_LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;

@Slf4j
@Component
Expand Down Expand Up @@ -54,8 +57,12 @@ protected void doFilterInternal( HttpServletRequest request, HttpServletResponse

private void setupAuthentication(HttpServletRequest request, String token) {
String username = jwtUtil.extractUsername(token);
String role = jwtUtil.extractRole(token);

GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + role);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(username, null, null);
new UsernamePasswordAuthenticationToken(username, null, Collections.singletonList(authority));

authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/dev/codehouse/backend/global/util/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,22 @@ public void init() {
key = Keys.hmacShaKeyFor(secret.getBytes());
}

private JwtBuilder jwtBuilder(String username, long expirationMills) {
private JwtBuilder jwtBuilder(String username, String role, long expirationMills) {
long now = System.currentTimeMillis();
return Jwts.builder()
.setSubject(username)
.claim("role", role)
.setIssuedAt(new Date(now))
.setExpiration(new Date(now + expirationMills))
.signWith(key);
}

public String generateAccessToken(String username) {
return jwtBuilder(username, accessExpiration).compact();
public String generateAccessToken(String username, String role) {
return jwtBuilder(username, role, accessExpiration).compact();
}

public String generateRefreshToken(String username) {
return jwtBuilder(username, refreshExpiration).compact();
public String generateRefreshToken(String username, String role) {
return jwtBuilder(username, role, refreshExpiration).compact();
}

private io.jsonwebtoken.JwtParser createJwtParser() {
Expand Down Expand Up @@ -71,4 +72,10 @@ public String extractUsername(String token) {
.getSubject();
}

public String extractRole(String token) {
return createJwtParser()
.parseClaimsJws(token)
.getBody()
.get("role", String.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import dev.codehouse.backend.global.response.ApiResponse;
import dev.codehouse.backend.global.response.ApiResponseFactory;
import dev.codehouse.backend.global.response.ResponseCode;
import dev.codehouse.backend.user.domain.User;
import dev.codehouse.backend.user.dto.UserRequestDto;
import dev.codehouse.backend.user.dto.UserRequest;
import dev.codehouse.backend.user.service.AuthService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -21,7 +20,7 @@ public class AuthController {
private final AuthService authService;

@PostMapping("/register")
public ResponseEntity<ApiResponse<Void>> register(@RequestBody UserRequestDto request) {
public ResponseEntity<ApiResponse<Void>> register(@RequestBody UserRequest request) {
authService.register(request);
return ApiResponseFactory.success(ResponseCode.USER_REGISTER_SUCCESS);
}
Expand Down Expand Up @@ -61,7 +60,7 @@ public ResponseEntity<ApiResponse<Void>> register(@RequestBody UserRequestDto re
// }

@PostMapping("/confirm")
public ResponseEntity<ApiResponse<Void>> confirm(@RequestBody UserRequestDto request) {
public ResponseEntity<ApiResponse<Void>> confirm(@RequestBody UserRequest request) {
authService.userExists(request.getUsername(), request.getClasses());
return ApiResponseFactory.success(ResponseCode.USER_CONFIRM_SUCCESS);
}
Expand All @@ -83,7 +82,7 @@ public ResponseEntity<ApiResponse<Void>> confirm(@RequestBody UserRequestDto req
// return ApiResponseFactory.success(ResponseCode.USER_LOGIN_SUCCESS, tokens);
// }
@PostMapping("/login")
public ResponseEntity<ApiResponse<Map<String, String>>> login(@RequestBody UserRequestDto request) {
public ResponseEntity<ApiResponse<Map<String, String>>> login(@RequestBody UserRequest request) {
Map<String, String> tokens = authService.login(request);
return ApiResponseFactory.success(ResponseCode.USER_LOGIN_SUCCESS,tokens);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import dev.codehouse.backend.global.response.ResponseCode;
import dev.codehouse.backend.user.domain.User;
import dev.codehouse.backend.user.domain.UserHistory;
import dev.codehouse.backend.user.dto.RankingResponseDto;
import dev.codehouse.backend.user.dto.UserResponseDto;
import dev.codehouse.backend.user.dto.RankingResponse;
import dev.codehouse.backend.user.dto.UserResponse;
import dev.codehouse.backend.user.repository.UserRepository;
import dev.codehouse.backend.user.service.UserFindService;
import dev.codehouse.backend.user.service.UserHistoryService;
Expand All @@ -28,36 +28,27 @@
public class UserController {

private final UserFindService userFindService;
private final UserRepository userRepository;
private final UserRankingService userRankingService;
private final UserHistoryService userHistoryService;

@GetMapping("/{username}")
public ResponseEntity<ApiResponse<UserResponseDto>> getUser(@PathVariable String username) {
return ApiResponseFactory.success(ResponseCode.USER_FOUND, userFindService.getUser(username));
@GetMapping("/me")
public ResponseEntity<ApiResponse<UserResponse>> getUser(Authentication authentication) {
return ApiResponseFactory.success(ResponseCode.USER_FOUND, userFindService.getUser(authentication.getName()));
}

@GetMapping("")
public ResponseEntity<ApiResponse<List<UserResponseDto>>> getAllUsers() {
public ResponseEntity<ApiResponse<List<UserResponse>>> getAllUsers() {
return ApiResponseFactory.success(ResponseCode.USER_FOUND, userFindService.getAllUsers());
}


@GetMapping("/{username}/point")
public Map<String, Integer> getPoint(@PathVariable String username) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("존재하지 않는 사용자입니다."));
return Map.of("point", user.getPoint());
}

@GetMapping("/ranking")
public ResponseEntity<ApiResponse<List<RankingResponseDto>>> getTopRanking(){
public ResponseEntity<ApiResponse<List<RankingResponse>>> getTopRanking(){
return ApiResponseFactory.success(ResponseCode.RANK_FOUND,userRankingService.getTopRanking());
}

@GetMapping("/ranking/{className}")
public ResponseEntity<ApiResponse<List<RankingResponseDto>>> getClassRanking(@PathVariable String className){
return ApiResponseFactory.success(ResponseCode.RANK_FOUND,userRankingService.getclassRanking(className));
public ResponseEntity<ApiResponse<List<RankingResponse>>> getClassRanking(@PathVariable String className){
return ApiResponseFactory.success(ResponseCode.RANK_FOUND,userRankingService.getClassRanking(className));
}

@GetMapping("/history")
Expand Down

This file was deleted.

14 changes: 0 additions & 14 deletions src/main/java/dev/codehouse/backend/user/dto/HistoryRequest.java

This file was deleted.

32 changes: 32 additions & 0 deletions src/main/java/dev/codehouse/backend/user/dto/RankingResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.codehouse.backend.user.dto;


import dev.codehouse.backend.user.domain.User;
import lombok.*;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RankingResponse {
private String username;
private int point;
private int rank;
private String classes;

public static List<RankingResponse> from(List<User> users) {
AtomicInteger rank = new AtomicInteger(1);

return users.stream()
.map(user -> RankingResponse.builder()
.username(user.getUsername())
.point(user.getPoint())
.rank(rank.getAndIncrement())
.classes(user.getClasses())
.build())
.toList();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@Getter
@Setter
public class UserRequestDto {
public class UserRequest {
private String username;
private String password;
private String classes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

@Getter
@AllArgsConstructor
public class UserResponseDto {
public class UserResponse {
private String username;
private String classes;
private int point;

public static UserResponseDto from(User user) {
return new UserResponseDto(
public static UserResponse from(User user) {
return new UserResponse(
user.getUsername(),
user.getClasses(),
user.getPoint()
Expand Down
Loading