๊ด€๋ฆฌ ๋ฉ”๋‰ด

<Hello Hosung๐Ÿ˜Ž/>

[SpringBoot] Spring Security๋ฅผ ํ™œ์šฉํ•œ ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๋ณธ๋ฌธ

๐Ÿ’ป Java/ใ…คJava(SpringBoot)

[SpringBoot] Spring Security๋ฅผ ํ™œ์šฉํ•œ ์ธ์ฆ ๋ฐ ๊ถŒํ•œ

์ขŒ์ถฉ์šฐ๋Œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž ์ผ๊ธฐ๐Ÿง 2024. 11. 23. 22:34

Spring Security๋Š” Spring ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ธ์ฆ(authentication) ๋ฐ ๊ถŒํ•œ(authorization) ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ์ด ๋ธ”๋กœ๊ทธ์—์„œ๋Š” Spring Security๋ฅผ ์„ค์ •ํ•˜๊ณ , ๊ธฐ๋ณธ์ ์ธ ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

1. Spring Security ์†Œ๊ฐœ


Spring Security๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ค‘์š”ํ•œ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

์ธ์ฆ(Authentication): ์‚ฌ์šฉ์ž๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ํ™•์ธํ•˜๋Š” ๊ณผ์ •.
๊ถŒํ•œ(Authorization): ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ํ™•์ธํ•˜๋Š” ๊ณผ์ •.

2. Spring Security ์„ค์ •ํ•˜๊ธฐ

2.1. ์˜์กด์„ฑ ์ถ”๊ฐ€


Spring Security๋ฅผ ํ”„๋กœ์ ํŠธ์— ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด pom.xml์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- ํ•„์š”ํ•œ ๊ฒฝ์šฐ JPA, Thymeleaf ๋“ฑ์˜ ์˜์กด์„ฑ๋„ ์ถ”๊ฐ€ -->
</dependencies>


Spring Boot ํ”„๋กœ์ ํŠธ์—์„œ๋Š” spring-boot-starter-security๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์ธ ๋ณด์•ˆ ์„ค์ •์ด ์ž๋™์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

2.2. ๊ธฐ๋ณธ ์ธ์ฆ ์„ค์ •


Spring Security๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ HTTP ๊ธฐ๋ณธ ์ธ์ฆ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ‘๊ทผํ•˜๋ ค๋Š” ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธ ํ™”๋ฉด์„ ํ†ตํ•ด ์ธ์ฆ์„ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()  // ๊ณต๊ฐœ URL์€ ์ธ์ฆ ์—†์ด ์ ‘๊ทผ ๊ฐ€๋Šฅ
                .anyRequest().authenticated()  // ๋‚˜๋จธ์ง€ URL์€ ์ธ์ฆ ํ•„์š”
            .and()
            .formLogin();  // ๊ธฐ๋ณธ ๋กœ๊ทธ์ธ ํผ ์‚ฌ์šฉ
    }
}


์ด ์„ค์ •์€ /public/** ๊ฒฝ๋กœ๋Š” ์ธ์ฆ ์—†์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ , ๋‹ค๋ฅธ ๋ชจ๋“  ์š”์ฒญ์€ ์ธ์ฆ์„ ์š”๊ตฌํ•˜๋„๋ก ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

3. ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ตฌํ˜„


Spring Security๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ UserDetailsService๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3.1. UserDetailsService ๊ตฌํ˜„


์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์œ„ํ•ด UserDetailsService๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // ์‹ค์ œ DB์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ
        if ("admin".equals(username)) {
            return User.builder()
                       .username("admin")
                       .password("{noop}password")  // {noop}์€ ํŒจ์Šค์›Œ๋“œ ์ธ์ฝ”๋”ฉ์„ ํ•˜์ง€ ์•Š์Œ์„ ์˜๋ฏธ
                       .roles("ADMIN")
                       .build();
        } else {
            throw new UsernameNotFoundException("User not found");
        }
    }
}

3.2. ๋น„๋ฐ€๋ฒˆํ˜ธ ์ธ์ฝ”๋”ฉ


ํŒจ์Šค์›Œ๋“œ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜๋ ค๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ธ์ฝ”๋”ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
Spring Security๋Š” BCryptPasswordEncoder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}


์‚ฌ์šฉ์ž ์ธ์ฆ ๊ณผ์ •์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ธ์ฝ”๋”ฉ๋œ ํ˜•ํƒœ๋กœ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

4. ๊ถŒํ•œ ๊ด€๋ฆฌ


Spring Security์—์„œ๋Š” ๊ถŒํ•œ ๊ด€๋ฆฌ๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ถŒํ•œ์€ @PreAuthorize, @Secured, ๋˜๋Š” hasRole() ๋“ฑ์˜ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4.1. URL ๊ธฐ๋ฐ˜ ๊ถŒํ•œ ์„ค์ •


Spring Security์—์„œ๋Š” HttpSecurity๋ฅผ ํ†ตํ•ด URL์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")  // ๊ด€๋ฆฌ์ž๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ
            .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")  // ์‚ฌ์šฉ์ž ๋˜๋Š” ๊ด€๋ฆฌ์ž๊ฐ€ ์ ‘๊ทผ ๊ฐ€๋Šฅ
            .anyRequest().authenticated()  // ๋‚˜๋จธ์ง€ URL์€ ์ธ์ฆ ํ•„์š”
        .and()
        .formLogin();
}


4.2. ๋ฉ”์†Œ๋“œ ๊ธฐ๋ฐ˜ ๊ถŒํ•œ ์„ค์ •


๋ฉ”์†Œ๋“œ ๋ ˆ๋ฒจ์—์„œ๋„ ๊ถŒํ•œ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ๊ถŒํ•œ์„ ์ฒดํฌํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/admin/dashboard")
    public String getAdminDashboard() {
        return "Admin Dashboard";
    }

    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    @GetMapping("/user/profile")
    public String getUserProfile() {
        return "User Profile";
    }
}


5. ๋งˆ๋ฌด๋ฆฌ

Spring Security๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ธ์ฆ๊ณผ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ๋งค์šฐ ์‰ฝ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ธ์ฆ๊ณผ ๊ถŒํ•œ ์„ค์ •์„ ํ†ตํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ณ , ์„ธ๋ฐ€ํ•œ ๊ถŒํ•œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ธฐ๋ณธ ์„ค์ •์„ ๋ฐ”ํƒ•์œผ๋กœ ๋” ๋ณต์žกํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ฐ˜์„ ๋งˆ๋ จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ, JWT ์ธ์ฆ, OAuth2 ์ธ์ฆ ๋“ฑ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค๋„ Spring Security์—์„œ ์ง€์›ํ•˜๋ฏ€๋กœ ํ•„์š”์— ๋”ฐ๋ผ ํ™•์žฅํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.