簡體   English   中英

Spring Boot 不接收從 Angular 客戶端添加的請求標頭

[英]Spring boot doesn't receive request headers added from Angular client

在我的演示應用程序中,我遇到了一個問題,即我沒有收到從 Angular 客戶端添加到 Spring Boot 服務器的請求標頭。 作為安全措施,我有 SSL(安全套接字層)和 CORS(跨源資源共享)配置。 在我的應用程序中 CSRF(跨站點請求偽造)被禁用。 我使用 JWT(JSON Wen Token)作為每個請求的用戶認證機制。 這就是我需要從標頭中提取 JWT 密鑰的地方。 我將從我的應用程序中添加代碼示例,請幫助我找出問題所在。

角度攔截器

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpResponse, HttpHandler, HttpRequest, HttpHeaders } from '@angular/common/http'; 
import { UserService } from './user.service';
import { Observable } from 'rxjs';

/*Interceptor
  Often you’ll want to intercept HTTP requests or responses before they’re handled else where in the application*/ 

@Injectable({
  providedIn: 'root'
})
export class InterceptorService implements HttpInterceptor {

  /**
   * Constructor of InterceptorService. 
   * @param userService UserService
   */
  constructor(private userService: UserService) { }

  /**
   * Responsible for intercepting requests. 
   * Responsible for adding 'Authorization' header with JWT (Json Web Token). 
   * @param theRequest HttpRequest<any>
   * @param handler HttpHandler
   */
  intercept(theRequest: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {
    debugger;
    const jwtKey = this.userService.getJwtString();
    const authReq = theRequest.clone({
      headers: new HttpHeaders({
        'Content-Type':  'application/json',
        'Authorization': `Bearer ${jwtKey}`
      })
    });
    console.log('Intercepted HTTP call', authReq);
    return handler.handle(authReq);
  }

}

角度用戶服務

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router';
import { AuthenticationConfigurationService } from './authentication-configuration.service';
import { User, AuthenticationResponse } from './data';

//Service for users 

@Injectable({
  providedIn: 'root'
})
export class UserService {
    //Attributes
  private jwtString: string = ''; //JWT (Jason Web Token)
  private isUserLoggedIn = false; 

  /**
   * Constructor of UserService. 
   * @param router Router
   * @param httpClient HttpClient
   */
  constructor(private router: Router, private httpClient: HttpClient) { }

  /**
   * Executes upon user login. 
   * @param theUser User 
   */
  login(theUser: User) {
    const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) };
      this.httpClient.post(AuthenticationConfigurationService.getAuthenticationURL('authenticationURL') + '/getjwt', theUser, httpOptions)
                .subscribe((response: AuthenticationResponse) => {
                    this.jwtString = response.jwt;
          this.isUserLoggedIn = true;
                });
  }

  /**
   * Executes upon user logout. 
   */
  logout() {;
    this.isUserLoggedIn = false; 
    this.jwtString = '';
    this.navigateToLoginPage();
  }

  /**
   * Responsible for returning the JWT (Json Web Token) string.
   * @returns string
   */
  getJwtString(): string {
    return this.jwtString; 
  }

  /**
   * Responsible for returning whether the user is logged-in. 
   * @returns boolean
   */
  userLoggedIn(): boolean {
    return this.isUserLoggedIn;
  }

  /**
   * Navigate to the login page. 
   */
  private navigateToLoginPage() {
    this.router.navigateByUrl('/login');
  }

}

春季啟動安全

package com.example.LibraryServer.Security;

