Angular客户端如何存储OAuth2令牌以调用带Bearer头的资源API?
问题1:如何存储OAuth2的access token和refresh token以满足Bearer头API调用需求
有两种实用方案可选:
前端存储+拦截器自动注入
将access token存在localStorage或sessionStorage中(前提是做好XSS防护,比如配置内容安全策略CSP、严格校验用户输入),同时编写Angular的HttpInterceptor,在每次发起API请求时自动从存储中读取token并注入到Authorization: Bearer <token>请求头里。
示例拦截器代码:@Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const token = localStorage.getItem('access_token'); if (token) { const authReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) }); return next.handle(authReq); } return next.handle(req); } }对于refresh token,若想降低风险,可交由后端存储在httpOnly Cookie中,前端通过调用后端的刷新接口获取新的access token。
后端代理令牌流程
前端不直接调用Keycloak的token接口,而是将授权码传给自身的Spring Boot后端,由后端完成Keycloak令牌获取逻辑。后端将access token返回给前端存在localStorage,同时把refresh token存入httpOnly、Secure、SameSite=Strict的Cookie中。后续前端需要刷新token时,调用后端专属接口,后端从Cookie读取refresh token去Keycloak换取新令牌,再将新的access token返回给前端。
问题2:存储在httpOnly Cookie中的令牌如何转为Authorization头
由于httpOnly Cookie无法被前端JS读取,必须依赖后端配合实现:
后端过滤器自动转换
在Spring Boot后端编写一个请求过滤器,当收到前端请求时,从Cookie中读取access token,自动将其注入到Authorization: Bearer <token>请求头中,无需前端做任何额外处理。
示例过滤器代码:@Component public class CookieToHeaderFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("access_token".equals(cookie.getName())) { HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) { @Override public String getHeader(String name) { if ("Authorization".equals(name)) { return "Bearer " + cookie.getValue(); } return super.getHeader(name); } }; filterChain.doFilter(wrappedRequest, response); return; } } } filterChain.doFilter(request, response); } }后端代理转发请求
前端所有API请求都先发送到自身后端,后端从Cookie中读取token,添加Authorization头后转发给目标资源API,再将响应结果返回给前端。这种方式还能隐藏资源API的真实地址,提升整体安全性。
内容的提问来源于stack exchange,提问作者Surya

