망할. 예전 코드가 또 deprecated 됐다.
스프링 부트가 버전 업 하면서 코드가 바뀌고, Spring Security가 버전 업 하면서 코드가 또 바뀐다.
스택오버플로우, 블로그에서 코드 복사해서 붙여넣고 수정하는 것도 힘들다. 내 유일한 선임 개발자이자 선생님인 ChatGPT에게 물어보니 2021년 예전 코드만 알려준다.
나에게 남은 선택은 기초부터 파악하기! 게임 스테이지 분석처럼 스프링 시큐리티도 한번 까 보자.
출처: https://www.youtube.com/@jgeek-xr6sy
이미지에 나온 스텝을 하나씩 해석해 보았다. (ChatGPT 선생님 번역본)
✅STEP 1: (사용자가 자격 증명 입력)
사용자가 로그인을 위해 자격 증명(예: 사용자 이름과 비밀번호)을 제출합니다.
✅STEP 2: (Spring Security Filters - Authentication / 스프링 보안 필터 - 인증)
자격 증명이 포함된 요청이 스프링 보안의 필터 체인에 의해 가로채져 인증 프로세스가 시작됩니다.
✅STEP 3: (Authentication Manager / 인증 관리자)
AuthenticationManager
가 인증 프로세스를 관리하며, 올바른 인증 제공자(AuthenticationProvider
)로 요청을 전달합니다.
✅STEP 4: (Authentication Providers / 인증 제공자)
AuthenticationManager
는 제공된 자격 증명으로 요청을 인증하는 책임이 있는 하나 이상의 AuthenticationProvider
구현체에게 위임합니다.
✅STEP 5: (UserDetails Manager/Service)
UserDetailsService
가 호출되어 사용자의 세부 정보(예: 역할 및 권한)를 데이터베이스 또는 기타 데이터 소스에서 로드합니다.
✅STEP 6: (PasswordEncoder / 비밀번호인코더)
PasswordEncoder
는 제공된 비밀번호가 사용자에 대해 저장된 비밀번호와 일치하는지 확인하거나 인코딩하는 데 사용됩니다.
✅STEP 7: (인증 관리자로 돌아감)
AuthenticationProvider
는 인증된 객체를 AuthenticationManager
에 반환하여 인증이 성공했음을 나타냅니다.
✅STEP 8: (스프링 보안 필터로 돌아감)
AuthenticationManager
는 사용자가 인증되었음을 나타내어 스프링 보안 필터 체인에 제어를 반환합니다.
✅STEP 9: (보안 컨텍스트)
인증된 사용자의 세부 정보가 세션의 보안 컨텍스트를 보유하고 있는 SecurityContextHolder
에 저장됩니다.
✅STEP 10: (사용자)
요청이 대상 리소스(예: 컨트롤러 메서드)로 전달되고 사용자는 애플리케이션의 보안 부분에 접근할 수 있는 권한을 부여받습니다.
스프링 보안 필터들은 각 요청을 가로채어 인증이 필요한지 확인합니다. 인증이 필요하면 사용자를 로그인 페이지로 이동시키거나 초기 인증 중 저장된 정보를 사용합니다.
UsernamePasswordAuthenticationFilter
와 같은 필터는 HTTP 요청에서 사용자 이름/비밀번호를 추출하여 인증 객체를 준비합니다. 인증은 스프링 보안 프레임워크 내에서 인증된 사용자 정보를 저장하는 핵심 표준입니다.
필터에서 요청을 받으면, 사용자 정보의 유효성 검사를 사용 가능한 인증 제공자들에게 위임합니다. 앱 내에 여러 제공자가 있을 수 있으므로, 인증 관리자가 모든 인증 제공자를 관리하는 책임이 있습니다.
인증 제공자는 사용자 정보를 인증하기 위한 핵심 로직을 가지고 있습니다.
UserDetailsManager
와 UserDetailsService
는 DB/저장 시스템에서 사용자 정보를 검색, 생성, 업데이트, 삭제하는 데 사용이 됩니다.
비밀번호를 인코딩하고 해싱하는 데 도움이 되는 서비스 인터페이스입니다. 그렇지 않으면 평문 비밀번호로 사용해야 할 수도 있습니다.
요청이 인증되면 인증 정보는 일반적으로 SecurityContextHolder
에 의해 관리되는 스레드 로컬 보안 컨텍스트에 저장됩니다. 이는 동일한 사용자로부터의 다가오는 요청을 처리하는 데 사용이 됩니다.
🟡 시나리오
/contact, /notics 는 누구나 접근 가능
/myAccount, /myBalance, /myLoans, /myCards는 시큐리티 적용
🔵 구조
스프링 부트 : 3.1.2 버전 + Gradle (8.2.x)
자바 : 17 (OpenJDK)
🔵 코드
config - ProjectSecurityConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class ProjectSecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
/**
* *authorizeHttpRequests 설정*
* my 으로 시작하는 URL은 접근 권한 필요
* notices, contact는 누구나 접근 가능
*
*/
http.authorizeHttpRequests( (requests) ->
requests.requestMatchers("/myAccount","/myBalance","/myLoans","/myCards").authenticated()
.requestMatchers("/notices","/contact").permitAll() )
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();
/**
* 모든 request 거절
* Configuration to deny all the requests
*/
/*http.authorizeHttpRequests(requests -> requests.anyRequest().denyAll())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();*/
/**
* 모든 request 허락
* Configuration to permit all the requests
*/
/*http.authorizeHttpRequests(requests -> requests.anyRequest().permitAll())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();*/
}
}
application.properties
spring.security.user.name = test1
spring.security.user.password = 123123
controller - NoticesController
package com.example.springsecurity6.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NoticesController {
@GetMapping("/notices")
public String getNotices() {
return "Here are the notices details from the DB";
}
}
controller - AccountController
package com.example.springsecurity6.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AccountController {
@GetMapping("/myAccount")
public String getAccountDetails() {
return "Here are the account details from the DB";
}
}
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.2'
id 'io.spring.dependency-management' version '1.1.2'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// spring security basic
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
}
tasks.named('test') {
useJUnitPlatform()
}
🟣 결과
/myAccount 접속 시, /login 으로 이동
test1 / 123123 입력 후
스프링 시큐리티 6 기본 셋팅을 해보았다.
다음에는 JPA, MySQL을 이용해서 설정을 해보자.