Angular 5调用Spring+Shiro后端接口时会话不一致问题求助
解决Angular 5调用Spring+Shiro接口时JSESSIONID无法保持的问题
这个问题我之前帮同事排查过!核心原因就是跨域请求下Angular默认不会自动携带Cookie,而Postman会自动帮你带上会话Cookie,所以导致每次请求后端都认为是新会话,生成新的JSESSIONID。下面给你一步步解决的方案:
一、后端配置允许跨域携带凭证
首先要在Spring后端配置CORS,明确允许前端携带Cookie(凭证),同时注意allowedOrigins不能设为*,必须指定具体的前端域名/端口:
方式1:全局CORS配置
创建一个配置类,全局设置跨域规则:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 替换成你的Angular前端地址,比如http://localhost:4200 .allowedOrigins("http://localhost:4200") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") // 关键:允许携带凭证(Cookie) .allowCredentials(true) .maxAge(3600); } }
方式2:接口级别的@CrossOrigin注解
如果不想全局配置,也可以在单个Controller或接口上添加注解:
@RestController @RequestMapping("/api") // 同样要指定具体 origin 和 allowCredentials @CrossOrigin(origins = "http://localhost:4200", allowCredentials = "true") public class AuthController { // 登录接口等 }
额外:Shiro过滤器放行OPTIONS请求
因为浏览器在跨域复杂请求前会发OPTIONS预检请求,要确保Shiro不会拦截它:
在Shiro的配置类里,添加OPTIONS请求的放行规则:
@Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 放行OPTIONS请求 filterChainDefinitionMap.put("/api/**", "anon"); // 其他规则... shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
二、前端Angular配置请求携带凭证
接下来要让Angular的HttpClient在请求时带上Cookie,有两种方式:
方式1:单个请求设置withCredentials
在每次请求(比如登录、接口调用)时,添加withCredentials: true选项:
import { HttpClient } from '@angular/common/http'; // 登录请求示例 login(loginData: any) { return this.http.post('/api/login', loginData, { withCredentials: true // 关键:携带Cookie }); }
方式2:全局拦截器统一设置
如果不想每个请求都写一遍,可以创建一个HTTP拦截器,全局给所有请求添加withCredentials:
- 创建拦截器类:
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // 克隆请求并添加withCredentials const modifiedRequest = request.clone({ withCredentials: true }); return next.handle(modifiedRequest); } }
- 在AppModule里注册拦截器:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ // ...其他配置 providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
三、额外注意事项
- 确保前端和后端的域名/端口是明确的跨域场景,比如后端
localhost:8080,前端localhost:4200,如果是同域名同端口,不会有这个问题。 - 测试时建议用浏览器无痕模式,避免旧的Cookie或缓存干扰。
- 检查Shiro的会话配置,确保是默认的基于Cookie的会话管理(默认就是,除非你手动改了)。
按照上面的步骤配置后,Angular请求时就会自动携带JSESSIONID Cookie,后端就能正常保持会话了!
内容的提问来源于stack exchange,提问作者Vinu
相关产品推荐
相关产品推荐

