从使用 Angular 7 的请求中接收 Spring Boot 中的空授权标头,但 Postman 工作正常

Posted

技术标签:

【中文标题】从使用 Angular 7 的请求中接收 Spring Boot 中的空授权标头,但 Postman 工作正常【英文标题】:Receiving null Authorization header in Spring Boot from requests with Angular 7 but Postman works fine 【发布时间】:2020-05-07 05:07:13 【问题描述】:

当我向使用 Spring Boot 设计的后端控制器发送请求时,我收到一个空授权标头。但是当我使用 Postman 发送相同的请求时,会命中正确的 API,并且会从后端正确获取数据。

在 Spring Boot 方面,这是 JwtSecurityConfiguration.java 的代码:

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
        .csrf().disable()
        .authorizeRequests()
        .antMatchers(HttpMethod.OPTIONS, "**/**").permitAll()
        .antMatchers("/auth/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .exceptionHandling().authenticationEntryPoint(entryPoint)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        http.headers().cacheControl();
    

我在JwtAuthenticationToken.java 中收到null 授权标头:

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException 
        String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer ")) 
            System.err.println("Header: " + header);
            LOGGER.error("JWT Token is either missing from HTTP header or has been provided in an incorrect format!");
            throw new AuthenticationCredentialsNotFoundException(
                    "JWT Token is either missing from HTTP header or has been provided in an incorrect format!");
        
        String authenticationToken = header.substring(7);
        JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(authenticationToken);
        LOGGER.error("JWT Token has been received successfully. Authentication is in progress...");
        return getAuthenticationManager().authenticate(jwtAuthenticationToken);
    

在等式的 Angular 方面,我使用 HTTP 拦截器将 JWT 令牌添加到发送的每个请求中。这是我的HttpInterceptorAuth.service.ts 的样子:

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    let jwtAuthHeader = this._authSvc.getAuthorizedToken();
    let jwtAuthUsername = this._authSvc.getLoggedInUsername();

    if (jwtAuthHeader && jwtAuthUsername) 
      request = request.clone(
        setHeaders: 
          Authorization: jwtAuthHeader
        
      );
    
    console.log(request);
    return next.handle(request);
  

由于我正在记录 HTTP 拦截器发送的请求,因此在 Chrome 控制台中是这样的:

在 Chrome 开发工具的网络选项卡上,这是 Chrome 发送的请求:

请注意OPTIONS 请求失败并显示401 Unauthorized

这可能是因为在后端,当 HTTP 拦截器使用授权标头中的 JWT 令牌更新请求时,我收到了一个空的授权标头。

我不知道为什么实际发送的请求与 HTTP 拦截器更新的请求不同。我该如何解决这个问题?

【问题讨论】:

浏览器不会将授权添加到预检 OPTIONS 请求。 Spring Security 内置了 CORS 处理,您只需要启用它即可;参见例如baeldung.com/spring-security-cors-preflight 似乎是后端问题,您的角度代码看起来不错。 好的。这是我第一次使用 JWT。问题是我需要从角度访问 Spring Boot 中的控制器 API,这需要 JWT 令牌才能访问 Spring Boot 控制器 API。我需要在 Authorization 标头中发送该 JWT 令牌。由于存在授权标头,因此浏览器会发送预检请求检查。那么,我该如何避免预检请求检查呢? 你没有避免它,你正确地处理它。见developer.mozilla.org/en-US/docs/Web/HTTP/CORS。 您对如何修改我的后端代码以便我可以正确处理预检请求有任何指示吗?我不知道是否需要从后端显式处理预检请求。 【参考方案1】:

要解决您可以在控制器中使用注释:

 @CrossOrigin (origins = "*" , exposedHeaders = "**")

将 * 更改为您的原始链接,将 ** 更改为要公开的特定令牌。

例子:

@CrossOrigin(origins = "http://localhost:4200", exposedHeaders = "token")
@RestController
public class AuthenticationController 
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private TokenService tokenService;

    @PostMapping("/auth")
    public ResponseEntity<?> autenticar(@RequestBody @Valid UserDTO userDTO)
        UsernamePasswordAuthenticationToken userData = new UsernamePasswordAuthenticationToken(userDTO.getUserName(), userDTO.getPassword());
        try 
            Authentication authentication = authenticationManager.authenticate(userData);
            String token = tokenService.generateToken(authentication);
            HttpHeaders responseHeaders = new HttpHeaders();
            responseHeaders.set("token", token);

            return ResponseEntity.ok().headers(responseHeaders).build();
         catch (AuthenticationException e) 
            return ResponseEntity.badRequest().build();
        
    

【讨论】:

【参考方案2】:

那么我该如何避免预检请求检查呢?

您不能禁用或避免 CORS 来源的预检请求机制。

Cross-Origin Resource Sharing (CORS) 是一种使用机制 额外的 HTTP 头告诉浏览器给一个 web 应用程序 在一个来源运行,访问来自不同来源的选定资源 起源。 Web 应用程序在执行跨域 HTTP 请求时 请求具有不同来源(域、协议或 端口)来自它自己的

解决可以使用CorsConfigurationSource@CrossOrigin

@Bean
    public CorsConfigurationSource corsConfigurationSource() 
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
        configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

@CrossOrigin(origins = "*",allowCredentials = ,allowedHeaders = , exposedHeaders = , methods = , value = )

Access-Control-Expose-Headers

【讨论】:

以上是关于从使用 Angular 7 的请求中接收 Spring Boot 中的空授权标头,但 Postman 工作正常的主要内容,如果未能解决你的问题,请参考以下文章

从列表中接收模型并显示属性[javascript] [angular 7]

Angular 7未根据请求发送正确的标头

angular 7 反应式表单:为表单中的每个输入字段动态添加/删除输入字段

具有基本身份验证的 Angular 7 HTTP 请求 [关闭]

如何将值从节点返回到原点(angular2 请求)

Spring / Angular 7 POST方法请求参数为空