Spring Security (세션기반)

  1. 과정

    1. 로그인 요청(/login 폼 제출)

      1. 특징
        1. UsernamePasswordAuthenticationFilter가 ID/비밀번호 읽음
      2. 장점
        1. 설정만 하면 즉시 동작(폼 / 세션 기본 제공)
      3. 단점
        1. API 서버에서 폼, 세션은 클라이언트 다양성(웹, 앱 등)에 약함
      <!-- login.html -->
      
      <form method = "POST" action="/login">
      	<input type = "text" name = "username" placeholder = "아이디">
      	<input type="password" name="password" placeholder="비밀번호" />
       <button type="submit">로그인</button>
       </form>
      
      1. 사용자가 /login으로 요청하면 Spring Security 기본 필터가 가로챔
      2. UsernamePasswordAuthenticationFilter가 username/password 추출
    2. 인증 위임(AuthenticationManager)

      1. 특징
        1. UserDetailsService#loadUserByUsername 호출로 사용자 조회
      2. 장점
        1. 사용자 저장소 (DB/메모리/외부) 교체 용이
      3. 단점
        1. UserDetails 스펙을 맞춰야 해서 초심자에게 장벽
    3. 예시

      @Service
      publci class CustomUserDetailsService implements UserDetailsService{
      	@Override
      	public UserDetails loadUserByUsername(String username){
      		//DB에서 사용자 조회 예시
      		if("minsang".equals(username)){
      			return User.builder()
      				.username("minsang")
      				.password(passwordEncoder().encoder("1234"))
      				.roles("USER")
      				.build();
      		}
      		throw new UsernameNotFoundException("사용자 없음");
      	}
      	
      	@Bean
      	public PasswordEncoder passwordEncoder(){
      		return new BCryptPasswordEncoder();
      		}
      
      }
      
    4. 비밀번호 검증(PasswordEncoder 매칭)

      1. 특징
        1. BCrypt 같은 해시로 비교
      2. 장점
        1. 안전한 해시 전략 일원화
      3. 단점
        1. 잘못된 인코더 설정 시 전체 인증 실패
      boolean matches = passwordEncoder.matches(rawPassword, encodedPasswordFromDB);
      if(!matches) throw new BadCredentailsException("비밀번호 불일치")
      
    5. SecurityContext 채우기 + 세션 생성

      1. 특징
        1. 성공 시 SecurityContext에 Authentication 저장 → 세션에 붙음
      2. 장점
        1. 이후 요청에서 세션 ID 만으로 빠른 재인증
      3. 단점
        1. 서버 사이드 세션(스티키 세션/ 세션 공유) 스케일링 부담
      Authentication authResult = authenticationManger.authenticate(authRequest);
      
      //인증 성공하면 SecurityContext에 저장
      SecurityContext context = SecurityContextHolder.createEmptyContext();
      context.setAuthentication(authResult);
      
    6. 인가(Authorization)

      1. 특징
        1. @PreAuthorize,hasRole() 등으로 접근 제어
        2. 장점
          1. URL/메서드 단위의 세밀한 권한 정책
        3. 단점
          1. 정책이 퍼지면 추적이 어려움

            1. 서버가 쿠키 세션을 여러 개 관리해야 할 때, 일관성 유지가 어렵다.
            @RestController
            public class BoardController{
            @PreAUthorize(hasRole('ADMIN'))
            @GetMapping("/admin")
            public String adminPage(){...}
            
            @PreAUthroize("hasRole('USER')")
            @GetMapping("/user")
            public String userPage(){...}
            }
            
            1. URL마다, 메서드 마다 권한 정책이 붙어 있어서 어떤 사용자가 어떤 URL/메서드에 접근할 수 있는지 정책이 코드에 분산돼 있음
        4. 문제점
          1. 권한 정책이 여기저기 흩어져 있으면, 전체 권한 구조를 한 눈에 보기 어려움
        5. ex)
          1. 신규 개발자가 “”user가 접근 가능한 모든 API”를 확인하려고 하면 모든 컨트롤러 / 메서드를 찾아봐야 됨 비효울
          2. 정책 변경시 어디에 어나 메서드가 있는 지 확인해야 함
          3. “antMatchers(”/admin/**”)같은 정책이 여러 SecurityConfig에 흩어져 있으면 전체 권한 정책 추적이 힘듬”
      2. 로그아웃(세션 무효화)
        1. 특징
          1. 세션 삭제로 즉시 무효
        2. 장점
          1. 탈취 피해를 빠르게 차단 가능
        3. 단점
          1. 분산 환경에서 세션 동기화 / 공유 필요
      3. SecurityConfig(폼 로그인 + 인 메모리 유저)
      @Configuration
      @EnableWebSecurity
      public class SecurityConfig{
      	@Bean
      	SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
      		http
      				.csrf(csrf -> csrf.disable()) //REST면 disable, 폼이면 enable 고려
      				.authorizeHttpRequests(auth -> auth
      					.requestMatchers("/login", "/public/**").permitAll()
      					.anyRequest().authenticated()) // 나머지 모든 요청은 인증된 사용자만 접근 가능
      					.formLogin(form -> form
      					.loginPage("/login")
      					.defaultSuccessUrl("/me",true)
      					.permitAll()).logout(logout -> logout.logoutUrl("/logout"));
      					return http.build();
      					
      	}
      	
      	@Bean 
      	public UserDetailsService userDetailsService(PasswordEncoder encoder){
      		UserDetails user = User.withUsername("test")
      						.password(encoder.encode("1234"))
      						.roles("USER")
      						.build();
      						
      				return new InMemoryUserDetailsManager(user)
      	}
      	@Bean
      	PasswordEncoder passwordEncoder(){
      		return new BCryptPasswordEncoder();
      	}
      }
      
  2. 회원가입(signUp)

    1. User Entity
    @Entity
    public class User {
    	@Id
    	@GeneratedValue
    	private Long id;
    	private String username;
    	private String password;
    	private String role;
    	
    	public User(String username, String password, String role){
    	this.username = username;
    	this.password = password;
    	this.role = role;
    	}
    }
    
  3. 과정 특징

    1. 서버 중심, DB 테이블과 매핑
  4. 장점

    1. ORM 사용 가능, 객체 중심 관리
  5. 단점

    1. DB 구조 변경 시 코드 수정 필요
  6. AuthController - 회원가입

    @PostMapping("/auth/signUp")
    public String singUp(@RequestBody SignUpRequest req){
    	User user= new User(req.username(), passwordEncoder.encode(req.password()),
    	userRepository.save(user);
    	return "회원가입 완료";
    
    }
    
    1. 메서드 특징
    2. 장점
    3. 단점

Bean (회원가입 관련)

쿠키

@PreAuthorize(”hasRole(’ROLE_USER’)”)

SpEL

CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)

@GeneratedValue

@Component

Autowired

Collection<? extends GrantedAuthority> GrantedAuthority의 자식 타입을 담을 수 있다는 의미 (제네릭 와일드 카드)

PasswordEncoder