๐ํ๊ฒฝ
IntelliJ Ultimate
Java 17
SpringBoot 3.2.3
๐์คํ๋ง ์ํ๋ฆฌํฐ(Spring Security)๋?
์ธ์ฆ(Autentication)๊ณผ ์ธ๊ฐ(Autorization), ๋ฐ์ดํฐ ๋ณดํธ ๊ธฐ๋ฅ์ ํฌํจํ์ฌ ์ฌ์ฉ์์ ๊ถํ์ ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ฃผ๋ Spring์ ํ๋ ์์ํฌ. ์ฝ๊ฐ์ ์ฝ๋์ ์ค์ ๋ง์ผ๋ก ๋ก๊ทธ์ธ ์ฒ๋ฆฌ์ ์๋ ๋ก๊ทธ์ธ, ๋ก๊ทธ์ธ ํ์ฒ๋ฆฌ ๋ฑ์ ์ฒ๋ฆฌํ์ฌ ๊ฐ๋ฐ์๊ฐ ๋ณด์์ ๊ดํ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ๊ตฌํํ ์ ์๋๋ก ๋์์ค๋ค.
โ๏ธ ์ธ์ฆ(Autentication): ์ค์ค๋ก๋ฅผ ์ฆ๋ช ํจ. ์ฐธ์ด๋ผ๋ ๊ทผ๊ฑฐ๊ฐ ์๋ ๋ฌด์ธ๊ฐ๋ฅผ ํ์ธํ๊ฑฐ๋ ํ์ฆํ๋ ํ์. ์ฌ์ฉ์๊ฐ ์ค์ค๋ก๋ฅผ ์ฆ๋ช ํ๊ธฐ ์ํ์ฌ ์์ ์ ์ ๋ณด(์์ด๋ ๋ฐ ๋น๋ฐ๋ฒํธ)๋ฅผ ์ ๊ณตํ๊ณ ์ด๋ฅผ ํตํด ์ธ์ฆ์ ์๋ฃํ๋ ๋ก๊ทธ์ธ ๊ฐ๋ ์ด ์ฌ๊ธฐ์ ์ํ๋ค.
โ๏ธ ์ธ๊ฐ(Autorization): ํ๊ฐ ๋๋ ๊ถํ. ๋ฆฌ์์ค์ ๋ํ ์ ๊ทผ ๊ถํ ๋ฐ ์ ์ฑ ์ ์ง์ ํ๋ ๊ธฐ๋ฅ. ์ธ์ฆ์ ๋ง์น ์ฌ์ฉ์์ผ์ง๋ผ๋ ์์ฒญํ๋ ๊ณณ์ ์ ๊ทผ ๊ถํ์ด ์๋์ง ํ์ธํ๋ ๊ณผ์ ์ ๋ปํ๋ค. ์ ๋ขฐํ ์ ์๋ ์ฌ์ฉ์๋ ๋ฆฌ์์ค์ ๋ฌด์ ํ์ ์ธ ์ ๊ทผ์ด ํ์ฉ๋๊ธฐ ๋๋ฌธ์ ๋ฐ๋์ ์ธ์ฆ์ ํตํด ์ ์์ด ํ์ธ๋์ด์ผ ํ๋ค. ์์คํ ๋ฆฌ์์ค๊ฐ ์์น ์์ ์ฉ๋๋ก ์ฌ์ฉ๋๋ ๊ฒ์ ํผํ๊ธฐ ์ํด์๋ ๋ถ๋ถ์ ์ผ๋ก ์ ๋ขฐํ ์ ์๋ ์ฌ์ฉ์๋ ๊ฒ์คํธ์ ๊ฒฝ์ฐ ์ ํ๋ ๋ฆฌ์์ค๋ง ์ ๊ทผํ ์ ์๋๋ก ์ค์ ํด์ผํ๋ค.
โ๏ธ ์ ๊ทผ ์ฃผ์ฒด(Principal): ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ๋์.
โ๏ธ ๊ถํ(Role): ์ธ์ฆ๋ ์ฃผ์ฒด๊ฐ ๋ฆฌ์์ค์ ์ ๊ทผ ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ ์ํํ ์ ์๋๋ก ํ๋ฝ๋์๋์ง ํ๋จํ ๋ ์ฌ์ฉ๋จ.
๐ํ๋ฆ
Spring Security๋ ํํฐ(Filter)๋ฅผ ํตํด์ ๋์ํ๊ธฐ ๋๋ฌธ์ Web MVC์ ๋ถ๋ฆฌ๋์ด ๊ด๋ฆฌ ๋ฐ ๋์ํ๋ค. ํํฐ๋ `Dispatcher Servlet` ์ด์ ์ ์ ์ฉ๋๋ค๋ ์ ์์ `Interceptor`์ ์ฐจ์ด๊ฐ ์๋ค.
1. ์ฌ์ฉ์๊ฐ ํน์ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ๋ฉด `DispatcherServlet`์ด ์์ฒญ์ ๋ฐ์ ์ฒ๋ฆฌํ๋๋ฐ, ์ด๋ `DispatcherServlet`๊ฐ ์์ฒญ์ ๋ฐ๊ธฐ ์ ์ `Filter`๊ฐ ์์ฒญ์ ๊ฐ๋ก์ฑ์ด `Authotication Manager`๋ก ์์ฒญ์ ์์ํ๋ค.
2. ์ฌ์ฉ์๊ฐ ์ธ์ฆํ๊ธฐ ์ํ์ฌ ์ ๋ณด๋ฅผ ์ ์ถํ๋ฉด `Authotication Manager`๊ฐ ๋ฑ๋ก๋ `Authotication Provider`๋ฅผ ์กฐํํ๋ฉฐ ์ธ์ฆ์ ์๊ตฌํ๋ค.
3. `Authotication Provider`๊ฐ ์ค์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ์ฌ `UserDetails`๋ฅผ ๋ฐํํ๋ค.
4. ๋ฐํ๋ ๊ฒฐ๊ณผ๊ฐ `securitycontextholder`์ ์ ์ฅ๋์ด ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ Spring Controller์์ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ๋ ์๋ฌด๋ฐ ์ค์ ์ด ์์ผ๋ฉด ๋ชจ๋ ๋ฆฌ์์ค(์์)์ ์ธ์ฆ์ ์๊ตฌํ๋ค. ์ฆ, ์ค์ ์ด ์์ผ๋ฉด ์ ์ ๊ฐ ์ด๋ค url์ ์ ๊ทผํ๋๋ผ๋ `/login`์ผ๋ก ํ๊ฒจ๋ธ๋ค. ๋ก๊ทธ์ธํ์ง ์์๋ ๋ณผ ์ ์๋๋ก ์ค์ ํ๊ฑฐ๋ ํน์ ํ์ด์ง๋ง ์ธ์ฆ์ ์๊ตฌํ๊ณ ์ถ๋ค๋ฉด ๊ฐ๋ฐ์๊ฐ ์ง์ ์ค์ ์ ์์ฑํด์ผํ๋ค. ์๋ฐ ์ฝ๋๋ก `SecurityFilterChain`์ ๋ฐํํ๋ `@Bean`์ ์์ฑํ์ฌ ์ค์ ํ ์ ์๋ค.
์ด๋ ์ธ์ฆ์ ์ํ ๋ก๊ทธ์ธ ํ์ด์ง์ ์ ์ ๋ฆฌ์์ค๋ ์ํ๋ฆฌํฐ๋ฅผ ์ ์ฉํ ํ์๊ฐ ์์ผ๋ฏ๋ก ๋ฉ์๋ ์ค์ ์ ์ถ๊ฐํ๋ค.
package com.ezen.management.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@Slf4j
@EnableMethodSecurity(prePostEnabled = true)
public class CustomSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ํด๋น ๋ฉ์๋๋ฅผ ์์ฑํ๋ฉด ํํฐ๋ฅผ ์ปค์คํ
ํ ์ ์์
http.authorizeHttpRequests(request ->
request.requestMatchers("/member/login", "/css/**", "/images/**", "/js/**")
.permitAll()
.anyRequest()
.authenticated();
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
// ์ ์ ๋ฆฌ์์ค์ ์ํ๋ฆฌํฐ ์ ์ฉํ์ง ์์
return (web -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations()));
}
// ์คํ๋ง ์ํ๋ฆฌํฐ๋ ๊ธฐ๋ณธ์ ์ผ๋ก PasswordEncoder๋ผ๋ ์กด์ฌ๋ฅผ ํ์๋ก ํจ
// BCryptPasswordEncoder ๋ ๊ฐ์ฅ ๋ฌด๋ํ ์ฌ์ฉ๋๋ ํจ์ค์๋ ์ธ์ฝ๋
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
`filterChain()` ๋ฉ์๋๊ฐ ๋์ํ๋ฉด ์ด์ ๊ณผ ๋ฌ๋ฆฌ ๋ชจ๋ ํ์ด์ง์ ์ ๊ทผํ ์ ์๋ค.
๐์ธ์ฆ ์ฒ๋ฆฌ
์คํ๋ง ์ํ๋ฆฌํฐ๋ ๊ธฐ์กด์ ๋ก๊ทธ์ธ ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ ์์ด๋์ ๋น๋ฐ๋ฒํธ๋ฅผ ํ๋ฒ์ ์กฐํํ๋ ๊ฒ์ด ์๋๋ผ ์ฌ์ฉ์๊ฐ ์ธ์ฆ์ ์ํด ์ ์ถํ username, ์ฆ ์์ด๋๋ง์ผ๋ก ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์จ ๋ค ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ๋์ง ๊ฒ์ฆํ๋ค. ์ธ์ฆ ์ฒ๋ฆฌ๋ `Authentication Provider`๊ฐ ์ฒ๋ฆฌํ๋๋ฐ, `Authentication Provider`์ ๊ทธ ์ดํ์ ์ฒ๋ฆฌ ๊ณผ์ ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ฑด๋๋ฆด ์ผ์ด ๊ฑฐ์ ์์ผ๋ฏ๋ก ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ค์ ๋ก ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ๋ด๋นํ๋ ๊ฐ์ฒด๋ง์ ์ค์ ํ๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ์์ ๊ฐ์ฅ ์ค์ํ ๊ฐ์ฒด๋ ์ค์ ๋ก ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ `UserDetailService`๋ผ๋ ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ค. `UserDetailService`๋ `UserDetail`์ ๋ฐํํ๋ `loadUserByUsername()`์ด๋ผ๋ ๋จ ํ๋์ ๋ฉ์๋๋ฅผ ๊ฐ์ง๋๋ฐ, ์ด ๋ฉ์๋๊ฐ ์ค์ ๋ก ์ธ์ฆ์ ์ฒ๋ฆฌํ ๋ ํธ์ถ๋๋ ๋ถ๋ถ์ด๋ค. ๊ฐ๋ฐ์๋ `UserDetailService`์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ฅผ ์์ฑํ์ฌ ์ธ์๋ก ๋๊ฒจ๋ฐ์ ์์ด๋(username)๋ฅผ ์ฌ์ฉํ์ฌ ์ธ์ฆ์ ๊ตฌํํ๋ค.
package com.ezen.management.security;
import com.ezen.management.domain.Member;
import com.ezen.management.dto.MemberSecurityDTO;
import com.ezen.management.repository.MemberRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@Slf4j
public class CustomUserDetailService implements UserDetailsService {
private PasswordEncoder passwordEncoder;
@Autowired
private MemberRepository memberRepository;
public CustomUserDetailService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = new BCryptPasswordEncoder();
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Member> result = memberRepository.getByIdWithRoles(username);
if(result.isEmpty()){
throw new UsernameNotFoundException("Username Not Found......");
}
Member member = result.get();
return new MemberSecurityDTO(
member.getId(),
member.getPwd(),
true, // enabled
true, // accountNonExpired
true, // credentialsNonExpired
true, // accountNonLocked
member.getRoleSet() // Set<MemberRole>
.stream() // Stream<MemberRole>
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.name())) // Stream<SimpleGrantedAuthority>
.collect(Collectors.toList()));
}
}
์ด์ `CustomSecurityConfig`์ ๊ธฐ๋ณธ ๋ก๊ทธ์ธ ํ์ด์ง ๋์ ์ปค์คํ ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ์ง์ ํ๊ณ , ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๊ฒฝ๋ก(url)๋ฅผ `/member/login`๋ก ์ง์ ํด์ค๋ค.
@Configuration
@Slf4j
@EnableMethodSecurity(prePostEnabled = true)
public class CustomSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ํด๋น ๋ฉ์๋๋ฅผ ์์ฑํ๋ฉด ํํฐ๋ฅผ ์ปค์คํ
ํ ์ ์์
http.authorizeHttpRequests(request ->
request.requestMatchers("/student/login", "/member/login", "/css/**", "/images/**", "/js/**")
.permitAll()
.anyRequest()
.authenticated())
.formLogin(formLogin -> formLogin.loginPage("/member/login").loginProcessingUrl("/member/login"))
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
...
}
์คํ๋ง ์ํ๋ฆฌํฐ๋ ๊ธฐ๋ณธ์ ์ผ๋ก GET ๋ฐฉ์์ ์ ์ธํ ๋ชจ๋ ์์ฒญ(POST/PUT/DELETE) ์์ฒญ์ CSRF ํ ํฐ์ ์๊ตฌํ๋ค. ๋ฐ๋ผ์ csrf ํ ํฐ์ ์ ์กํ์ง ์์ผ๋ฉด 403(Forbidden) ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ์ด ๊ธ์์๋ ํ์ JWT๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ CSRF ํ ํฐ์ ๋นํ์ฑํ ์ฒ๋ฆฌํ๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ์ ๊ฐ์ฅ ์ค์ํ ์ ์ ์ค์ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ POST ๋ฐฉ์์ ๋ํ ์ฝ๋๋ฅผ ์์ฑํ์ง ์๋๋ค๋ ๊ฒ์ด๋ค. ์์์ `CustomSecurityConfig`๋ฅผ ์์ฑํ ๋ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๊ฒฝ๋ก๋ฅผ `/member/login`์ผ๋ก ์ง์ ํ๊ธฐ ๋๋ฌธ์ ์คํ๋ง ์ํ๋ฆฌํฐ ๋ด๋ถ์์ POST ๋ฐฉ์์ ๋ก๊ทธ์ธ ์์ฒญ์ ์ฒ๋ฆฌํ๋ค. ๊ฐ๋ฐ์๋ GET ๋ฐฉ์์ผ๋ก `/member/login`์ ์ ๊ทผํ์ ๋ ์ปค์คํ ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๋งคํํ๊ธฐ ์ํ ์ฝ๋๋ฅผ `Controller`์ ์์ฑํด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
๐์ฐธ๊ณ
์๋ฐ ์น ๊ฐ๋ฐ ์ํฌ๋ถ - ๊ตฌ๋ฉ๊ฐ๊ฒ ์ฝ๋ฉ๋จ
Spring Security :: Spring Security
If you are ready to start securing an application see the Getting Started sections for servlet and reactive. These sections will walk you through creating your first Spring Security applications. If you want to understand how Spring Security works, you can
docs.spring.io
Spring Security๋? ์ฌ์ฉํ๋ ์ด์ ๋ถํฐ ์ค์ ๋ฐฉ๋ฒ๊น์ง ์๋ ค๋๋ฆฝ๋๋ค! I ์ด๋์ ๋ธ๋ก๊ทธ
ํํ์ด์ง์ ์ธ์ฆ ๋ฐ ๊ถํ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ๋ถ์ฌํด ์ธ์ฆ ๋ฐ ๊ถํ ๋ณดํธ ๊ธฐ๋ฅ์ ์์ฝ๊ฒ ์ถ๊ฐํ ์ ์๋ Spring์ ํ๋ ์์ํฌ ์ค ํ๋์ธ ‘Spring Security’์ ๋ํด ์ด๋์์์ ์์ธํ ์๋ ค๋๋ฆฝ๋๋ค. I spring
www.elancer.co.kr
[SpringBoot] Spring Security๋?
๋๋ถ๋ถ์ ์์คํ ์์๋ ํ์์ ๊ด๋ฆฌ๋ฅผ ํ๊ณ ์๊ณ , ๊ทธ์ ๋ฐ๋ฅธ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ(Authorization)์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ด์ผ ํ๋ค. Spring์์๋ Spring Security๋ผ๋ ๋ณ๋์ ํ๋ ์์ํฌ์์ ๊ด๋ จ๋ ๊ธฐ๋ฅ
mangkyu.tistory.com
[SpringSecurity] AbstractAuthenticationProcessingFilter ์์ ์ ๋ณต
Abstract processor of browser-based HTTP-based authentication requests.๋ธ๋ผ์ฐ์ ๊ธฐ๋ฐ์ Http ๊ธฐ๋ฐ ์ธ์ฆ์ ๋ํ ์ถ์ ํ๋ก์ธ์์ด๋ค.GenericFilterBean์ ์์์ ๋ฐ๊ณ ์๊ณ , subclass๋ก๋ OAut
velog.io
'Java > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SpringBoot] ํ์ด์ง๋ค์ด์ (Pagination) (0) | 2024.03.26 |
---|---|
[Spring] ๋ฆฌ๋ค์ด๋ ์ ํ ํ์๊ฐ ๋๋ฌด ๋ง์ต๋๋ค. (0) | 2024.03.24 |
[SpringBoot] Spring Security ์ฌ์ฉ ์ deprecated (0) | 2024.03.16 |
_(underscore)๋ก ์ฐ๊ฒฐ๋ uuid์ ์๋ณธ ํ์ผ๋ช ์๋ผ์ ๋ณด๊ดํ๊ธฐ (0) | 2024.03.16 |
[SpringBoot] Swagger UI์์ MultipartFile ์ค์ ์ด ๋์ง ์๋ ์ค๋ฅ (0) | 2024.03.15 |