import com.example.LibraryServer.Filter.Security.JwtRequestFilter;
import com.example.LibraryServer.Services.LibraryUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/**
 * Class responsible for security configurations.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //Attributes
    private final LibraryUserDetailsService libraryUserDetailsService;
    private final JwtRequestFilter jwtRequestFilter;

    /**
     * Constructor of SecurityConfig.
     * @param theLibraryUserDetailsService LibraryUserDetailsService
     * @param theJwtRequestFilter JwtRequestFilter
     */
    @Autowired
    public SecurityConfig(LibraryUserDetailsService theLibraryUserDetailsService, JwtRequestFilter theJwtRequestFilter) {
        this.libraryUserDetailsService = theLibraryUserDetailsService;
        this.jwtRequestFilter = theJwtRequestFilter;
    }

    /**
     * Responsible for user security configuration.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @param theHttpSecurity HttpSecurity
     * @throws Exception - Exception upon security configuration.
     */
    @Override
    protected void configure(HttpSecurity theHttpSecurity) throws Exception {
        //theHttpSecurity.authorizeRequests()
                //.antMatchers("/**").access("permitAll") //Allow all paths
                /*.and().cors()*/
                //.and().csrf().disable(); //Allow all requests - CSRF (Cross-Site Request Forgery)
        theHttpSecurity.csrf().disable() //Allow all requests - CSRF (Cross-Site Request Forgery)
                .authorizeRequests().antMatchers("/authenticate/**").access("permitAll") //Allow all paths
                .anyRequest().authenticated() //All other paths need authentication
                /*Inform spring security not to manage sessions.
                  All requests will be filtered via 'JwtRequestFilter' with JWT and does not need sessions.*/
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //Inform spring security about the filter 'JwtRequestFilter' for username and password authentication.
        theHttpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * Responsible for configuring user-store.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @param theAuthentication AuthenticationManagerBuilder
     * @throws Exception - Exception upon user store creation.
     */
    @Override
    public void configure(AuthenticationManagerBuilder theAuthentication) throws Exception {
        //theAuthentication.inMemoryAuthentication()
                //.withUser("sankalpa")
                //.password("{noop}123")
                //.authorities("ROLE_USER");
        theAuthentication.userDetailsService(libraryUserDetailsService);
    }

    /**
     * Method constructing AuthenticationManager bean.
     * This method is needed since AuthenticationManager is being used in 'HelloController'.
     * Therefore this bean should be in spring application context.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @return AuthenticationManager
     * @throws Exception - Exception upon execution.
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * Method constructing a password encoder bean.
     * Constructs 'NoOpPasswordEncoder'.
     * @return PasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

}

Spring Boot CORS 配置類

package com.example.LibraryServer.CORS;

import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Configuration class which is responsible for handling CORS.
 * Cross-Origin Resource Sharing (CORS) configuration class.
 */
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {

    /**
     * Responsible for CORS mapping.
     * Overridden from WebMvcConfigurer level.
     * @param theRegistry CorsRegistry
     */
    @Override
    public void addCorsMappings(@NotNull CorsRegistry theRegistry) {
        //End points
        var authorizedEndpoints = new String[] {
                "/book/**",
                "/author/**",
                "/authenticate/**"
        };

        //Add mapping
        for (var endPoint : authorizedEndpoints) {
            theRegistry.addMapping(endPoint)
                    .allowedOrigins("http://localhost:4200")
                    .allowedMethods("*") //"HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"
                    .allowedHeaders("*")
                    .allowCredentials(true)
                    .exposedHeaders("Authorization");
        }
    }

}

彈簧靴過濾器

package com.example.LibraryServer.Filter.Security;

import com.example.LibraryServer.Services.LibraryUserDetailsService;
import com.example.LibraryServer.Uilities.JsonWebTokenUtility;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

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

/**
 * Filter class for intercepting all the requests and validate with JWT (Jason Web Token).
 */
@Slf4j
@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    //Attributes
    private final LibraryUserDetailsService libraryUserDetailsService;
    private final JsonWebTokenUtility jsonWebTokenUtility;

    /**
     * Constructor of JwtRequestFilter.
     * @param theLibraryUserDetailsService LibraryUserDetailsService
     * @param theJsonWebTokenUtility JsonWebTokenUtility
     */
    @Autowired
    public JwtRequestFilter(LibraryUserDetailsService theLibraryUserDetailsService, JsonWebTokenUtility theJsonWebTokenUtility) {
        this.libraryUserDetailsService = theLibraryUserDetailsService;
        this.jsonWebTokenUtility = theJsonWebTokenUtility;
    }

    /**
     * Responsible for intercepting the request via filter and do the validations and needful with JWT.
     * Overridden from OncePerRequestFilter level.
     * @param theRequest HttpServletRequest
     * @param theResponse HttpServletResponse
     * @param theChain FilterChain
     * @throws ServletException - Exception upon execution.
     * @throws IOException - Exception upon execution.
     */
    @Override
    protected void doFilterInternal(HttpServletRequest theRequest, HttpServletResponse theResponse, FilterChain theChain)
            throws ServletException, IOException {
        printLog("doFilterInternal() -> Executed");
        //Get the 'Authorization' from the request header. JWT is suppose to send in request header under 'Authorization'.
        final String authorizationHeader = theRequest.getHeader("Authorization");

        //Method local attributes
        String username = null;
        String jwt = null;

        //Get the JWT String and username out from 'authorizationHeader'
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            printLog("doFilterInternal() -> Extracting the JWT token from the authorization header");
            jwt = authorizationHeader.substring(7);
            username = jsonWebTokenUtility.extractUserName(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            printLog("doFilterInternal() -> User name found: " + username + " and there is no current logged-in user");
            //If the username is not null and there is no current user authenticated

            //Get the user from user details service
            UserDetails userDetails = this.libraryUserDetailsService.loadUserByUsername(username);

            if (jsonWebTokenUtility.validateToken(jwt, userDetails)) {
                printLog("doFilterInternal() -> Load user for the username: " + userDetails.getUsername() +
                        " and validated the JWT successfully");
                //Validating user with the JWT String is successful

                //Create token
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                printLog("doFilterInternal() -> Created 'UsernamePasswordAuthenticationToken'");
                //Set the information to the token
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(theRequest));
                printLog("doFilterInternal() -> Set details to the 'UsernamePasswordAuthenticationToken'");
                //Set the authorized user to the context
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                printLog("doFilterInternal() -> Set 'UsernamePasswordAuthenticationToken' to the 'SecurityContextHolder'");
            }
        }
        //Continue the chain
        printLog("doFilterInternal() -> Continue the chain...");
        theChain.doFilter(theRequest, theResponse);
    }

    /**
     * Responsible for printing main log messages to the console.
     * @param theLogMessage String
     */
    private void printLog(String theLogMessage) {
        log.info("JwtRequestFilter: " + theLogMessage);
    }

}

在上面的過濾器中,我想從標頭中獲取“授權”,但是請求根本沒有標頭。 當我通過郵遞員做同樣的事情時,它工作得很好。 當我通過 Angular 客戶端執行此操作時會發生這種情況。 請幫助我找到問題,並提前致謝。

調試在此處輸入圖片說明

有同樣的問題。 就我而言,我在預檢電話中遇到了 cors 錯誤。 添加 http.cors() 如下解決了我的問題。

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {



    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.headers().cacheControl();

        http.csrf().disable()
                .authorizeRequests()
                .....;
        http.cors();
    }

    
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM