Spring Security 6 튜토리얼 (1)

Sorrynthx Kim·2023년 8월 17일
2
post-thumbnail

🍃 Spring Security 6

망할. 예전 코드가 또 deprecated 됐다.
스프링 부트가 버전 업 하면서 코드가 바뀌고, Spring Security가 버전 업 하면서 코드가 또 바뀐다.

스택오버플로우, 블로그에서 코드 복사해서 붙여넣고 수정하는 것도 힘들다. 내 유일한 선임 개발자이자 선생님인 ChatGPT에게 물어보니 2021년 예전 코드만 알려준다.

나에게 남은 선택은 기초부터 파악하기! 게임 스테이지 분석처럼 스프링 시큐리티도 한번 까 보자.

✅ Spring Security Internal Flow


출처: 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: (사용자)

요청이 대상 리소스(예: 컨트롤러 메서드)로 전달되고 사용자는 애플리케이션의 보안 부분에 접근할 수 있는 권한을 부여받습니다.

😤그래서 뭐하는 놈들?

🟢 스프링 보안 필터 (Spring Security Filters)

스프링 보안 필터들은 각 요청을 가로채어 인증이 필요한지 확인합니다. 인증이 필요하면 사용자를 로그인 페이지로 이동시키거나 초기 인증 중 저장된 정보를 사용합니다.

🟢 인증 (Authentication)

UsernamePasswordAuthenticationFilter와 같은 필터는 HTTP 요청에서 사용자 이름/비밀번호를 추출하여 인증 객체를 준비합니다. 인증은 스프링 보안 프레임워크 내에서 인증된 사용자 정보를 저장하는 핵심 표준입니다.

🟢 인증 관리자 (AuthenticationManager)

필터에서 요청을 받으면, 사용자 정보의 유효성 검사를 사용 가능한 인증 제공자들에게 위임합니다. 앱 내에 여러 제공자가 있을 수 있으므로, 인증 관리자가 모든 인증 제공자를 관리하는 책임이 있습니다.

🟢 인증 제공자 (AuthenticationProvider)

인증 제공자는 사용자 정보를 인증하기 위한 핵심 로직을 가지고 있습니다.

🟢 UserDetailsManager/UserDetailsService

UserDetailsManagerUserDetailsService는 DB/저장 시스템에서 사용자 정보를 검색, 생성, 업데이트, 삭제하는 데 사용이 됩니다.

🟢 PasswordEncoder

비밀번호를 인코딩하고 해싱하는 데 도움이 되는 서비스 인터페이스입니다. 그렇지 않으면 평문 비밀번호로 사용해야 할 수도 있습니다.

🟢 보안 컨텍스트 (SecurityContext)

요청이 인증되면 인증 정보는 일반적으로 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을 이용해서 설정을 해보자.

profile
https://github.com/sorrynthx/Spring-Boot/tree/main/springsecurity

0개의 댓글