java - 实现 Spring security Authentication 后,我的应用程序不返回正确的正文,仅返回空的 200 HTTP 响应

标签 java spring spring-boot spring-security

我已经实现了一些简单的方法来在 springboot 中对用户进行身份验证,该方法返回 jwt 并验证用户,但应用程序停止使用实际主体提供响应并开始发送空的 200 个响应,有任何线索说明为什么会出现这种情况吗?根据调试器,程序永远不会结束我试图调用的请求,但使用 jwt 的授权总是成功的

我的类(class)

JWTService

@Service
public class JwtService {
private static final String SECRET_KEY = "32bitkey";
public String extractEmail(String token) {
return exctractClaim(token, Claims::getSubject);
}

    public <T> T exctractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    
    public String generateToken(
            UserDetails userDetails) {
        return generateToken(Map.of(), userDetails);
    }
    public String generateToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails) {
        return Jwts
                .builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }
    
    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String email = extractEmail(token);
        return email.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }
    
    public boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
    
    private Date extractExpiration(String token) {
       return exctractClaim(token, Claims::getExpiration);
    }
    
    private Claims extractAllClaims(String token) {
        return Jwts
                .parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
    private Key getSigningKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);
    }

安全过滤器链

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
        security
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .requestMatchers(
                        "/fixture/getFixturesBySportAndDate",
                        "/user/register",
                        "/user/authenticate",
                        "/league/getLeaguesByFixturePlayedAtDateInSport",
                        "/team/fillTeamsHockey",
                        "/fixture/fillFixturesHockey",
                        "/fixture/fillFixturesBasketball")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthetificationFilter, UsernamePasswordAuthenticationFilter.class);

        return security.build();
    }

JwtAuthFilter

@Component
@RequiredArgsConstructor
public class JwtAuthetificationFilter extends OncePerRequestFilter {
    private final JwtService jwtService;
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain
    ) throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");
        final String jwt;
        final String email;
        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }
        jwt = authorizationHeader.substring(7);
        email = jwtService.extractEmail(jwt);
        if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
            UserDetails userDetails = userDetailsService.loadUserByUsername(email);
            if (jwtService.isTokenValid(jwt, userDetails)){
                UsernamePasswordAuthenticationToken authenticationToken
                        = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
    }

应用程序配置

@Configuration @RequiredArgsConstructor
public class ApplicationConfig {

    private final UserRepository userRepository;

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        };

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

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

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

请求示例

    @GetMapping(value = "/getUserInfo")
    public ResponseEntity<User> getUserInfo(HttpServletRequest request){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String email = authentication.getName();
        User user = userRepository.findByEmail(email).get();
        return ResponseEntity.ok(user);
    }

我尝试基本上更改所有内容并调试信息,但无法完成,因为它只是给了我请求永远不会执行的答案

最佳答案

我根本没有尝试运行您的代码,但乍一看,您在 JwtAuthenticationFilter 中设置安全上下文的身份验证后似乎没有调用过滤器链(尽管如果没有授权 header ,您会这样做)。

尝试调整此:

...
        if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
            UserDetails userDetails = userDetailsService.loadUserByUsername(email);
            if (jwtService.isTokenValid(jwt, userDetails)){
                UsernamePasswordAuthenticationToken authenticationToken
                        = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

调用链中的下一个过滤器:

        if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
            UserDetails userDetails = userDetailsService.loadUserByUsername(email);
            if (jwtService.isTokenValid(jwt, userDetails)){
                UsernamePasswordAuthenticationToken authenticationToken
                        = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        // causes the next filter in the chain to be invoked
        filterChain.doFilter(request, response);

以下是 FilterChain header 中对 doFilter 方法的注释:

    /**
     * Causes the next filter in the chain to be invoked, or if the calling
     * filter is the last filter in the chain, causes the resource at the end of
     * the chain to be invoked.
     *
     * @param request
     *            the request to pass along the chain.
     * @param response
     *            the response to pass along the chain.
     *
     * @throws IOException if an I/O error occurs during the processing of the
     *                     request
     * @throws ServletException if the processing fails for any other reason
     */
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;

关于java - 实现 Spring security Authentication 后,我的应用程序不返回正确的正文,仅返回空的 200 HTTP 响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76492486/

相关文章:

java - 我可以在接口(interface)上使用@MappedSuperclass 注解吗?

java - 获取存储库中的所有文件夹和文档 Alfresco Restful

java - 使用 Spring Data JPA @LastModifiedDate 从审计中排除某些字段

java - 对于以下用例,我应该使用哪种设计模式(优雅的方式)?

java - 在 IntelliJ 中每次 git pull 后生成的源根消失了

java源代码刽子手

java - 没有 @Validated 的 Spring JSR303/349 验证

spring - 如何在不使用 Spring Boot 的情况下注入(inject) Feign 客户端并调用 REST 端点

spring - @Autowired 对象在一个类中获得空值,而在另一个类中成功连接

java: lombok: @Slf4j: 日志无法解析