inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

스프링부트 시큐리티 & JWT 강의

권한 인증 403가 뜹니다

1808

성부근

작성한 질문수 1

0

https://github.com/bgseong/Security-test

 

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private TokenService tokenService;

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, TokenService tokenService) {
        super(authenticationManager);
        this.tokenService = tokenService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        super.doFilterInternal(request, response, chain);

        String token = tokenService.resolveToken(request);

        if(token == null){
            chain.doFilter(request, response);
            return;
        }

        if (tokenService.validateToken(token)) {
            Authentication authentication = tokenService.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            System.out.println(SecurityContextHolder.getContext().getAuthentication());
        }

        chain.doFilter(request,response);
    }
}
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    @Autowired
    TokenService tokenService;

    @Autowired
    CorsConfig corsConfig;

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http
                .csrf().disable()
                .httpBasic().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .formLogin().disable()
                .apply(new MyCustomDsl())

                .and()
                .authorizeHttpRequests(authorize ->
                        authorize
                                .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // 특정 정적 리소스 허용
                                .requestMatchers("/api/v1/user/**").hasAnyRole("ADMIN", "MANAGER")
                                .requestMatchers("/api/v1/manager/**").hasRole("ADMIN")
                                .requestMatchers("/api/v1/admin/**").hasRole("ROLE_ADMIN")
                                .anyRequest().permitAll());

        return http.build();


    }

    public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
            http
                    .addFilter(corsConfig.corsFilter())
                    .addFilter(new LoginFilter(authenticationManager,tokenService))
                    .addFilter(new JwtAuthorizationFilter(authenticationManager,tokenService));

        }
    }
}
@Component
public class TokenService implements InitializingBean {
    private final UserRepository usersrepository;

    private final Logger logger = LoggerFactory.getLogger(TokenService.class);
    private static final String AUTHORITIES_KEY = "auth";
    private final String secret;
    private final long accessTokenValidityInMilliseconds;

    private final long refreshTokenValidityInMilliseconds;

    public static final String AUTHORIZATION_HEADER = "Authorization";
    public static final String REFRESHTOKEN_HEADER = "RefreshToken";
    private Key key;

    public TokenService(
            UserRepository usersrepository, @Value("${spring.jwt.secret}") String secret,
            @Value("${spring.jwt.token-validity-in-seconds}") long tokenValidityInSeconds) {
        this.usersrepository = usersrepository;
        this.secret = secret;
        this.accessTokenValidityInMilliseconds = tokenValidityInSeconds * 500;
        this.refreshTokenValidityInMilliseconds = tokenValidityInSeconds * 1000 * 336;
    }

    @Override
    public void afterPropertiesSet() {
        byte[] keyBytes = Decoders.BASE64.decode(secret);
        this.key = Keys.hmacShaKeyFor(keyBytes);
    }


    public String createAccessToken(PrincipalDetails principalDetails) {
        return createAccessToken(principalDetails.getUser().getEmail(), principalDetails.getAuthorities());
    }

    public String createRefreshToken(PrincipalDetails principalDetails) {
        return createRefreshToken(principalDetails.getUser().getEmail(), principalDetails.getAuthorities());
    }

    public String createAccessToken(String email, Collection<? extends GrantedAuthority> inputAuthorities) {
        String authorities = inputAuthorities.stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));

        long now = (new Date()).getTime();

        String accessToken = Jwts.builder()
                .setSubject(email)
                .claim(AUTHORITIES_KEY, authorities)
                .signWith(key, SignatureAlgorithm.HS512)
                .setExpiration(new Date(now + this.accessTokenValidityInMilliseconds))
                .compact();

        return accessToken;
    }

    public String createRefreshToken(String email, Collection<? extends GrantedAuthority> inputAuthorities) {
        String authorities = inputAuthorities.stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));

        long now = (new Date()).getTime();

        String Token = Jwts.builder()
                .setSubject(email)
                .claim(AUTHORITIES_KEY, authorities)
                .signWith(key, SignatureAlgorithm.HS512)
                .setExpiration(new Date(now + this.refreshTokenValidityInMilliseconds))
                .compact();

        return Token;
    }

    public Authentication getAuthentication(String token) {
        Claims claims = Jwts
                .parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();

        User user = usersrepository.findByEmail(claims.get("sub",String.class));

        Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

        PrincipalDetails principal = new PrincipalDetails(user);

        return new UsernamePasswordAuthenticationToken(principal, null, authorities);
    }

    public String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);

        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }

        return null;
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            logger.info("worng JWT sign");
        } catch (ExpiredJwtException e) {
            logger.info("expire JWT");
        } catch (UnsupportedJwtException e) {
            logger.info("No support JWT");
        } catch (IllegalArgumentException e) {
            logger.info("JWT is worng");
        }
        return false;
    }

}

