11.29 TIL - Spring Security

이서준·2025년 11월 29일

SpringSecurity

목록 보기
1/4

Spring Security

  • 일반적인 공격에 대한 인증, 권한 부여 및 보호 기능을 제공하는 프레임 워크
  • 인증과 인가에 대한 부분을 Filter 흐름에 따라 처리

용어

구분설명
Authentication (인증)사용자가 누구인지 확인하는 절차 (로그인 과정)
Authorization (인가)인증된 사용자가 특정 자원에 접근할 수 있는지 판단하는 과정
Principal (접근 주제)인증된 사용자 정보(주로 username)
Credential (비밀번호)사용자 비밀번호
UserDetails사용자 정보를 담는 객체 (Spring Security 표준 인터페이스)
UserDetailsService사용자 정보를 DB에서 조회하는 서비스 (loadUserByUsername)
AuthenticationProvider실제 인증 로직을 수행하는 컴포넌트
  • 인증 절차를 거친 후에 인가 절차를 진행, 인가 과정에서 해당 리소스에 대한 접근 권한이 있는지 확인
  • 인증과 인가를 위해 Principal을 아이디로, Credential을 비밀번호로 사용

구조

  1. 클라이언트 요청이 SecurityFilterChain에 진입한
  2. AuthenticationFilter(예: UsernamePasswordAuthenticationFilter)가 요청에서 id/password를 추출
  3. 이 값을 바탕으로 Authentication 객체를 생성AuthenticationManager 에 전달
  4. AuthenticationManager는 내부에 등록된 여러 AuthenticationProvider 중 적절한 Provider를 선택
  5. Provider는 UserDetailsService를 통해 DB에서 사용자 정보를 조회
  6. 조회된 UserDetails와 입력받은 Authentication 객체를 비교해 인증을 수행
  7. 인증에 성공하면 Authenticated Authentication 객체를 반환
  8. SecurityContextHolder가 이 인증 객체를 SecurityContext에 저장
  9. 이후 요청에서는 SecurityContext에 저장된 인증 정보를 기반으로 인가(Authorization)가 처리

주요 모듈

1. Filter

  • 요청에서 인증 정보를 추출
  • Authentication 객체 생성 후 AuthenticationManager에게 위임
  • 성공 → SecurityContext 저장 후 SuccessHandler 호출
  • 실패 → FailureHandler 호출

2. Authentication

  • 인증 정보 + 권한(Authorities)을 담는 객체
  • SecurityContextHolder → SecurityContext → Authentication 순으로 접근

3. UsernamePasswordAuthenticationToken

  • 사용자 요청 정보(로그인 시) 또는 인증 완료 정보(인증 후)를 담는 Token 객체
  • principal = 사용자명, credentials = 비밀번호

4. AuthenticationProvider

  • 로그인 정보 ↔ DB 정보 비교
  • 인증 성공하면 인증된 Authentication 반환
  • AuthenticationManager가 여러 Provider를 조합함

5. UserDetailsService

  • DB에서 유저 정보를 가져오는 역할(loadUserByUsername(String))

6. UserDetails

  • 사용자의 정보를 담는 인터페이스, 직접 받아 사용.
  • 인증된 토큰을 생성하기 위해 사용.

변경 메서드

과거 (Boot 2)Spring Boot 3 / 4비고
authorizeRequests()authorizeHttpRequests()완전 확정된 API
antMatchers()requestMatchers()경로 매칭 통합
access("hasAnyRole('ROLE_A')")hasAnyRole("A")ROLE_ prefix 자동 처리

requestMatchers(String)

자동 분별 기능 제거 사용법

Spring MVC 경로일 경우

requestMatchers(new MvcRequestMatcher(introspector, "/test1"))

단순 URL

requestMatchers(new AntPathRequestMatcher("/images/**"))

requestMatchers 정리

상황사용
Spring MVC Controller URLnew MvcRequestMatcher(introspector, "/url")
정적 파일, 단순 경로new AntPathRequestMatcher("/images/**")
CORS pre-flightCorsUtils::isPreFlightRequest
  • requestMatchers(String)의 자동 구분 기능이 사라짐
    • MVC: MvcRequestMatcher
    • static/non-MVC: AntPathRequestMatcher
  • SecurityContextHolder 전략 변경
    • 자식 스레드까지 인증 정보 전파
  • WebSecurityConfigurerAdapter 완전 제거
  • 모든 설정 메서드가 람다 기반 DSL로 통일됨

적용 예시

Config

@Configuration
public class SpringSecurityConfig {
 
	//password 인코더 
	@Bean
	PasswordEncoder passwordencoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.csrf(AbstractHttpConfigurer::disable)
		.authorizeHttpRequests((request) -> request
		.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
		.requestMatchers("/status", "/images/**", "/view/join", "/auth/join").permitAll()
		.requestMatchers("view/setting/admin").hasRole("ADMIN")
		.requestMatchers("view/setting/user").hasRole("USER")
		.anyRequest().authenticated()	// 어떠한 요청이라도 인증필요
		)
		.formLogin((login) -> login		// form 방식 로그인 사용
		.loginPage("/view/login") 		//만든 로그인 페이지
		.loginProcessingUrl("/login-process") 	//로그인중 url
		.usernameParameter("userid")		//submit할 아이디
		.passwordParameter("pw")			//submit할 비밀번호
		.defaultSuccessUrl("/view/dashboard", true)	//성공 시 dashboard로
		.permitAll()	// 대시보드 이동이 막히면 안되므로 허용
		)
		.logout(Customizer.withDefaults());		// 로그아웃은 기본설정으로 (/logout으로 인증해제)
 
		http.logout(logout -> logout.logoutUrl("/logout"));
		return http.build();
	}
}

UserDetailsService

@Component
public class MyUserDetailsService implements UserDetailsService {
 
	private final MemberService memberService;
	
	public MyUserDetailsService(MemberService memberService) {
		this.memberService = memberService;
	}
	
	@Override
	public UserDetails loadUserByUsername(String insertedUserId)
    									throws UsernameNotFoundException {
	//db에서 유저를 찾아서 없으면 exception
		Optional<Member> findOne = memberService.findOne(insertedUserId);
		Member member = findOne.orElseThrow(
        () -> new UsernameNotFoundException("없는 회원입니다 ㅠ왕왕왕왕"));
		
        return User.builder()
                .username(member.getUserid())
                .password(member.getPw())
                .roles(member.getRoles())
                .build();
	}
}

Controller 에서 로그인 정보를 받기

@GetMapping("/dashboard")
public String dashboardPage(@AuthenticationPrincipal User user, Model model) {
	model.addAttribute("loginId", user.getUsername());
	model.addAttribute("loginRoles", user.getAuthorities());
	return "dashboard";
}
profile
Allons-y

0개의 댓글