diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/common/controller/HealthCheckController.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/common/controller/HealthCheckController.java index 801b4aa3..b9581a4a 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/common/controller/HealthCheckController.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/common/controller/HealthCheckController.java @@ -14,9 +14,10 @@ public class HealthCheckController { private final Environment env; - @GetMapping("/health") + @GetMapping("/api/health") public String healthCheck() { - return "ok" + Arrays.toString(env.getActiveProfiles()); + String hostname = env.getProperty("HOSTNAME"); + return "ok:" + Arrays.toString(env.getActiveProfiles())+":"+hostname; } } diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/JwtProvider.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/JwtProvider.java index a3d8ddfa..ad697a69 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/JwtProvider.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/JwtProvider.java @@ -10,7 +10,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import java.util.Set; @@ -19,6 +18,7 @@ @RequiredArgsConstructor public class JwtProvider implements AuthenticationProvider { private final JwtUtils jwtUtils; + private static final String ROLE_PREFIX = "ROLE_"; /** * JwtAuthenticationToken을 받아서 인증을 진행 @@ -31,14 +31,17 @@ public Authentication authenticate(Authentication authentication) throws Authent String token = (String) authentication.getCredentials(); // 토큰을 검증하는 단계 - if(!jwtUtils.validateToken(token)){ + if (!jwtUtils.validateToken(token)) { throw new AuthenticationServiceException("유효하지 않은 토큰입니다."); } JwtUser jwtUser = jwtUtils.getJwtUser(JwtToken.ValidToken.of(token)); - Set authorities = Set.of(new SimpleGrantedAuthority(jwtUser.getRole().name())); // 검증 후 인증정보 Authentication 객체를 반환 - return new UsernamePasswordAuthenticationToken(jwtUser, null, authorities); + return new UsernamePasswordAuthenticationToken(jwtUser, null, getAuthorities(jwtUser)); + } + + private Set getAuthorities(JwtUser jwtUser) { + return Set.of(new SimpleGrantedAuthority(ROLE_PREFIX + jwtUser.getRole().name())); } /** diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java index 197dd329..4bea6765 100644 --- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java +++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java @@ -7,7 +7,12 @@ import org.haedal.zzansuni.global.exception.UnauthorizedException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @@ -19,6 +24,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @@ -65,13 +71,21 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { * anyRequest() 메서드를 통해 나머지 요청에 대한 인증 설정을 한다. * authenticated() 메서드를 통해 인증된 사용자만 접근 가능하도록 설정한다. */ - http.authorizeHttpRequests((authorize) -> - authorize - .requestMatchers( - "/api/auth/**", - "/swagger-ui/**" - ).permitAll() - .anyRequest().authenticated() + http.authorizeHttpRequests((authorize) -> authorize + .requestMatchers( + "/api/auth/**", + "/swagger-ui/**", + "/api/health" + ).permitAll() + .requestMatchers( + HttpMethod.GET, + "/api/challengeGroups/**", + "/api/users/ranking" + ) + .permitAll() // 해당 PATH의 GET 요청은 인증 없이 접근 가능 + .requestMatchers("/api/admin/auth/manager").hasRole("ADMIN") // ADMIN 권한만 접근 가능 + .requestMatchers("/api/admin/**").hasRole("MANAGER") // MANAGER, ADMIN 권한만 접근 가능 + .anyRequest().authenticated() // 나머지 요청은 인증된 사용자만 접근 가능 ); /** @@ -82,10 +96,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { */ http.exceptionHandling((exception) -> exception .authenticationEntryPoint((request, response, authException) -> - responseError(response, "UNAUTHORIZED", "인증이 필요합니다.",401) + responseError(response, "UNAUTHORIZED", "인증이 필요합니다.", 401) ) .accessDeniedHandler((request, response, accessDeniedException) -> - responseError(response, "ACCESS_DENIED", "권한이 없습니다.",403) + responseError(response, "ACCESS_DENIED", "권한이 없습니다.", 403) ) ); @@ -120,12 +134,12 @@ private void responseError( } @Bean - public AuthenticationManager authenticationManager(){ + public AuthenticationManager authenticationManager() { return new ProviderManager(List.of(jwtProvider)); } @Bean - public AuthorizationJwtHeaderFilter jwtAuthenticationFilter(AuthenticationManager authenticationManager){ + public AuthorizationJwtHeaderFilter jwtAuthenticationFilter(AuthenticationManager authenticationManager) { return new AuthorizationJwtHeaderFilter(authenticationManager); } @@ -149,4 +163,24 @@ public CorsConfigurationSource corsConfigSource() { return source; } + + /** + * RoleHierarchy + * ADMIN > MANAGER > USER + */ + @Bean + public RoleHierarchy roleHierarchy() { + return RoleHierarchyImpl.withDefaultRolePrefix() + .role("ADMIN").implies("MANAGER") + .role("MANAGER").implies("USER") + .build(); + + } + + @Bean + public MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) { + DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + expressionHandler.setRoleHierarchy(roleHierarchy); + return expressionHandler; + } }