1 minute read

jwt가 적용된 SpringBoot security에서 CORS 처리

  • 회사내부 프로젝트에서 cors 관련 이슈로 고생하는 동료와 함께 들여다 봄.
  • 교훈
    • 시큐리티 설정은 순서가 중요하다. Is the order matter? Yes!!!
    • 안된다고 이렇게 저렇게 코드를 휘젓지 말자. 나의 경우, 처음에 콘트롤러에서 @CrossOrigin가지고 이렇게 저렇게 하다가 누락되어 되지 않았던 경우였다. 당연히 되있겠거니 한것이다. 누가 바꿨냐면 바로 나. 그러나 기억못함.

      SecurityConfig.java

      ```java

import org.springframework.beans.factory.annotation.Value; 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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.CorsUtils; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.Filter;

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value(“${jwt.secret}”) private String secret;

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring()
            .antMatchers("/css/**", ....., "/img/**", "/js/**")
    ;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    Filter filter = new JwtAuthenticationFilter(authenticationManager(), jwtUtil());

    http
            .csrf().disable()
            .addFilter(filter)
            .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/init").hasRole(Role.NOT_ACTIVATE.name())
                .antMatchers("/admin/**").hasRole(Role.ADMIN.name())
                .
                .
                .
                .
                .
                .anyRequest().hasAnyRole(Role.USER.name(), Role.ADMIN.name())
            .and().cors().configurationSource(getConfig())
            .and().headers().frameOptions().sameOrigin()
            .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            ;
}

private CorsConfigurationSource getConfig() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.addAllowedOrigin("*");
    configuration.addAllowedMethod("*");
    configuration.addAllowedHeader("*");
    configuration.setAllowCredentials(true);
    configuration.setMaxAge(5000L);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

@Bean
public JwtUtil jwtUtil() {
    return new JwtUtil(secret);
} }




### SampleController.java
```java
@Slf4j
@RestController
@RequestMapping("/sample")
@CrossOrigin
public class SampleController  {

    private final AdminService adminService;

    public AdminController(final AdminService adminService) {
        this.adminService = adminService;
    }

    /**
     * 총 사원 수 조회
     *
     * @return
     */
    @GetMapping("/test")
    public String test() {

        return "Yes";
    }

import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    private JwtUtil jwtUtil;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtUtil jwtUtil) {
        super(authenticationManager);
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain)
            throws IOException, ServletException {
        Authentication authentication = getAuthentication(request);

        if (authentication != null) {
            SecurityContext context = SecurityContextHolder.getContext();
            context.setAuthentication(authentication);
        }

        chain.doFilter(request, response);
    }

    private Authentication getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(JwtConstants.AUTHORIZATION);
        if (token == null) {
            return null;
        }

        Claims claims = jwtUtil.getClaims(token.substring("Bearer ".length()));

        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();

        if (claims.containsKey(JwtConstants.ROLE)) {
            GrantedAuthority grantedAuthority
                    = new SimpleGrantedAuthority("ROLE_" + claims.get(JwtConstants.ROLE));
            grantedAuthorityList.add(grantedAuthority);
        }

        Authentication authentication =
                new UsernamePasswordAuthenticationToken(claims, null, grantedAuthorityList);

        logger.debug(authentication.toString());
        return authentication;
    }
}

jwt와 cors

Comments