인증 / 인가 처리
현재 프로젝트 인증 방식
현재 이 프로젝트는 공개 접근(Public Access) 방식으로 운영된다.
- 일반 사용자 페이지: 누구나 접근 가능
- 어드민 페이지 (
/admin/**): 별도 인증 없이 직접 URL 접근 가능 (내부망 운영 또는 차후 도입 예정) - 관리자 기능에 인증 도입 시 이 문서를 갱신한다.
Spring Security 설정 (향후 도입 시 참고)
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/news/**", "/guide/**", "/health").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/api/guide/search").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/login")
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
)
.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/**") // REST API는 CSRF 제외
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // strength 12 (최소)
}
}
세션 타임아웃 설정
# application.yml
server:
servlet:
session:
timeout: 30m # 세션 타임아웃 30분
인증 실패 로깅
@Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private static final Logger log = LoggerFactory.getLogger(AuthFailureHandler.class);
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException {
String username = request.getParameter("username");
// 개인정보(비밀번호, 이메일 전체 등) 로그 출력 금지
log.warn("로그인 실패: userId={}", username);
super.onAuthenticationFailure(request, response, exception);
}
}
권한 체크 (메서드 레벨)
@Service
public class PostService {
// 메서드 레벨 권한 체크
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public void delPost(Long postId, Long userId) {
// ...
}
}
CSRF 토큰 (Thymeleaf)
<!-- POST 폼에 CSRF 토큰 필수 -->
<form th:action="@{/submit}" method="post">
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
</form>
REST API 클라이언트 (JavaScript):
// CSRF 토큰을 헤더로 전달
const csrfToken = document.querySelector('meta[name="_csrf"]').content;
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content;
fetch('/api/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
[csrfHeader]: csrfToken
},
body: JSON.stringify(data)
});
JWT 도입 시 고려사항
향후 JWT 기반 인증을 도입할 때 참고:
spring-boot-starter-security+jjwt의존성 추가JwtAuthenticationFilter를SecurityFilterChain앞에 추가- CSRF 비활성화 (Stateless 방식에서는 불필요)
- JWT 시크릿 키는
JWT_SECRET환경변수로 관리 - 액세스 토큰 만료: 15분, 리프레시 토큰 만료: 7일
- 리프레시 토큰은 DB에 저장 (탈취 시 무효화 가능)
- 로그에 토큰 값 출력 절대 금지
보안 체크리스트
- [ ] 인증 없이 접근 가능한 엔드포인트 최소화
- [ ] 다른 사용자 리소스 접근 불가 (수평적 권한 상승 방지)
- [ ] 관리자 기능(
/admin/**) 권한 검증 - [ ] 비밀번호 BCrypt 암호화 (최소 12자)
- [ ] 세션 타임아웃 30분 설정
- [ ] CSRF 토큰 적용 (POST 폼, REST API 헤더)
- [ ] 인증/인가 실패 로그 기록
- [ ] 로그에 개인정보/비밀번호 미포함