Skip to content

Commit e967cea

Browse files
authored
[refactor] 사용자 권한에 따른 보안 정책 분리
1 parent da3ac3f commit e967cea

4 files changed

Lines changed: 34 additions & 23 deletions

File tree

backend/src/main/java/com/back/global/config/SecurityConfig.java

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.back.global.config;
22

3+
import java.io.IOException;
4+
35
import org.springframework.beans.factory.annotation.Value;
46
import org.springframework.context.annotation.Bean;
57
import org.springframework.context.annotation.Configuration;
@@ -14,8 +16,12 @@
1416
import org.springframework.web.cors.CorsConfiguration;
1517
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
1618

19+
import com.back.global.error.code.AuthErrorCode;
20+
import com.back.global.error.code.ErrorCode;
1721
import com.back.global.properties.CorsProperties;
22+
import com.back.global.response.ApiResponse;
1823
import com.back.global.security.CustomAuthenticationFilter;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
1925

2026
import jakarta.servlet.http.HttpServletResponse;
2127
import lombok.RequiredArgsConstructor;
@@ -28,6 +34,7 @@ public class SecurityConfig {
2834

2935
private final CorsProperties corsProperties;
3036
private final CustomAuthenticationFilter authenticationFilter;
37+
private final ObjectMapper objectMapper;
3138

3239
@Bean
3340
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@@ -40,8 +47,12 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
4047
.requestMatchers("/h2-console/**").permitAll() // H2 콘솔 접근 허용
4148
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() // Swagger 접근 허용
4249
.requestMatchers("/.well-known/**").permitAll()
43-
//.requestMatchers("/api/v1/admin/**").hasRole("ADMIN") //추후 주석 해제
50+
.requestMatchers("/api/v1/auth/signup").permitAll()
51+
.requestMatchers("/api/v1/auth/login").permitAll()
52+
.requestMatchers("/api/v1/admin/auth/**").permitAll()
53+
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
4454
.requestMatchers("/actuator/**").permitAll() // 모니터링/Actuator 관련
55+
// .requestMatchers("/api/v1/**").authenticated() // TODO: 개발 후 인증 활성화
4556
.anyRequest().permitAll() // TODO: 보안 인증 설정 시 제거, 현재는 모든 API 요청을 인증없이 허용
4657
)
4758
.csrf(csrf -> csrf
@@ -56,27 +67,11 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
5667
//401 403 커스텀 에러
5768
.exceptionHandling(exceptionHandling -> exceptionHandling
5869
.authenticationEntryPoint((request, response, authException) -> {
59-
response.setContentType("application/json; charset=UTF-8");
60-
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
61-
response.getWriter().write("""
62-
{
63-
"status": "UNAUTHORIZED",
64-
"message": "로그인 후 이용해주세요.",
65-
"data": null
66-
}
67-
""");
70+
writeError(response, AuthErrorCode.UNAUTHORIZED);
6871
})
6972

7073
.accessDeniedHandler((request, response, accessDeniedException) -> {
71-
response.setContentType("application/json; charset=UTF-8");
72-
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
73-
response.getWriter().write("""
74-
{
75-
"status": "FORBIDDEN",
76-
"message": "접근 권한이 없습니다.",
77-
"data": null
78-
}
79-
""");
74+
writeError(response, AuthErrorCode.FORBIDDEN);
8075
})
8176
);
8277

@@ -105,4 +100,12 @@ public PasswordEncoder passwordEncoder(
105100
) {
106101
return new BCryptPasswordEncoder(strength);
107102
}
103+
104+
private void writeError(HttpServletResponse response, ErrorCode code) throws IOException {
105+
response.setStatus(code.getHttpStatus().value());
106+
response.setContentType("application/json; charset=UTF-8");
107+
108+
ApiResponse<?> body = ApiResponse.fail(code);
109+
response.getWriter().write(objectMapper.writeValueAsString(body));
110+
}
108111
}

backend/src/main/java/com/back/global/error/code/AuthErrorCode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public enum AuthErrorCode implements ErrorCode {
1313

1414
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "로그인 후 이용해주세요."),
1515

16+
FORBIDDEN(HttpStatus.FORBIDDEN, "접근 권한이 없습니다."),
17+
ADMIN_ONLY(HttpStatus.FORBIDDEN, "관리자 계정만 접근 가능합니다."),
18+
1619
TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "토큰이 만료되었습니다."),
1720
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."),
1821
REFRESH_TOKEN_REQUIRED(HttpStatus.BAD_REQUEST, "리프레시 토큰이 없습니다."),

backend/src/main/java/com/back/global/security/CustomAuthenticationFilter.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
@Component
3232
@RequiredArgsConstructor
3333
public class CustomAuthenticationFilter extends OncePerRequestFilter {
34-
35-
private static final String AUTH_PATH_PREFIX = "/api/v1/auth/";
3634
private static final String BEARER_PREFIX = "Bearer ";
3735

3836
private static final Set<String> AUTH_WHITELIST = Set.of(
@@ -93,7 +91,14 @@ private void authenticate(
9391

9492
long userId = ((Number)payload.get("id")).longValue();
9593
String nickname = (String)payload.getOrDefault("nickname", "");
96-
UserRole role = (UserRole)payload.getOrDefault("role", UserRole.NORMAL);
94+
Object roleObj = payload.get("role");
95+
UserRole role = UserRole.NORMAL;
96+
97+
if (roleObj instanceof String roleStr) {
98+
role = UserRole.valueOf(roleStr);
99+
} else if (roleObj instanceof UserRole userRole) {
100+
role = userRole;
101+
}
97102

98103
SecurityUser securityUser = new SecurityUser(
99104
userId,

backend/src/main/java/com/back/global/security/JwtProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private Map<String, Object> createBaseClaims(User user) {
6060
Map<String, Object> claims = new HashMap<>();
6161
claims.put(CLAIM_ID, user.getId());
6262
claims.put(CLAIM_NICKNAME, user.getNickname());
63-
claims.put(CLAIM_ROLE, user.getRole());
63+
claims.put(CLAIM_ROLE, user.getRole().name());
6464
return claims;
6565
}
6666

0 commit comments

Comments
 (0)