Spring Boot 不接收从 Angular 客户端添加的请求标头

San*_*ama 2 java interceptor http-headers spring-boot angular

在我的演示应用程序中,我遇到一个问题,即我没有收到从 Angular 客户端添加到 Spring boot 服务器的请求标头。作为安全措施,我有 SSL(安全套接字层)和 CORS(跨源资源共享)配置。在我的应用程序中,CSRF(跨站点请求伪造)被禁用。我使用 JWT(JSON Wen Token)作为每个请求的用户身份验证机制。这就是我需要从标头中提取 JWT 密钥的地方。我将从我的应用程序中添加代码示例,请帮助我找出问题。

\n

角拦截器

\n
import { Injectable } from \'@angular/core\';\nimport { HttpEvent, HttpInterceptor, HttpResponse, HttpHandler, HttpRequest, HttpHeaders } from \'@angular/common/http\'; \nimport { UserService } from \'./user.service\';\nimport { Observable } from \'rxjs\';\n\n/*Interceptor\n  Often you\xe2\x80\x99ll want to intercept HTTP requests or responses before they\xe2\x80\x99re handled else where in the application*/ \n\n@Injectable({\n  providedIn: \'root\'\n})\nexport class InterceptorService implements HttpInterceptor {\n\n  /**\n   * Constructor of InterceptorService. \n   * @param userService UserService\n   */\n  constructor(private userService: UserService) { }\n\n  /**\n   * Responsible for intercepting requests. \n   * Responsible for adding \'Authorization\' header with JWT (Json Web Token). \n   * @param theRequest HttpRequest<any>\n   * @param handler HttpHandler\n   */\n  intercept(theRequest: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {\n    debugger;\n    const jwtKey = this.userService.getJwtString();\n    const authReq = theRequest.clone({\n      headers: new HttpHeaders({\n        \'Content-Type\':  \'application/json\',\n        \'Authorization\': `Bearer ${jwtKey}`\n      })\n    });\n    console.log(\'Intercepted HTTP call\', authReq);\n    return handler.handle(authReq);\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

角度用户服务

\n
import { Injectable } from \'@angular/core\';\nimport { HttpClient, HttpHeaders, HttpParams } from \'@angular/common/http\';\nimport { Router } from \'@angular/router\';\nimport { AuthenticationConfigurationService } from \'./authentication-configuration.service\';\nimport { User, AuthenticationResponse } from \'./data\';\n\n//Service for users \n\n@Injectable({\n  providedIn: \'root\'\n})\nexport class UserService {\n    //Attributes\n  private jwtString: string = \'\'; //JWT (Jason Web Token)\n  private isUserLoggedIn = false; \n\n  /**\n   * Constructor of UserService. \n   * @param router Router\n   * @param httpClient HttpClient\n   */\n  constructor(private router: Router, private httpClient: HttpClient) { }\n\n  /**\n   * Executes upon user login. \n   * @param theUser User \n   */\n  login(theUser: User) {\n    const httpOptions = { headers: new HttpHeaders({ \'Content-Type\': \'application/json\' }) };\n      this.httpClient.post(AuthenticationConfigurationService.getAuthenticationURL(\'authenticationURL\') + \'/getjwt\', theUser, httpOptions)\n                .subscribe((response: AuthenticationResponse) => {\n                    this.jwtString = response.jwt;\n          this.isUserLoggedIn = true;\n                });\n  }\n\n  /**\n   * Executes upon user logout. \n   */\n  logout() {;\n    this.isUserLoggedIn = false; \n    this.jwtString = \'\';\n    this.navigateToLoginPage();\n  }\n\n  /**\n   * Responsible for returning the JWT (Json Web Token) string.\n   * @returns string\n   */\n  getJwtString(): string {\n    return this.jwtString; \n  }\n\n  /**\n   * Responsible for returning whether the user is logged-in. \n   * @returns boolean\n   */\n  userLoggedIn(): boolean {\n    return this.isUserLoggedIn;\n  }\n\n  /**\n   * Navigate to the login page. \n   */\n  private navigateToLoginPage() {\n    this.router.navigateByUrl(\'/login\');\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

春季启动安全

