无法在 Java Spring Boot / Angular 应用程序中获取 cookie

Posted

技术标签:

【中文标题】无法在 Java Spring Boot / Angular 应用程序中获取 cookie【英文标题】:Cannot get cookies in Java Spring Boot / Angular application 【发布时间】:2021-06-14 01:38:12 【问题描述】:

我正在开发一个 Angular11 前端应用程序和一个 Java Spring boot 后端。

上下文

我正在尝试使用带有 JWT 的 cookie 创建身份验证。 为此,我受到了启发

JWT refresh token flow Access token and Refresh token best practices ? How to implement Access & Refresh Tokens

我的端点 /authenticate 返回一个 jwt 访问令牌并在 cookie (httpOnly) 中设置一个 jwt 刷新令牌。 我的端点 /refreshtoken 从 cookie 中获取刷新令牌并在生成新的 jwt 访问令牌和新的 jwt 刷新令牌之前对其进行验证。 这是我AuthenticationController的代码

    @RestController
    @AllArgsConstructor
    @CrossOrigin(origins = "http://localhost:4200", allowCredentials = "true")
    public class AuthenticationController 
        
            private final AuthenticationManager authenticationManager;
            private final JwtTokenUtil jwtTokenUtil;
            private final UserDetailsServiceImpl userDetailsService;
            private final static String REFRESH_TOKEN_COOKIE = "resfreshtoken";
        
            @PostMapping(value = "/authenticate")
            public ResponseEntity<JwtAuthenticationResponse> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletResponse response) throws Exception 
                try 
                    this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()));
                
                catch (DisabledException e) 
                    throw new Exception("USER_DISABLED", e);
                
                catch (BadCredentialsException e) 
                    throw new Exception("INVALID_CREDENTIALS", e);
                
                final UserDetails userDetails = this.userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
                final String token = this.jwtTokenUtil.generateToken(userDetails.getUsername());
                final String refreshToken = this.jwtTokenUtil.generateRefreshToken(new HashMap<>(), userDetails.getUsername());
        
                Cookie cookie = new Cookie(REFRESH_TOKEN_COOKIE, refreshToken);
                cookie.setHttpOnly(true);
                // cookie.setDomain("localhost");
                // cookie.setPath("/");
                response.addCookie(cookie);
        
                return ResponseEntity.ok(new JwtAuthenticationResponse(token));
            
        
            @GetMapping(value = "/refreshtoken")
            public ResponseEntity<?> refreshToken(@CookieValue(value = REFRESH_TOKEN_COOKIE, required = false) String refreshToken, HttpServletRequest request, HttpServletResponse response) 
                Cookie[] cookies = request.getCookies(); // ALWAYS returns null
                log.debug("refreshToken ", refreshToken); // ALWAYS null
                try 
                    Claims claims = this.jwtTokenUtil.getAllClaimsFromToken(refreshToken);
                    final String username = claims.get("sub").toString();
                    final String newAccessToken = this.jwtTokenUtil.generateToken(username);
                    final String newRefreshToken = this.jwtTokenUtil.generateRefreshToken(claims, username);
                    CookieUtil.writeCookie(response, REFRESH_TOKEN_COOKIE, newRefreshToken);
        
                    return ResponseEntity.ok(new JwtAuthenticationResponse(newAccessToken));
                
                catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException ex) 
                    throw new BadCredentialsException("INVALID_CREDENTIALS", ex);
                
                catch (ExpiredJwtException e) 
                    // user should re-login
                
    
                  return new ResponseEntity<>("Something went wrong", HttpStatus.BAD_REQUEST);
                
        

在 Angular 前端,这里是我添加到 /refreshtoken Http 请求中的选项 (withCredentials=true)

refreshToken(): Observable<AuthenticationResponse> 
        return this.http.get<AuthenticationResponse>(this.apiUrl + 'refreshtoken',  withCredentials: true ).pipe(
            tap(response => LocalStorageUtils.save(LocalStorageKey.JWT_ACCESS_TOKEN_KEY, response.jwtAccessToken))
        );
    

我的问题

如果我尝试从邮递员到达我的端点 /authenticate 然后 /refreshtoken,一切正常,我可以看到 cookie 如果我从前端调用/authenticate,我可以在响应中看到带有 SET-COOKIE 标题的 cookie(以及在 cookies 选项卡中) 但是,如果我从前端调用/refreshtoken,我无法在后端获取带有request.getCookies()@CookieValue() 的cookie,它总是返回null看起来像cookie 在前端很受欢迎,但没有设置(我在 application 选项卡 -> Chrome 上的 cookie 中看不到它)。 因此它不会与 /refreshtoken 请求一起发送

如果有人可以提供帮助或知道出了什么问题,我将不胜感激!如果需要,我可以提供有关我的代码的更多详细信息。

【问题讨论】:

【参考方案1】:

您需要将withCredentials: true 添加到您的httpOptions 并在每个请求中使用HttpClient 传递它。您也可以使用 HttpClient 和 Interceptors 全局配置它。

【讨论】:

我用更多细节更新了我的问题。我试过你的建议,这是一个很好的线索,但在我的情况下它仍然不起作用。我的 cookie 仍未随 /refreshtoken 请求一起发送。 能否也添加.ts 部分进行身份验证? 你实际上让我走对了路。我的错误是将withCredentials: true 仅用于 /refreshtoken 请求。但实际上,我还必须将它放在 /authenticate 请求中,这允许它在浏览器中设置 cookie!谢谢!

以上是关于无法在 Java Spring Boot / Angular 应用程序中获取 cookie的主要内容,如果未能解决你的问题,请参考以下文章

java.lang.IllegalStateException:在 Spring Boot 中无法加载 ApplicationContext 错误

Spring Boot——Using WebSocket to build an interactive web application

java spring-boot错误:“无法解析符号jdbc”

Spring-Boot学习

Heroku 无法部署 Java 11 Spring Boot App [重复]

Spring boot 启动错误处理:Action: Consider the following: If you want an embedded database (H2, HSQL or Der