앞에 필터와 JWT를 사용하여 인증/인가를 구현해보았고 기본적인 개념들을 정리해보도록 하자.
또한 오늘 과제를 하면서 알게된 점을 정리해봅시다.
New Impo
@EntityGraph(attributePaths = "user")
{}가 없었지만, 강의에는 있는 것을 발견했다.{} 가 없어도 실행없는데는 지장이 없지만 여러 엔티티를 묶어야된다면, {"user, post"} 와 같이 사용할 수 있다는 것을 알게 되었다.yml파일을 통해 오류를 해결하였고 커밋을 하는데 아차 싶었다.
jwt의 시크릿 키라던가, DB의 비밀번호 등 많은 정보가 있는데 커밋을 해버렸다.
이걸 해결할 수 있는 방법이 .env 파일을 활용하는 것이었다.
순서
1. .env파일 프로젝트에 생성
2. gitIgnore에 추가
application-local.yml
.env
3. .env파일에 진짜 키값들을 작성
DB_URL=jdbc:mysql://url~
DB_USERNAME=이름이름
DB_PASSWORD=비밀번호
DB_DRIVER=com.mysql.**
JWT_SECRET_KEY=키값
4. 실제 yml파일에는 불러올 수 있도록 설정
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: ${DB_DRIVER}
jpa:
show-sql: true
hibernate:
ddl-auto: create-drop
properties:
hibernate:
format_sql: true
defer-datasource-initialization: true
jwt:
secret:
key: ${JWT_SECRET_KEY}
다만 스프링에서 .env에 있는 값을 바로 처리를 하지않아 필요한 플러그인을 추가해야된다거나 빌드에 추가해야된다는 등 불편한 과정이 있기는 하지만, 커밋할 시 yml파일에 기밀 정보가 담기지 않기 때문에 마음 놓고 커밋을 해도 괜찮다.
.env 파일과 gitIgnore 에 파일은 절대로 커밋해서는 아니된다.@PostMapping("/todos")
public ResponseEntity<?> save(@Auth AuthUser authUser, @RequestBody TodoRequest request)
Resolver 클래스 구현
어떤 파라미터를 지원하는지 정의 (supportsParameter)
파라미터 객체를 어떻게 만들어 반환할지 정의 (resolveArgument)
Spring MVC에 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final AuthUserArgumentResolver authUserArgumentResolver;
public WebConfig(AuthUserArgumentResolver authUserArgumentResolver) {
this.authUserArgumentResolver = authUserArgumentResolver;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(authUserArgumentResolver);
}
}
@Component 만 달면 동작하지 않는 이유스프링이 가진 기본 리졸버들 먼저 등록됨
이후 WebMvcConfigurer → addArgumentResolvers() 목록을 확인
여기에 추가된 리졸버만 실제로 동작
"이 클래스는 스프링 설정을 정의하는 클래스야."
WebMvcConfigurer의 내용을 스프링 MVC 구조에 실제로 적용하려면 스프링이 이 클래스를 설정으로 인식해야 하는데 그 역할을 @Configuration이 해준다.
인증 / 인가
인증 : “너 누구야?” 를 확인하는 과정
인가 : “너 이거 해도 돼?” 를 판단하는 과정
인증에 성공하면, 사용자 정보(Principal)가 보안 컨텍스트(SecurityContext)에 저장됨
Spring Security 에서의 인증 인가Spring Security
스프링 기반 애플리케이션의 인증(Authentication)과 인가(Authorization)를 담당하는 보안 프레임워크
특징
적용 요구사항
implementation 'org.springframework.boot:spring-boot-starter-security'
변화 내용


Spring Security는 세션 방식이 기본이지만, JWT기반 인증을 사용하면 로그인 이후 모든 요청을 커스텀 필터에서 처리하게 된다.
[사용자 요청]
↓
[JwtFilter (커스텀 필터)]
↓
[Security Filter Chain]
↓
[User 저장 및 인증 처리]
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable) // .csrf().disable() 방식은 더 이상 사용 안함.
.httpBasic(AbstractHttpConfigurer::disable) // BasicAuthenticationFilter 비활성화
.formLogin(AbstractHttpConfigurer::disable) // UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter 비활성화
.addFilterBefore(jwtFilter, SecurityContextHolderAwareRequestFilter.class)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login").permitAll()
.anyRequest().authenticated()
)
.build();
}
crsf : CSRF(Cross-Site Request Forgery) 보호를 비활성화
httpBasic : HTTP Basic 인증 방식을 비활성화
formLogin : 기본 로그인 폼과 관련된 필터를 비활성화
addFilterBefore : 필터의 순서를 지정해주는 역할
.addFilterBefore(jwtFilter, SecurityContextHolderAwareRequestFilter.class)
jwtFilter 뒤에 SecurityContextHolderAwareRequestFilter 를 위치
authorizeHttpRequests
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasRole("USER")
SecurityContextHolder
현재 쓰레드에서 인증된 사용자(Authentication 객체)를 저장/조회하는 Spring Security의 핵심 저장소
지금 로그인한 사용자가 누구인지"를 저장하는 곳
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.getContext().setAuthentication(authentication); //수동 저장
JwtFilter : JWT 토큰 존재/유효성 검사 → 사용자 인증 처리
JwtUtil : 토큰 발급, 파싱, 검증 유틸 클래스
SecurityContextHolder : 인증 결과 저장소 (스레드 단위)
Authentication : 인증 객체 (권한 포함)
UsernamePasswordAuthenticationToken : Spring Security에서 JWT 토큰 방식 사용하기 위함 객체
User : Spring Security에서 인증된 사용자 표현
[SecurityContext에 저장된 Authentication]
↓
[FilterSecurityInterceptor]
↓
[AccessDecisionManager]
↓
[권한 정보와 비교 → 접근 허용 or 거부]
1) URL 기반 설정 (권장: 관리 편의성)
http.authorizeHttpRequests()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().permitAll();
** 은 이 뒤에 무엇이 오든 상관이 없다 라는 뜻이다.2) 메서드 기반 어노테이션 (서비스 레이어 권한 제어 {컨트롤러 레이어도 가능함})
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) { ... }
지나가다 들렀는데 내용이 유익하네요 그리고 반존대 뭐죠