\n
package com.example.LibraryServer.Security;\n\nimport com.example.LibraryServer.Filter.Security.JwtRequestFilter;\nimport com.example.LibraryServer.Services.LibraryUserDetailsService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.CorsConfigurationSource;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\n\nimport java.util.Arrays;\n\n/**\n * Class responsible for security configurations.\n */\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    //Attributes\n    private final LibraryUserDetailsService libraryUserDetailsService;\n    private final JwtRequestFilter jwtRequestFilter;\n\n    /**\n     * Constructor of SecurityConfig.\n     * @param theLibraryUserDetailsService LibraryUserDetailsService\n     * @param theJwtRequestFilter JwtRequestFilter\n     */\n    @Autowired\n    public SecurityConfig(LibraryUserDetailsService theLibraryUserDetailsService, JwtRequestFilter theJwtRequestFilter) {\n        this.libraryUserDetailsService = theLibraryUserDetailsService;\n        this.jwtRequestFilter = theJwtRequestFilter;\n    }\n\n    /**\n     * Responsible for user security configuration.\n     * Overridden from WebSecurityConfigurerAdapter level.\n     * @param theHttpSecurity HttpSecurity\n     * @throws Exception - Exception upon security configuration.\n     */\n    @Override\n    protected void configure(HttpSecurity theHttpSecurity) throws Exception {\n        //theHttpSecurity.authorizeRequests()\n                //.antMatchers("/**").access("permitAll") //Allow all paths\n                /*.and().cors()*/\n                //.and().csrf().disable(); //Allow all requests - CSRF (Cross-Site Request Forgery)\n        theHttpSecurity.csrf().disable() //Allow all requests - CSRF (Cross-Site Request Forgery)\n                .authorizeRequests().antMatchers("/authenticate/**").access("permitAll") //Allow all paths\n                .anyRequest().authenticated() //All other paths need authentication\n                /*Inform spring security not to manage sessions.\n                  All requests will be filtered via \'JwtRequestFilter\' with JWT and does not need sessions.*/\n                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);\n\n        //Inform spring security about the filter \'JwtRequestFilter\' for username and password authentication.\n        theHttpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);\n    }\n\n    /**\n     * Responsible for configuring user-store.\n     * Overridden from WebSecurityConfigurerAdapter level.\n     * @param theAuthentication AuthenticationManagerBuilder\n     * @throws Exception - Exception upon user store creation.\n     */\n    @Override\n    public void configure(AuthenticationManagerBuilder theAuthentication) throws Exception {\n        //theAuthentication.inMemoryAuthentication()\n                //.withUser("sankalpa")\n                //.password("{noop}123")\n                //.authorities("ROLE_USER");\n        theAuthentication.userDetailsService(libraryUserDetailsService);\n    }\n\n    /**\n     * Method constructing AuthenticationManager bean.\n     * This method is needed since AuthenticationManager is being used in \'HelloController\'.\n     * Therefore this bean should be in spring application context.\n     * Overridden from WebSecurityConfigurerAdapter level.\n     * @return AuthenticationManager\n     * @throws Exception - Exception upon execution.\n     */\n    @Override\n    @Bean\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    /**\n     * Method constructing a password encoder bean.\n     * Constructs \'NoOpPasswordEncoder\'.\n     * @return PasswordEncoder\n     */\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return NoOpPasswordEncoder.getInstance();\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Spring Boot CORS配置类