이렇게 구성해 놨습니다. 그런데 모든 권한이 적용된 url에 접근을 하면 403 에러가 뜹니다.

필터에서 SecurityContextHolder를 출력하면 아래와 같이 출력이 되는 걸 확인했고

[Principal=com.securitytest.Securitytest.auth.PrincipalDetails@45095607, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_ADMIN]]

컨트롤러에서 PrincipalDetails를 호출해보니, null이라서 오류가 난다고 뜹니다. 무엇이 문제일까요ㅠㅠ..

spring spring-security jwt

답변 2

0

볼록볼록한 랫서팬더

코드를 보니 권한은 ADMIN 이라고 쓴 곳도 있고 ROLE_ADMIN 이라고 쓴 곳도 보이네요.

예를 들어 SecurityConfig 클래스 안에 SecurityFilterChain 를 보면,

권한 설정 하는 부분이 그러합니다. User.role 에 저장되는 권한 이름이 ADMIN 이라면 ADMIN, USER 등으로

권한을 설정하시고, ROLE_ADMIN, ROLE_USER 라면 이런 식으로 설정하시면 되겠습니다.

이 부분을 먼저 확인해보시겠어요 ?

0

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴이에요. 권한 인증 403 에러는 클라이언트가 요청한 리소스에 대한 접근 권한이 없을 때 발생하는 오류입니다.

403 에러가 발생하는 이유는 여러 가지가 있을 수 있는데요, 주로 다음과 같은 이유로 발생할 수 있습니다.

  1. 권한 설정 오류: 서버에서 요청한 리소스에 대한 접근 권한 설정이 제대로 이루어지지 않은 경우 403 에러가 발생할 수 있습니다. 해당 요청에 대한 접근 권한을 확인해보세요.

  2. 인증 토큰 오류: 인증 토큰이 올바르지 않거나 만료된 경우에도 403 에러가 발생할 수 있습니다. 토큰의 유효성을 확인하고, 만료되었다면 새로운 토큰을 발급받아 사용해보세요.

  3. CORS(Cross-Origin Resource Sharing) 설정 오류: 다른 도메인에서 온 요청에 대해 서버가 허용하지 않는 경우에도 403 에러가 발생할 수 있습니다. CORS 설정을 확인해보세요.

또한, 이 외에도 다른 원인일 수 있으니 더 자세한 정보가 필요할 경우에는 로그를 확인하거나 디버깅을 통해 원인을 찾아보세요.

해당 코드를 보면 JwtAuthorizationFilter 클래스와 SecurityConfig 클래스가 있는데, 이 부분에서 권한 인증과 관련된 설정이 이루어지고 있을 것입니다. 관련 설정을 확인해보시고 오류를 해결해보세요.

더 자세한 도움이 필요하시다면, 코드의 다른 부분이나 로그 정보 등을 함께 공유해주시면 더 정확한 도움을 드릴 수 있을 거예요. 수고하세요!

JWT를 구현한 다음 이 API를 호출해서 사용하는 것은 프론트엔드 쪽에서 하는 역할인가요?

0

98

1

Jwt쓰면 스프링시큐리티는 필수적으로 사용해야하나요?

0

401

1

13:23 system.out 출력문이 다르게 나옵니다.

0

130

1

수료증 문의

0

228

2

9분대에 질문이 있습니다 !

0

115

1

password 비교를 하지 않았는데 어떻게 인증이 통과된 건가요?

0

321

1

이전 강의 참고하라는 말씀

0

253

1

강의 실습하다가 막히는 분들 참고(2024년8월 기준)

2

1116

2

구글 소셜 로그인 302

0

201

1

오류 문의 _ org.springframework.orm.jpa.JpaSystemException: could not deserialize

1

585

1

[자바] 시큐리티 Config 참고

13

953

1

이론강의

0

280

1

SpringSecurity JWT 로그인 URL 2개 설정하는 방법

0

488

1

2024.06기준) 최근 SecurityConfig 설정 문의

0

922

3

구글 로그인시 authentication이 null 값이라고 에러가 발생합니다.

0

678

2

특정 url필터 거는 방법 이슈

0

422

1

강사님께서 말씀하시는 시큐리티세션이 SecurityContext인가요?

0

279

1

25강 마지막 테스트에서 오류

1

1044

2

jwt를 저장하는 위치에 궁금한 점이 있습니다.

0

298

1

mustache를 사용하지 않고 thymeleaf를 사용하려고 하는데

0

697

1

세션 인증방식이 REST 원칙에 위배되는 건가요?

0

342

1

jwt와 실제데이터의 관계

1

246

1

jwt 와 세션ID의 관계

1

313

1

SecurityConfig에서 세션 설정, 인가 설정

0

422

1