Spring Boot RestApi 与 Spring Security,JWT 403 错误

问题描述 投票:0回答:1

我使用的是 Spring Boot 3.2 和 Spring Security 6.2,登录和注册控制器正常进行。即使返回了有效的令牌,但是如果您请求注册后控制器,则会出现空的 403 错误,如果您向尚未声明的控制器发送请求,则会出现空的 403 错误。我的目的是将 jwt 中包含的用户昵称作为作者存储,并将其传递给 dto 类。这是我的源代码。

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtUtils jwtUtils;
    private final JwtProvider jwtProvider;

    public SecurityConfig(JwtUtils jwtUtils, JwtProvider jwtProvider) {
        this.jwtUtils = jwtUtils;
        this.jwtProvider = jwtProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/v1/auth/**").permitAll()
                        .anyRequest().authenticated())
                .addFilterBefore(new JwtFilter(jwtProvider, jwtUtils), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(exception -> exception.authenticationEntryPoint(entryPoint));

        return http.build();
    }
}

@Component
public class JwtUtils {

    private final UserDetailsServiceImpl userDetailsService;
    private final RefreshTokenRepository refreshTokenRepository;
    private final JwtProvider jwtProvider;
    private final MemberRepository memberRepository;

    public JwtUtils(UserDetailsServiceImpl userDetailsService, RefreshTokenRepository refreshTokenRepository, JwtProvider jwtProvider, MemberRepository memberRepository) {
        this.userDetailsService = userDetailsService;
        this.refreshTokenRepository = refreshTokenRepository;
        this.jwtProvider = jwtProvider;
        this.memberRepository = memberRepository;
    }

    public String getHeaderToken(HttpServletRequest request, String type) {
        return type.equals("Access")
                ? request.getHeader(jwtProvider.getAccessTokenHeader())
                : request.getHeader(jwtProvider.getRefreshTokenHeader());
    }

    public Boolean tokenValidation(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(jwtProvider.getKey()).build().parseClaimsJws(token);
            return true;
        } catch (Exception ex) {
            log.error(ex.getMessage());
            return false;
        }
    }

    public Boolean refreshTokenValidation(String token) {
        if (!tokenValidation(token)) return false;

        Optional<RefreshToken> refreshToken = refreshTokenRepository.findByMemberEmail(getEmailFromToken(token));
        return refreshToken.isPresent() && token.equals(refreshToken.get().getRefreshToken());
    }

    public Authentication createAuthentication(String email) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(email);
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    public String getEmailFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(jwtProvider.getKey()).build().parseClaimsJws(token).getBody().getSubject();
    }

    public String getNicknameFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(jwtProvider.getKey()).build().parseClaimsJws(token).getBody().get("nickname", String.class);
    }

    public Member loadMemberByEmail(String email) {
        return memberRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException("User not found: " + email));
    }
}
@Component
public class JwtUtils {

    private final UserDetailsServiceImpl userDetailsService;
    private final RefreshTokenRepository refreshTokenRepository;
    private final JwtProvider jwtProvider;
    private final MemberRepository memberRepository;

    public JwtUtils(UserDetailsServiceImpl userDetailsService, RefreshTokenRepository refreshTokenRepository, JwtProvider jwtProvider, MemberRepository memberRepository) {
        this.userDetailsService = userDetailsService;
        this.refreshTokenRepository = refreshTokenRepository;
        this.jwtProvider = jwtProvider;
        this.memberRepository = memberRepository;
    }

    public String getHeaderToken(HttpServletRequest request, String type) {
        return type.equals("Access")
                ? request.getHeader(jwtProvider.getAccessTokenHeader())
                : request.getHeader(jwtProvider.getRefreshTokenHeader());
    }

    public Boolean tokenValidation(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(jwtProvider.getKey()).build().parseClaimsJws(token);
            return true;
        } catch (Exception ex) {
            log.error(ex.getMessage());
            return false;
        }
    }

    public Boolean refreshTokenValidation(String token) {
        if (!tokenValidation(token)) return false;

        Optional<RefreshToken> refreshToken = refreshTokenRepository.findByMemberEmail(getEmailFromToken(token));
        return refreshToken.isPresent() && token.equals(refreshToken.get().getRefreshToken());
    }

    public Authentication createAuthentication(String email) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(email);
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    public String getEmailFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(jwtProvider.getKey()).build().parseClaimsJws(token).getBody().getSubject();
    }

    public String getNicknameFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(jwtProvider.getKey()).build().parseClaimsJws(token).getBody().get("nickname", String.class);
    }

    public Member loadMemberByEmail(String email) {
        return memberRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException("User not found: " + email));
    }
}
@Service
@Transactional
public class BoardServiceImpl implements BoardService {

    private final BoardRepository boardRepository;

    public BoardServiceImpl(BoardRepository boardRepository) {
        this.boardRepository = boardRepository;
    }

    public void createBoard(BoardCreateRequestDto requestDto, String nickname) {

        Board board = Board.builder()
                .title(requestDto.getTitle())
                .content(requestDto.getContent())
                .category(requestDto.getCategory())
                .writer(nickname)
                .build();

        boardRepository.save(board);
    }
}
@Getter
@Setter
@NoArgsConstructor
public class BoardCreateRequestDto {

    private String title;

    private String content;

    private Category category;

    @Builder
    public BoardCreateRequestDto(String title, String content, Category category) {
        this.title = title;
        this.content = content;
        this.category = category;
    }
}
@RestController
@RequestMapping("/api/v1/board")
public class BoardController {

    private final JwtUtils jwtUtils;
    private final BoardService boardService;

    public BoardController(JwtUtils jwtUtils, BoardService boardService) {
        this.jwtUtils = jwtUtils;
        this.boardService = boardService;
    }

    @PostMapping("/new")
    public ResponseEntity<String> createBoard(@RequestBody BoardCreateRequestDto requestDto, HttpServletRequest request) {
        String token = jwtUtils.getHeaderToken(request, "Access");

        if (token != null && !token.isEmpty()) {
            String nickname = jwtUtils.getNicknameFromToken(token);

            boardService.createBoard(requestDto, nickname);

            return ResponseEntity.status(HttpStatus.CREATED).body("Board created");
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
        }
    }
}
@Log4j2
public class JwtFilter extends OncePerRequestFilter {

    private final JwtProvider jwtProvider;
    private final JwtUtils jwtUtils;

    public JwtFilter(JwtProvider jwtProvider, JwtUtils jwtUtils) {
        this.jwtProvider = jwtProvider;
        this.jwtUtils = jwtUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String accessToken = jwtUtils.getHeaderToken(request, "Access");
        String refreshToken = jwtUtils.getHeaderToken(request, "Refresh");

        log.debug(accessToken);
        log.debug(refreshToken);

        try {
            if (accessToken != null) {
                if (jwtUtils.tokenValidation(accessToken)) {
                    setAuthentication(jwtUtils.getEmailFromToken(accessToken));
                    log.debug("Access token valid. Authentication set.");
                } else if (refreshToken != null && jwtUtils.refreshTokenValidation(refreshToken)) {
                    refreshAccessToken(refreshToken, response);
                    log.debug("Refresh token valid. Access token refreshed");
                } else {
                    log.debug("Access token invalid. Sending error response.");
                    jwtExceptionHandler(response, "Access Token Expired or Invalid", HttpStatus.UNAUTHORIZED);
                    return;
                }
            } else if (refreshToken != null && jwtUtils.refreshTokenValidation(refreshToken)) {
                refreshAccessToken(refreshToken, response);
                log.debug("Refresh token valid. Access token refreshed");
            }
        } catch (Exception e) {
            log.error("Error during JWT validation: ", e);
            jwtExceptionHandler(response, "Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR);
            return;
        }

        filterChain.doFilter(request, response);
    }

    private void refreshAccessToken(String refreshToken, HttpServletResponse response) throws IOException {
        String email = jwtUtils.getEmailFromToken(refreshToken);
        Member member = jwtUtils.loadMemberByEmail(email);
        String newAccessToken = jwtProvider.createToken(
                new TokenPayload(email, member.getRole().name(), member.getNickname()), "Access"
        );
        jwtProvider.setHeaderAccessToken(response, newAccessToken);
        setAuthentication(email);
    }

    private void setAuthentication(String email) {
        Authentication authentication = jwtUtils.createAuthentication(email);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    private void jwtExceptionHandler(HttpServletResponse response, String msg, HttpStatus status) {
        response.setStatus(status.value());
        response.setContentType("application/json");
        try {
            String json = new ObjectMapper().writeValueAsString(Collections.singletonMap("error", msg));
            response.getWriter().write(json);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

我尝试设置crsf并尝试了所有能做的事情,但没有成功。

spring-boot spring-security jwt http-status-code-403
1个回答
0
投票

本可以将其保留为评论,但我超出了字符限制-

代码看起来很合理,虽然有点复杂 - 关键是检查

SecurityContextHolder.getContext().setAuthentication(authentication)
是否被正确调用,看起来确实如此。

为了安心,您还可以根据您的代码检查

authentication.isAuthenticated()
是否为真(它应该是真的)。

您可以在您的

logging.level.org.springframework.security=TRACE
中尝试
application.properties
- 这可以给您一些见解 - 也许过滤器链下游的过滤器正在扰乱身份验证。

作为实验,您可以尝试在

LogoutFilter
之后而不是在
UsernamePasswordAuthenticationFilter
之前添加过滤器,例如

  .addFilterAfter(new JwtFilter(jwtProvider, jwtUtils), LogoutFilter.class)
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.