\n
package com.example.LibraryServer.CORS;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.CorsRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * Configuration class which is responsible for handling CORS.\n * Cross-Origin Resource Sharing (CORS) configuration class.\n */\n@Configuration\npublic class CorsConfiguration implements WebMvcConfigurer {\n\n    /**\n     * Responsible for CORS mapping.\n     * Overridden from WebMvcConfigurer level.\n     * @param theRegistry CorsRegistry\n     */\n    @Override\n    public void addCorsMappings(@NotNull CorsRegistry theRegistry) {\n        //End points\n        var authorizedEndpoints = new String[] {\n                "/book/**",\n                "/author/**",\n                "/authenticate/**"\n        };\n\n        //Add mapping\n        for (var endPoint : authorizedEndpoints) {\n            theRegistry.addMapping(endPoint)\n                    .allowedOrigins("http://localhost:4200")\n                    .allowedMethods("*") //"HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"\n                    .allowedHeaders("*")\n                    .allowCredentials(true)\n                    .exposedHeaders("Authorization");\n        }\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

弹簧启动过滤器

\n
package com.example.LibraryServer.Filter.Security;\n\nimport com.example.LibraryServer.Services.LibraryUserDetailsService;\nimport com.example.LibraryServer.Uilities.JsonWebTokenUtility;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * Filter class for intercepting all the requests and validate with JWT (Jason Web Token).\n */\n@Slf4j\n@Component\npublic class JwtRequestFilter extends OncePerRequestFilter {\n\n    //Attributes\n    private final LibraryUserDetailsService libraryUserDetailsService;\n    private final JsonWebTokenUtility jsonWebTokenUtility;\n\n    /**\n     * Constructor of JwtRequestFilter.\n     * @param theLibraryUserDetailsService LibraryUserDetailsService\n     * @param theJsonWebTokenUtility JsonWebTokenUtility\n     */\n    @Autowired\n    public JwtRequestFilter(LibraryUserDetailsService theLibraryUserDetailsService, JsonWebTokenUtility theJsonWebTokenUtility) {\n        this.libraryUserDetailsService = theLibraryUserDetailsService;\n        this.jsonWebTokenUtility = theJsonWebTokenUtility;\n    }\n\n    /**\n     * Responsible for intercepting the request via filter and do the validations and needful with JWT.\n     * Overridden from OncePerRequestFilter level.\n     * @param theRequest HttpServletRequest\n     * @param theResponse HttpServletResponse\n     * @param theChain FilterChain\n     * @throws ServletException - Exception upon execution.\n     * @throws IOException - Exception upon execution.\n     */\n    @Override\n    protected void doFilterInternal(HttpServletRequest theRequest, HttpServletResponse theResponse, FilterChain theChain)\n            throws ServletException, IOException {\n        printLog("doFilterInternal() -> Executed");\n        //Get the \'Authorization\' from the request header. JWT is suppose to send in request header under \'Authorization\'.\n        final String authorizationHeader = theRequest.getHeader("Authorization");\n\n        //Method local attributes\n        String username = null;\n        String jwt = null;\n\n        //Get the JWT String and username out from \'authorizationHeader\'\n        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {\n            printLog("doFilterInternal() -> Extracting the JWT token from the authorization header");\n            jwt = authorizationHeader.substring(7);\n            username = jsonWebTokenUtility.extractUserName(jwt);\n        }\n\n        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {\n            printLog("doFilterInternal() -> User name found: " + username + " and there is no current logged-in user");\n            //If the username is not null and there is no current user authenticated\n\n            //Get the user from user details service\n            UserDetails userDetails = this.libraryUserDetailsService.loadUserByUsername(username);\n\n            if (jsonWebTokenUtility.validateToken(jwt, userDetails)) {\n                printLog("doFilterInternal() -> Load user for the username: " + userDetails.getUsername() +\n                        " and validated the JWT successfully");\n                //Validating user with the JWT String is successful\n\n                //Create token\n                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =\n                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());\n                printLog("doFilterInternal() -> Created \'UsernamePasswordAuthenticationToken\'");\n                //Set the information to the token\n                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(theRequest));\n                printLog("doFilterInternal() -> Set details to the \'UsernamePasswordAuthenticationToken\'");\n                //Set the authorized user to the context\n                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);\n                printLog("doFilterInternal() -> Set \'UsernamePasswordAuthenticationToken\' to the \'SecurityContextHolder\'");\n            }\n        }\n        //Continue the chain\n        printLog("doFilterInternal() -> Continue the chain...");\n        theChain.doFilter(theRequest, theResponse);\n    }\n\n    /**\n     * Responsible for printing main log messages to the console.\n     * @param theLogMessage String\n     */\n    private void printLog(String theLogMessage) {\n        log.info("JwtRequestFilter: " + theLogMessage);\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在上面的过滤器中,我想从标头中获取“授权”,但请求根本没有标头。当我通过邮递员做同样的事情时,效果非常好。当我通过 Angular 客户端执行此操作时会发生这种情况。

\n

调试\n在此输入图像描述

\n

小智 8

有同样的问题。就我而言,我在飞行前调用中遇到了 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();
    }

    
}
Run Code Online (Sandbox Code Playgroud)