2024. 11. 21. 14:32ใSpring/Spring Security
๐๏ธ github ์ฐธ๊ณ
https://github.com/kuk1157/spring-plus/commit/bd962480c81770d7e04057345d45c62bedfa9f60
๐ ๏ธ Spring Security ์ฌ์ฉ๋ฐฉ๋ฒ
์ฌ์ฉํ๊ฒ ๋ ๋ชฉ์ )
- ํน์ ์ฌ๋ฌ๊ฐ์ API์์ ํ์์ ๋ณด๊ฐ ํ์ํ ๊ฒฝ์ฐ์ ํ์ฌ ์ปค์คํ ์ด๋ ธํ ์ด์ ์ผ๋ก ๋ฐ๋ก ์ธํ ํด์ ๊ฐ์ ธ์ค๋ ์ ๋ณด๋ฅผ Spring Security๋ก ํด๊ฒฐ ํ๊ธฐ์ํจ.
1. gradle ํ์ผ์ ์์กด์ฑ ์ถ๊ฐ
// 2_9 Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test:6.0.0'
- ๋ฒ์ ์ ๋ณธ์ธ์ springboot ๋ฒ์ ๊ณผ ํ์ธํด์ผํ๋ค.
- ๋๋ฒ์งธ์ค์ Spring Security๋ฅผ ์ค์ ํ ํ ํ ์คํธ์ฝ๋์ ๋ํ ์์กด์ฑ์ผ๋ก ์์ด๋ ๋จ.
(ํ์๋ ํ ์คํธํ ๋ถ๋ถ์ด์์ด์ ๋ฃ์์ํ)
2. config ํจํค์ง์ WebSecurityConfig ํ์ผ ์ธํ
โป WebSecurityConfig.java
- Spring Security๋ฅผ ํ์ฉํ์ฌ JWT ๊ธฐ๋ฐ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ ๋ณด์ ์ค์ ์ ๊ตฌ์ฑํ ๊ฒ์ ๋๋ค. ๊ธฐ๋ณธ์ ์ธ ์น ๋ณด์ ์ค์ ํ,
JWT ํํฐ๋ฅผ ํตํด ์ฌ์ฉ์์ ์์ฒญ์์ ํ ํฐ์ ๊ฒ์ฌํ๊ณ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋๋ก ์ค๊ณ๋์ด ์์ต๋๋ค.
@EnableWebSecurity๋ก ๋ณด์์ ํ์ฑํ, ์ธ์ฆ์ด ํ์ํ URL๊ณผ ํ์์๋ URL์ ๊ตฌ๋ถํ์ฌ ์ ๊ทผ์ ์ ์ดํ๊ณ ์์ต๋๋ค.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtRequestFilter jwtRequestFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// CSRF ์ค์
http.csrf((csrf) -> csrf.disable());
http.authorizeHttpRequests((authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources ์ ๊ทผ ํ์ฉ ์ค์
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/error").permitAll() // ๊ถ์ฅํ์ง๋ ์๋ ๋ฐฉ๋ฒ
);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
WebSecurityConfig ์ฝ๋ํด์)
1. SecurityFilterChain Spring Security์์ ์ ๊ณต๋๋ ๋ณด์ํํฐ์ฒด์ธ์ด๊ณ HttpSecurity๋ก ๋ฆฌ์์ค(url)๋ณ๋ก ์ ๊ทผ ๊ถํ์ ์ค์
2. CSRF์ค์ ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ ์ธ๋ถapi์ฐ๋ํ ๋ ๊ฐ๋ฐ์๋๊ตฌ์์ ์๋ฌ๋ฐ์ํ๊ฒ ๋๋ฉด ๋ง์ด ๋ณด๊ฒ๋๋ ๊ฒ์ด๋ฉฐ, csrf๊ณต๊ฒฉ์ ๋ฐฉ์งํ๋ ๊ธฐ๋ฅ์ ๋นํ์ฑํ ํ๋ค. (์ ์ฒด ํ์ด์ง์ ๋ํ์ฌ 403์๋ฌ๋ฅผ ๋ฐํํ๊ณ ์๊ณ , REST API์์๋ CSRF๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ์ ๋นํ์ฑํ)
3. authorizeHttpRequests - HTTP ์์ฒญ์ ๋ํ ๊ถํ์ ์ค์ ํจ.
4. requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
- ์ ์ ๋ฆฌ์์ค์ ๋ํด์ ์ธ์ฆ์์ด ์ ๊ทผ ํ์ฉํจ.(๊ณตํตํ์ผ์ด๋ css, ๊ณตํตjs, ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ)
5. requestMatchers("/auth/**").permitAll() - /auth/๋ก ์์ํ๋ ๋ชจ๋ ์์ฒญ(api)๋ ์ธ์ฆ ์์ด ์ ๊ทผํ์ฉ(๋ก๊ทธ์ธ,ํ์๊ฐ์
)
6. anyRequest().authenticated() - ๊ทธ ์ธ์ ๋ชจ๋ ์์ฒญ์ ์ธ์ฆ์ ํ๋๋ก ์งํ
7. addFilterBefore: JwtRequestFilter - jwtRequestFilter๋ผ๋ ์ปค์คํ
ํํฐ(jwtํ ํฐํํฐ) ์ฌ์ฉ์ ์์ฒญ์ ๊ฒ์ฌ
8. UsernamePasswordAuthenticationFilter.class
- Spring Security์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ์ฌ์ฉ์์ด๋ฆ,๋น๋ฐ๋ฒํธ ์ธ์ฆ ํํฐ.
- jwtRequestFilterํด๋น ์ปค์คํ ํํฐ๋ณด๋ค ๋จผ์ ์คํ๋๋๋ก ์ค์
9. AuthenticationManager - Spring Security์์ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ๋ด๋นํ๋ ๋งค๋์
10. AuthenticationConfiguration - Spring Security์ ์ธ์ฆ ์ค์ ์ ๊ด๋ฆฌํ๋ ํด๋์ค(์ด ํด๋์ค์์ Manager๋ฅผ ๊ฐ์ ธ์ด)
3. JwtRequestFilter ํ์ผ์ธํ
โป JwtRequestFilter.java
- JwtRequestFilter๋ HTTP ์์ฒญ์ ํฌํจ๋ JWT ํ ํฐ์ ๊ฒ์ฆํ๊ณ ,
ํ ํฐ์ด ์ ํจํ๋ฉด ์ฌ์ฉ์๋ฅผ ์ธ์ฆ๋ ์ํ๋ก ์ค์ ํ์ฌ,
์ดํ์ ์์ฒญ์์ ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ์ ๊ทผํ๋๋ก ์ฒ๋ฆฌํ๋ ํํฐ์
๋๋ค.
์ด๋ฅผ ํตํด Spring Security์์ JWT ๊ธฐ๋ฐ ์ธ์ฆ์ ์์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
@Slf4j
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Autowired
public JwtRequestFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
try {
Long userId = jwtUtil.extractUserId(token); // userId ์ถ์ถ
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userId, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
log.error("Internal server error", e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
chain.doFilter(request, response);
}
}
JwtRequestFilter ์ฝ๋ํด์)
1. OncePerRequestFilter - Spring Security์์ ์ ๊ณตํ๋ ํํฐ ํด๋์ค
- doFilterInternal ๋ฉ์๋๊ฐ ํ ์์ฒญ์ ๋ํด ํ๋ฒ๋ง ์คํ๋๋๋กํจ.(ํํฐ์ค๋ณต์คํ ๋ฐฉ์ง)
2. doFilterInternal - ์ด ๋ฉ์๋๋ ์์ฒญ์ด ํํฐ ์ฒด์ธ์ ๋ค์ด์ฌ๋๋ง๋ค ํธ์ถ๋จ.
- ํํฐ๋ ์์ฒญ์ ๊ฐ๋ก์ฑ์ ์ฒ๋ฆฌ ํ chain.doFilter()๋ฅผ ํตํด(์ตํ๋จ) ํํฐ ์ฒด์ธ ๊ณ์ ์คํ
3. SecurityContextHolder - Spring Security ๋ณด์ ์ปจํ
์คํธ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฐ์ฒด
4. UsernamePasswordAuthenticationToken - ์ฌ์ฉ์์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ ํ์ฉํ๋ ํ ํฐ
- ํ์ ๊ณ ์ id๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ธ์ฆ ๊ฐ์ฒด ์์ฑ์ค
5. SecurityContextHolder.getContext().setAuthentication(authentication)
- ์ธ์ฆ๋ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ SecurityContextHolder ์ ์ค์ ํ์ฌ ํ์ ์์ฒญ์์ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋ ์ํ๋ก ์ฒ๋ฆฌ๋๋๋ก ํจ.
(๊ณ์์ธ์ฆ๋์ํ๋ก ์ ๊ทผ๊ฐ๋ฅ)
6. chain.doFilter(request, response) - ํ ํฐ ๊ฒ์ฆ์ด ๋๋ ํ ํํฐ ์ฒด์ธ์ ๋๋จธ์ง ๋ถ๋ถ๋ ์คํ๋๋๋ก ์งํ.
๊ธฐํ)
- JwtUtil ์ผ๋ถ
- token์ ํ์ฉํด์ ํ์ id(๊ณ ์ ๋ฒํธ) ์ถ์ถ ๋ฉ์๋
โป ์ฃผ์ํ ์ )
- jwtํ ํฐ ๊ด๋ จ ์์ธ๋ ๋ค ํด๋น ํ์ผ์์ ์งํํด์ค์ผํจ. (์์)
- 403์๋ฌ์ ๋ํ ๋ด์ฉ์ ํธ๋ฌ๋ธ์ํ ๋งํฌ ์ฐธ๊ณ
https://kuk1938.tistory.com/215
4. ์ค์ ํ์ฉ
โป Spring Security์์ ์ ๊ณตํ๋ Principal ์ธํฐํ์ด์ค ์ฌ์ฉ
- Controller์์ Principal ์ธํฐํ์ด์ค๋ฅผ ํต์งธ๋ก ๋ด์์ ๋ณด๋ด๊ณ ์๋ค.
- ์ธํฐํ์ด์ค๋ก ๊ฐ์ ธ์จ userId๋ก DB์ ์ง์ ์กฐํํ์ฌ user๊ฐ์ฒด๋ฅผ ํ์ฉ
โป Principal ์ฅ์ )
1) ๊ฐ๊ฒฐํ๊ณ ์ง๊ด์
- Spring Security์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ค๋ฃฐ ๋ ๋งค์ฐ ์ง๊ด์ ์ด๊ณ ๊ฐ๋จํฉ๋๋ค.
2) Spring Security์์ ํตํฉ
- Spring Security์ ์์ฐ์ค๋ฝ๊ฒ ํตํฉ๋์ด ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณด์ ์ํ๋ฅผ ์์ ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
3) ์ ์ฐ์ฑ
- Principal์ ๋ค์ํ ๋ณด์ ์ธ์ฆ ๋ฐฉ์๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์๋ ์ ์ฐํ ๋ฐฉ๋ฒ์
๋๋ค.
- ์๋ฅผ ๋ค์ด, OAuth2, SSO ๋ฑ ๋ค์ํ ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉํ ๋๋ ์ธ์ฆ๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ผ๊ด๋๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.