๐Ÿ”’ Springย security

jijiยท2023๋…„ 11์›” 24์ผ
0

Spring Boot Project ๐ŸŒฑ

๋ชฉ๋ก ๋ณด๊ธฐ
16/16

์Šคํ”„๋ง๋ถ€ํ„ฐ 3.0 ๋ถ€ํ„ฐ ์‹œํ๋ฆฌํ‹ฐ ์„ค์ • ๋ฐฉ๋ฒ•์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
(Springย security 6.0 ๋ฒ„์ „ ๋ถ€ํ„ฐ)
ํ˜„์žฌ ํ”„๋กœ์ ํŠธ : v 5.7.8

  • ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” Bean์ข…๋ฅ˜
@Bean
public UserDetailsService userDetailsService()

@Bean
public PasswordEncoder passwordEncoder()

@Bean
public SecurityFilterChain filterChain(HttpSecurity http)

@Bean
public WebSecurityCustomizer webSecurityCustomizer()

Gradle

implementation 'org.springframework.boot:spring-boot-starter-security'

์˜์กด์„ฑ ์ถ”๊ฐ€ ์‹œ ๋ชจ๋“  ๊ฒฝ๋กœ์—์„œ ๋กœ๊ทธ์ธํ™”๋ฉด์ด ๋ณด์ด๊ฒŒ ๋œ๋‹ค.

โ— Application Class์— ์ถ”๊ฐ€ํ•˜๋ฉด Security ๊ธฐ๋Šฅ์„ ๋Œ ์ˆ˜ ์žˆ๋‹ค.

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)

๋น„ํšŒ์›/ํšŒ์›์˜ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€์— ๋Œ€ํ•ด Spring Security ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž!


๐Ÿค– Spring Security ์„ค์ •

  • ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์„ filterChain์œผ๋กœ ์ง€์ •ํ•˜๋ฉด Spring์ด ํ•ด๋‹น ๋นˆ์„ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜๊ณ  ์ฒ˜๋ฆฌ
  • @Configuration, @EnableWebSecurity ์–ด๋…ธํ…Œ์ด์…˜ ์„ค์ •
    : ๋ณด์•ˆ ์„ค์ • ํ™œ์„ฑํ™”
  • Spring Security์˜ ๋Œ€๋ถ€๋ถ„์˜ ์„ค์ •์€ HttpSecurity๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.
    • URL ์ ‘๊ทผ ๊ถŒํ•œ ์„ค์ •๋ถ€ํ„ฐ ์ธ์ฆ
    • ๋กœ๊ทธ์•„์›ƒ ํŽ˜์ด์ง€ ์ธ์ฆ ์„ฑ๊ณต ๋ฐ ์‹คํŒจ ์‹œ ํŽ˜์ด์ง€ ์ด๋™
    • ์ปค์Šคํ…€ ์ธ์ฆ ๋กœ์ง์€ ์œ„ํ•œ ์ปค์Šคํ…€ ํ•„ํ„ฐ ์„ค์ •
    • csrf ๋ณดํ˜ธ, https ๊ฐ•์ œ ํ˜ธ์ถœ ๋“ฑ

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป code

JwtAuthFilter

// ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ „์†กํ•œ ํ† ํฐ์„ ๊ฒ€์‚ฌํ•˜๋Š” ํ•„ํ„ฐ
@Component
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
  private final TokenProvider tokenProvider;

  // ํ•„ํ„ฐ๊ฐ€ ํ•ด์•ผํ•  ์ž‘์—…์„ ๊ธฐ์ˆ 
  @Override
  protected void doFilterInternal(
      HttpServletRequest request,
      HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {

    try {
      String token = parseBearerToken(request);
      log.info("Jwt Token Filter is running... - token: {}", token);

      // ํ† ํฐ ์œ„์กฐ๊ฒ€์‚ฌ ๋ฐ ์ธ์ฆ ์™„๋ฃŒ ์ฒ˜๋ฆฌ
      if (token != null) {
        // ํ† ํฐ ์„œ๋ช…์œ„์กฐ ๊ฒ€์‚ฌ์™€ ํ† ํฐ์„ ํŒŒ์‹ฑํ•ด์„œ ํด๋ ˆ์ž„์„ ์–ป์–ด๋‚ด๋Š” ์ž‘์—…
        TokenUserInfo userInfo
            = tokenProvider.validateAndGetTokenUserInfo(token);
        // ์ธ๊ฐ€ ์ •๋ณด ๋ฆฌ์ŠคํŠธ
        List<SimpleGrantedAuthority> authorityList
            = new ArrayList<>();
//        authorityList.add(new SimpleGrantedAuthority("ROLE_" + userInfo.getRole().toString()));

        // ์ธ์ฆ ์™„๋ฃŒ ์ฒ˜๋ฆฌ
        // - ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—๊ฒŒ ์ธ์ฆ์ •๋ณด๋ฅผ
        //   ์ „๋‹ฌํ•ด์„œ ์ „์—ญ์ ์œผ๋กœ ์•ฑ์—์„œ
        //   ์ธ์ฆ์ •๋ณด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •
        AbstractAuthenticationToken auth
            = new UsernamePasswordAuthenticationToken(
            userInfo, // ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ™œ์šฉํ•  ์œ ์ €์ •๋ณด
            null, // ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ - ๋ณดํ†ต ๋„๊ฐ’
            authorityList // ์ธ๊ฐ€ ์ •๋ณด(๊ถŒํ•œ ์ •๋ณด)
        );
        // ์ธ์ฆ์™„๋ฃŒ ์ฒ˜๋ฆฌ์‹œ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ •๋ณด ์„ธํŒ…
        auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์ปจํ…Œ์ด๋„ˆ์— ์ธ์ฆ์ •๋ณด๊ฐ์ฒด ๋“ฑ๋ก
        SecurityContextHolder.getContext().setAuthentication(auth);
      }
    } catch (Exception e) {
      e.printStackTrace();
      log.error("ํ† ํฐ์ด ์œ„์กฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    }

    // ํ•„ํ„ฐ ์ฒด์ธ์— ๋‚ด๊ฐ€ ๋งŒ๋“  ํ•„ํ„ฐ ์‹คํ–‰๋ช…๋ น
    filterChain.doFilter(request, response);
  }

  private String parseBearerToken(HttpServletRequest request) {
    // ์š”์ฒญ ํ—ค๋”์—์„œ ํ† ํฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    // http request header
    // -- Content-type : application/json
    // -- Authorization : Bearer dshlkfsdhfiue#$23hdshf
    String bearerToken = request.getHeader("Authorization");

    // ์š”์ฒญ ํ—ค๋”์—์„œ ๊ฐ€์ ธ์˜จ ํ† ํฐ์€ ์ˆœ์ˆ˜ํ† ํฐ ๊ฐ’์ด ์•„๋‹ˆ๋ผ
    // ์•ž์— Bearer ๊ฐ€ ๋ถ™์–ด์žˆ์œผ๋‹ˆ ์ด๊ฒƒ์„ ์ œ๊ฑฐํ•˜๋Š” ์ž‘์—…
    if (StringUtils.hasText(bearerToken)
        && bearerToken.startsWith("Bearer")) {
      return bearerToken.substring(7);
    }
    return null;
  }
}

WebSecurityConfig

@EnableWebSecurity
@RequiredArgsConstructor
// ์ž๋™ ๊ถŒํ•œ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ •
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
  private final JwtAuthFilter jwtAuthFilter;
  // ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    http
        .cors()
        .and()
        .csrf().disable()
        .httpBasic().disable()
        // ์„ธ์…˜์ธ์ฆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฒ ๋‹ค
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        // ์–ด๋–ค ์š”์ฒญ์—์„œ ์ธ์ฆ์„ ์•ˆํ•  ๊ฒƒ์ธ์ง€ ์„ค์ •, ์–ธ์ œ ํ•  ๊ฒƒ์ธ์ง€ ์„ค์ •
        .authorizeRequests()
        .antMatchers(HttpMethod.PUT, "/api/auth/promote").authenticated()
        .antMatchers("/api/auth/load-profile").authenticated()
        .antMatchers("/", "/api/auth/**").permitAll()
//                    .antMatchers(HttpMethod.POST, "/api/todos").hasRole("ADMIN")
        .anyRequest().authenticated()
    ;

    // ํ† ํฐ์ธ์ฆ ํ•„ํ„ฐ ์—ฐ๊ฒฐ
    http.addFilterAfter(
        jwtAuthFilter
        , CorsFilter.class // import์ฃผ์˜: ์Šคํ”„๋ง๊บผ
    );

    return http.build();
  }
}

0๊ฐœ์˜ ๋Œ“๊ธ€