.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET,"/api/posts/**").permitAll()// 모두 허용.requestMatchers("/api/auth/**").permitAll()// 모두 허용.requestMatchers("/api/admin/**").hasRole("ADMIN")// ROLE_ADMIN만.requestMatchers("/api/user/**").hasAnyRole("USER","ADMIN")// 두 역할 모두.anyRequest().authenticated()// 나머지는 로그인 필요)
@Configuration@EnableMethodSecurity// 메서드 보안 활성화publicclassSecurityConfig{...}// 컨트롤러 또는 서비스에서 사용@PreAuthorize("hasRole('ADMIN')")@DeleteMapping("/api/admin/users/{id}")publicResponseEntity<Void>deleteUser(@PathVariableLong id){...}@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")publicvoidupdateUser(Long userId,UpdateRequest request){...}
// Role 설정 (내부적으로 "ROLE_ADMIN"으로 저장).hasRole("ADMIN")// Authority 설정 (정확한 이름 그대로 비교).hasAuthority("ROLE_ADMIN")
.exceptionHandling(ex -> ex
.authenticationEntryPoint((req, res, e)->{// 인증 실패 → 401 res.setStatus(401); res.setContentType("application/json"); objectMapper.writeValue(res.getWriter(),ApiResponse.error("UNAUTHORIZED","로그인이 필요합니다"));}).accessDeniedHandler((req, res, e)->{// 인가 실패 → 403 res.setStatus(403); res.setContentType("application/json"); objectMapper.writeValue(res.getWriter(),ApiResponse.error("FORBIDDEN","접근 권한이 없습니다"));}))