Spring Security 에서, 403 Forbidden

TIP

난 잘못한게 없는데, 403이 떴다! 싶을 때, 참고하세요.
case를 늘려가면서 추가할 예정입니다.

Config 설정 돌아보기

spring security는 uri 별로 권한을 지정해 줄 수 있습니다다.
따라서, uri가 잘못 되었을 때나 해당 uri에 권한 지정이 잘못 되었을 때, 문제가 될 수 있습니다.
아래의 코드에서 예시 상황을 들어보겠습니다.
만약 uri가 /main인 화면은 권한과 상관없이 모든 사용자가 볼 수 있어야 한다고 가정해 봅시다.
그렇다면, /main은 permitAll()이 되어야 합니다. 모든 사용자가 볼 수 있어야 하니까요.
하지만, /main이라는 uri는 아래 소스에 존재하지 않지요. 따라서, /main은 anyRequest 조건에 걸리게 되고, USER가 아닌 사람들에게는 403이 뜨게 됩니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .httpBasic().disable() // rest api 이므로 기본설정 사용안함. 기본설정은 비인증시 로그인폼 화면으로 리다이렉트 된다.
      .csrf().disable() // rest api이므로 csrf 보안이 필요없으므로 disable처리.
      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt token으로 인증하므로 세션은 필요없으므로 생성안함.
      .and()
          .authorizeRequests() // 다음 리퀘스트에 대한 사용권한 체크
              .antMatchers("/signin", "/signup", "/login").permitAll() // 가입 및 인증 주소는 누구나 접근가능
              .antMatchers("/test").hasAnyRole("USER", "ADMIN")
              .anyRequest().hasRole("USER") // 그외 나머지 요청은 모두 인증된 회원만 접근 가능
      .and()
          .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); // jwt token 필터를 id/password 인증 필터 전에 넣는다
}
// 소스 코드 출처: https://sybarits.github.io/2020/11/08/rest-api-security.html









 





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Filter 들여다 보기

위 소스코드의 마지막을 보면, JwtAuthenticationFilter가 Filter로 등록됩니다.
이 필터의 소스는 아래와 같습니다.

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        // 토큰 정보 추출
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
        // 토큰이 유효하면, 인증 정보 추출
        if (token != null && jwtTokenProvider.validateToken(token)) {
            // 추출된 인증 정보를 필터링에 사용할 수 있도록 context에 등록
            Authentication auth = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        // 필터링 수행
        filterChain.doFilter(request, response);
    }











 

1
2
3
4
5
6
7
8
9
10
11
12
13

위와 같이, 토큰으로 정보를 조회하고, 인증정보를 가져온 결과는 사진과 같습니다. 디버깅 포인트는 위 코드에서 하이라이트 된 12번 line입니다. 필터 디버깅 정보 정상적인 토큰에서 정보를 잘 추출 했고, 인증정보(authorities)를 보면 USER라는 권한이 잘 들어가 있습니다.
위 configure메서드에서 설정한 USER라는 권한과 토큰에서의 인증정보가 동일한 String(USER)을 가지고 있으니, 통과 될 것이라 생각 하기 쉽습니다.
저도 스프링 시큐리티를 처음 썼을 때, 그렇게 기대를 하고 썼다가 한참을 헤맸습니다.. ㅠㅠ
스프링 시큐리티는, config에서 정의한 권한이 USER이면, 토큰에서 추출한 권한은 ROLE_USER인지 확인을 합니다.
즉, ROLE_ 접두어가 자동으로 붙는 것입니다.
만약 유저의 이 권한이 DB에 저장되어있다면, 이 또한 역시 ROLE_USER로 저장되어 있어야 합니다.
별도의 권한 Mapper를 통해 접두어를 바꿀 수도 있다고 합니다.

정리하자면,

config 설정 시에는 ROLE_이 빠진 진짜 역할 이름을 적고, 토큰 안에 있는 정보는 ROLE_역할 형태로 저장되어 있어야 합니다. DB에서 권한을 불러 온다면, DB도 ROLE_역할형태로 저장되어 있어야 합니다.

Last Updated: 2021. 10. 13. 오후 2:58:35
Contributors: yongjun