본 시리즈는 작성자의 이해와 경험을 바탕으로 실습 위주의 설명을 기반으로 작성되었습니다.
실습 위주의 이해를 목표로 하기 때문에 다소 과장이 많고 생략된 부분이 많을 수 있습니다.
따라서, 이론적으로 미흡한 부분이 있을 수 있는 점에 대해 유의하시기 바랍니다.
또한, 본 시리즈는 ChatGPT의 도움을 받아 작성되었습니다.
수 차례의 질문을 통해 도출된 여러가지 다양한 방식의 코드를 종합하여
작성자의 이해와 경험을 바탕으로 가장 정석으로 생각되는 코드를 재정립하였습니다.
WebSecurityConfig.java
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtTokenFilter jwtTokenFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.csrf().disable()
.authorizeRequests()
// 관리자 관련 모든 요청에 대해 승인된 사용자 중 ADMIN 권한이 있는 사용자만 허용
.antMatchers("/api/v1/admin/**").hasRole("ADMIN")
.antMatchers("/api/v2/admin/**").hasRole("ADMIN")
// 회원가입 및 로그인 관련 모든 요청에 대해 아무나 승인
.antMatchers("/api/v1/auth/**").permitAll()
.antMatchers("/api/v2/auth/**").permitAll()
// 중복체크 관련 모든 요청에 대해 아무나 허용
.antMatchers("/api/v1/user/check/**").permitAll()
.antMatchers("/api/v2/user/check/**").permitAll()
// 유저정보 관련 모든 요청에 대해 승인된 사용자만 허용
.antMatchers("/api/v1/user/**").authenticated()
.antMatchers("/api/v2/user/**").authenticated()
// 첨부파일 관련 GET 요청에 대해 아무나 승인
.antMatchers(HttpMethod.GET, "/api/v1/attachment/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/v2/attachment/**").permitAll()
// 댓글 관련 GET 요청에 대해 아무나 승인
.antMatchers(HttpMethod.GET, "/api/v1/comment/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/v2/comment/**").permitAll()
// 게시글 관련 GET 요청에 대해 아무나 승인
.antMatchers(HttpMethod.GET, "/api/v1/post/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/v2/post/**").permitAll()
// 기타 모든 요청에 대해 승인된 사용자만 허용
.antMatchers("/api/v1/**").authenticated()
.antMatchers("/api/v2/**").authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(this.jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
접근권한에 대해 실질적으로 기준을 세우는 설정입니다.
antMatchers
를 통해 어떤 API에 어떤 사용자를 허가할 것인지 결정합니다.
addFilterBefore
에는jwtTokenFilter
를 적용하여Token
의 유효성을 검사합니다.
passwordEncoder()
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
password
를 암호화하는 기능으로@Bean
으로 등록하여 사용이 용이하도록 합니다.
securityFilterChain()
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.csrf().disable()
.authorizeRequests()
.antMatchers("/api/v1/**").hasRole("ADMIN")
.antMatchers("/api/v1/**").authenticated()
.antMatchers("/api/v1/**").permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(this.jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
예전에는
WebSecurityConfigurerAdapter
를 상속받아 구현했었는데, 현재는Deprecated
되어SecurityFilterChain
을@Bean
으로 등록해서 사용해야합니다.기본적인 형태는 위와 같고,
여기에antMatchers()
를 통해 필요한 조건을 추가합니다.
antMatchers()
// 게시글 관련 GET 요청에 대해 아무나 승인
.antMatchers(HttpMethod.GET, "/api/v1/post/**").permitAll()
// 게시글 관련 POST 요청에 대해 승인된 모든 사용자 허용
.antMatchers(HttpMethod.POST, "/api/v1/post/**").authenticated()
// 모든 API 요청에 대해 승인된 사용자 중 ADMIN 권한이 있는 사용자만 허용
.antMatchers("/api/v1/**").hasRole("ADMIN")
// 모든 API 요청에 대해 승인된 모든 사용자 허용
.antMatchers("/api/v1/**").authenticated()
// 모든 API 요청에 대해 모든 사용자 허용
.antMatchers("/api/v1/**").permitAll()
여기서 승인된(authenticated) 사용자라는 것은
로그인하여 Token을 발급받은 사용자 요청을 의미합니다.
antMatchers()
를 설정할 때는 순서에 유의하여야 합니다.
조건의 적용 순서는 위에서부터 아래로 흘러가며 접근 조건을 확인합니다.
잘못된 예 (X)
// 모든 API 요청에 대해 모든 사용자 허용
.antMatchers("/api/v1/**").permitAll()
// 관리자 관련 API 요청에 대해 ADMIN 권한을 가진 사용자만 허용
.antMatchers("/api/v1/admin/**").hasRole("ADMIN")
// 게시글 관련 GET 요청에 대해 아무나 승인
.antMatchers(HttpMethod.GET, "/api/v1/post/**").permitAll()
// 게시글 관련 POST 요청에 대해 승인된 모든 사용자 허용
.antMatchers(HttpMethod.POST, "/api/v1/post/**").authenticated()
올바른 예 (O)
// 관리자 관련 API 요청에 대해 ADMIN 권한을 가진 사용자만 허용
.antMatchers("/api/v1/admin/**").hasRole("ADMIN")
// 게시글 관련 POST 요청에 대해 승인된 모든 사용자 허용
.antMatchers(HttpMethod.POST, "/api/v1/post/**").authenticated()
// 모든 API 요청에 대해 모든 사용자 허용
.antMatchers("/api/v1/**").permitAll()
예를 들어
잘못된 예(X)
의 경우,
제일 먼저/api/v1/**
으로 시작하는 모든 요청에 대해 모든 사용자를 허용하므로 이후에 나오는/api/v1/admin/**
,/api/v1/post/**
요청 조건은 무시되고, 로그인하지 않은 사용자도 글을 작성하거나 관리자 페이지에 접근할 수 있게 됩니다.반면
올바른 예(O)
의 경우,
/api/v1/admin/**
을 먼저 확인하고,/api/v1/post/**
를 확인한 뒤, 어디에도 해당이 안 된다면, 그 외 다른 모든 요청에 대해서 모든 사용자를 허용하게 됩니다.
즉, 로그인을 헀더라도
ADMIN
권한이 없으면 관리자 페이지에 접근할 수 없고,
로그인을 해야만 게시글 관련하여 게시글 작성과 같은POST
요청을 보낼 수 있게 되며, 그 외에 메인 페이지나 회원가입, 로그인 페이지 등은 모든 사용자가 접근할 수 있게 됩니다.
조건이 많아지게 되면 점점 조건의 순서를 정하는 것이 헷갈리게 되는데,
조건의 순서를 정하는 좋은 방법은 중요도가 높은 순서대로 작성하는 것입니다.