Spring
Spring Boot
Spring Security

Spring Security

Spring Security는 스프링 기반의 어플리케이션에서의 인증(Authentication)과 권한부여(Authorization)을 구현해둔 보안 프레임워크. 기본적인 CSRF, SQL Injection 등의 공격이 막혀있다.
스스로 구현하는 Interceptor나 Resolver 등이 기본적으로 Interface나 abstract 형태로 구현해있어 그걸 extends해서 쓰는 형태이며, 기본적으론 전부 막혀있다고 보면 된다.
기본적으로는 Session 기반으로 되어 있다.

주요기능

  1. 인증 : 로그인 기능 제공
  2. 권한부여 : 특정 리소스에 대한 접근 권한 부여
  3. 세션관리 : 사용자의 세션 관리 및 세션 유지 만료 시간을 설정
  4. 보안설정 : 보안 관련 구성을 통하여, URL 또는 리소스에 대한 보안 설정
  5. 보안이벤트처리 : 인증 및 권한 에러에 대한 이벤트 핸들링을 제공

Config

/config/Security/SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
  private List<String> SWAGGER = List.of("/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/v2/api-docs", "/v3/api-docs/**");
 
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
        // Default는 활성화
        .csrf()
        .disable()
        .authorizeHttpRequests(it -> {
          // static Resource에 대해서는 모두 허가함
          it.requestMatchers(
                  PathRequest.toStaticResources()
                      .atCommonLocations()
              )
              .permitAll()
              // swagger는 인증 없이 통과
              .mvcMatchers(SWAGGER.toArray(new String[0]))
              .permitAll()
              // open-api 예외
              .mvcMatchers("/open-api/**")
              .permitAll()
              // 그 외 모든 요청은 인증을 받음
              .anyRequest()
              .authenticated();
        })
        .formLogin(Customizer.withDefaults());
    return httpSecurity.build();
  }
}

PasswordEncoder

Spring Security는 기본적으로 BCryptPasswordEncoder 방식을 사용하며, encoding해서 대조는 가능하지만 decoding해서 애초에 입력한 비밀번호를 알 수 없다.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
  ... other code 
 
  @Bean
  public PasswordEncoder passwordEncoder() {
    // 해쉬 방식으로 암호화
    return new BCryptPasswordEncoder();
  }
}

Service에서 PasswordEncoder 사용

PasswordEncoder를 Bean으로 등록해줬기 때문에 SecurityConfig이 아니라 PasswordConfig으로 Bean을 주입받아 사용 가능하다.

@RequiredArgsConstructor
@Service
public class StoreUserService {
 
    private final StoreUserRepository storeUserRepository;
    private final PasswordEncoder passwordEncoder;
 
    public StoreUserEntity register(
        StoreUserEntity storeUserEntity
    ){
        storeUserEntity.setStatus(StoreUserStatus.REGISTERED);
        storeUserEntity.setPassword(passwordEncoder.encode(storeUserEntity.getPassword()));
        storeUserEntity.setRegisteredAt(LocalDateTime.now());
        return storeUserRepository.save(storeUserEntity);
    }
 
    ... other code
}

How To Use

StoreUser의 me api 같은 곳에서 AuthenticationPrincipal을 통해서 UserSession을 주입받아 사용할 수 있다.

    @GetMapping("/me")
    public StoreUserResponse me(
        @Parameter(hidden = true)
        @AuthenticationPrincipal UserSession userSession
    ){
        return storeUserConverter.toResponse(userSession);
    }

OAuth2 JWT

동작 원리