build.gradle
에
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
...
}
package com.myweapon.rtc_chatting.Entity;
import java.util.Arrays;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Users {
@jakarta.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String roles; // USER,ADMIN
@Builder
public Users(String username, String password, String roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public List<String> getRoles() {
if (roles.length() > 0) {
return Arrays.asList(roles.split(","));
}
return List.of();
}
}
Cors정책은 설정해주자.
package com.myweapon.rtc_chatting.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/api/**", config);
return new CorsFilter(source);
}
}
package com.myweapon.rtc_chatting.Config;
import org.springframework.context.annotation.Bean;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
return http.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(sessionManagementConfig ->
sessionManagementConfig
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(registry ->
registry.requestMatchers("api/v1/user/**")
.hasAnyRole("USER", "MANAGER", "ADMIN")
.requestMatchers("/api/v1/manager/**")
.hasAnyRole("USER", "MANAGER", "ADMIN")
.requestMatchers("/api/v1/admin/**")
.hasAnyRole("MANAGER", "ADMIN")
.anyRequest().permitAll())
.build();
}
}
filterChain에 준 옵션을 하나씩 살펴보자.
@Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception
Spring Security의 HTTP 보안 설정을 정의하겠다는 뜻이다.
http.csrf(AbstractHttpConfigurer::disable)
CRSF 보호를 비활성화하겠다는 의미로
CSRF(Cross-Site Request Forgery)은
웹 사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행동을 하도록 만드는 공격을 말한다.
비활성화를 해주면 보안이 취약해지는 게 맞다.
하지만 RESTful API같이 클라이언트가 요청마다 토큰을 관리하기 어려운 경우나
서비스가 내부 네트워크에서만 사용되는 특수한 경우에만 CSRF 보호를 비활성화하기도 한다고 한다.
.sessionManagement(sessionManagementConfig -> sessionManagementConfig .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
state(상태)는 클라이언트 세션 정보를 의미하며
stateless 서버는 요청 간에 클라이언트 세션 정보를 저장하지 않는다는 것을 의미한다.
클라이언트의 각 요청은 이전 요청과 무관한 독립적인 요청으로 처리되고
서버는 클라이언트 세션 상태를 유지하지 않아서 서버의 메모리 사용량을 줄일 수 있다.
Spring Security에서 session을 생성할 지 아닐지 정책으로 정한다는 의미이다.
세션 관리 정책 중에 하나로 Spring Security가 세션을 생성하거나 사용하지 않는다.
RESTful API와 같이 각 요청이 서로 독립적이어야 하는 경우에 주로 사용한다.
이 정책을 사용하면 모든 요청은 새로운 인증이 필요하고 쿠키 사용이 제한된다.
여기서는 토큰으로 인증을 진행한다.
formLogin(AbstractHttpConfigurer::disable)
: 폼 기반 로그인을 비활성화
Spring Security 의존성만 추가하고 애플리케이션을 실행하면
자동으로 Spring Security에서 만든 로그인 페이지로 이동하는데
이걸 비활성화하면 더 이상 나오지 않게 된다.
폼 기반 로그인이 비활성화된다고 해도 최초의 로그인은 수행해야한다.
예를 들어, POST /login 사용해서 ID,password를 보내면 로그인을 수행할 수 있다.
로그인을 수행하면 Token을 받게되고 이후에 모든 요청에 token을 담아 ID,password를
추가로 보내지 않아도 된다.
httpBasic(AbstractHttpConfigurer::disable)
:HTTP Basic 인증을 비활성화
stateless 방식으로 클라이언트는 매 요청마다 인증 정보를 전송해야하는 방식이다.
반면 HTTP Basic 인증을 비활성화하면 클라이언트는 사용자 이름과 비밀번호를 제공하지
않아도 요청을 보낼 수 있게 된다. 토큰만 들고 있으면 요청을 보낼 수 있도록 하기위해
비활성화를 했다.
.authorizeHttpRequests(registry ->
registry.requestMatchers("api/v1/user/**")
.hasAnyRole("USER", "MANAGER", "ADMIN")
.requestMatchers("/api/v1/manager/**")
.hasAnyRole("USER", "MANAGER", "ADMIN")
.requestMatchers("/api/v1/admin/**")
.hasAnyRole("MANAGER", "ADMIN")
.anyRequest().permitAll())
registry.requestMatchers("api/v1/user/**").hasAnyRole("USER", "MANAGER", "ADMIN")
:/api/v1/manager/**
경로의 모든 요청에 대해registry.requestMatchers("/api/v1/admin/**").hasAnyRole("MANAGER", "ADMIN")
:anyRequest().permitAll()
:build()
지금까지 만든 필터를 하나의 SecurityFilterChain 객체로 합쳐서 생성한다.