SpringBoot解决跨域访问及一致性Session问题

解决问题

前后端分离项目背景下,跨域访问及一致性session问题(是否同一用户)。

ps:以前做的项目都是前、后端部署在一个tomcat容器中,不会涉及到跨域访问以及一致性session问题。随着前后端分离架构的流行,前、后端部署在不同服务器等都会涉及到跨域等问题。

 

同源策略

同源策略是浏览器保证安全的基础,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页同源。 所谓同源必须同时满足以下3点:

  • 协议相同

  • 域名相同

  • 端口相同

    例如:www.abc.com:8080/login 协议是http,域名是www.abc.com,端口是8080。

 

当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求。浏览器向请求的服务器发送两次请求:第一次浏览器使用OPTIONS方法发起一个预检请求,第二次才是异步请求

预检请求查看服务器是否允许跨域请求:如果允许,则发送异步请求;否则拦截用户请求。

 

跨域解决办法

  • JSONP

    只支持get请求,限制比较大。

  • CROS

    CORS跨域资源共享是一种机制,它使用额外的 HTTP头来告诉浏览器 让运行在一个 origin上的Web应用被准许访问来自不同源服务器上的指定的资源。

准备工作

这里我只是在本地进行模拟跨域问题,源码可以参考我的github:https://github.com/FllowY/spring-boot-examples/tree/master/spring-boot-cors

模拟前后端分离:

1、一个客户端项目,使用maven创建并发布到Tomcat服务器上,端口设置为8090,用来调用服务端接口。为了保证请求跨域,客户端和服务端我使用了不同的端口号。

2、一个服务端项目,使用SpringBoot项目,使用嵌入式Tomcat服务器启动,端口号设置为8080,并使其运行开启服务,保证客户端能访问到。

解决步骤

解决两个问题:跨域、Session不一致问题。

  1. ajax请求中,加入xhrFields:{withCredentials: true},表示携带cookie信息。非常重要:必须设置参数,否则每次跨域调用接口,都会新建一个session信息,相当于每次调用都是不同用户访问,是不合理,更是不安全的设计。

    <button id="login" onclick="login()">login</button><br/>
    <button id="cros" onclick="cros()">cros</button><br/>
    //登录
    function login(){
        $.ajax({
            url:"http://127.0.0.1:8080/cors-server/login",
            type:"post",
            dataType:"json",
            xhrFields: {//携带cookie信息
                withCredentials: true
            },
            success:function(json){
                console.log(json);
            }
        });
    }
    
    //登录后,跨域请求服务,用于测试session
    function cros(){
    	$.ajax({
    		url:"http://127.0.0.1:8080/cors-server/cookie",
    		type:"post",
    		dataType:"json",
    		xhrFields: {
                withCredentials: true
            },
            success:function(json){
                console.log(json);
            }
        });
    }

     

  2. 服务端Controller层接收用户请求

    /**
     * @Description 跨域接口
     * @Author rongtao
     * @Data 2019/4/24 13:27
     */
    @RestController
    public class UserController {
    
        /**
        * @Description 模拟登录,保存session
        * @param request
        * @Return java.util.Map<java.lang.Object,java.lang.Object>
        * @Author rongtao
        * @Date 2019/4/24 13:28
        */
        @RequestMapping("/login")
        public Map<Object, Object> cros(HttpServletRequest request){
            Map<Object,Object> map = new HashMap<>();
            User user = new User("liyi","love");
            map.put("user",user);
            request.getSession().setAttribute("user", user);
            return map;
        }
    
        /**
        * @Description 获取用户cookie
        * @param request
        * @Return java.util.Map<java.lang.Object,java.lang.Object>
        * @Author rongtao
        * @Date 2019/4/24 13:29
        */
        @RequestMapping("/cookie")
        public Map<Object, Object> getCookie(HttpServletRequest request){
            Map<Object, Object> map = new HashMap<>();
            HttpSession session = request.getSession();
            System.out.println("session : " + session.getId() + ", user : " + session.getAttribute("user"));
    
            Cookie[] cookies = request.getCookies();
            System.out.print("cookie : ");
            //jdk1.8;map:逐个处理集合中元素;forEach:遍历
            Arrays.stream(cookies).map(Cookie::getValue).forEach(System.out::println);
            map.put("cookie",cookies);
            return map;
        }
    
    }

     

  3. SpringBoot项目中加入CORS过滤器,为请求附加头信息,使其满足跨域

    /**
     * @Description 跨域请求拦截器
     * @Author rongtao
     * @Data 2019/4/24 13:32
     */
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class CorsFilter implements Filter {
    
        public CorsFilter(){
            System.out.println("CorsFilter Constructor...");
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("CorsFilter init...");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("CorsFilter doFilter...");
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            //允许请求携带认证信息(cookie)
            res.setHeader("Access-Control-Allow-Credentials", "true");
            //指定允许其他域名访问
            res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
            //允许请求的类型
            res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
            //允许的请求头字段
            res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
            //设置预检请求的有效期
            //浏览器同源策略:出于安全考虑,浏览器限制跨域的http请求。怎样限制呢?通过发送两次请求:预检请求、用户请求。
            //1、预检请求作用:获知服务器是否允许该跨域请求:如果允许,才发起第二次真实的请求;如果不允许,则拦截第二次请求
            //2、单位:s,在此期间不用发送预检请求。
            //3、若为0:表示每次请求都发送预检请求,每个ajax请求之前都会先发送预检请求。
            res.setHeader("Access-Control-Max-Age", "3600");
            //OPTIONS Method表示浏览器发送的预检请求。
            if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
                res.setStatus(HttpServletResponse.SC_OK);
            } else {
                chain.doFilter(req, res);
            }
        }
    
        @Override
        public void destroy() {
            System.out.println("CorsFilter destroy...");
        }
    }

     

  4. 设置session有效期

    server.port=8080
    server.servlet.context-path=/cors-server
    #session超时时间
    server.servlet.session.timeout=1

     

  5. 设置session超时拦截器,模拟用户登录超时情况

    /**
     * @Description 登录超时拦截器
     * @Author rongtao
     * @Data 2019/4/24 13:37
     */
    @Component
    public class SessionInterceptor extends HandlerInterceptorAdapter {
        //拦截action
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            User user = (User) request.getSession().getAttribute("user");
            System.out.println(user);
            //session中User过期
            if(user == null){
                String uri = request.getRequestURI();
                System.out.println(uri);
                //ajax请求响应头会有,x-requested-with
                if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with")
                        .equalsIgnoreCase("XMLHttpRequest")) {
                    //在响应头设置session状态
                    response.setHeader("sessionstatus", "timeout");
                    response.setHeader("url", uri.substring(0, uri.indexOf("/", 1)));
                } else {
                    PrintWriter out = response.getWriter();
                    StringBuilder sb = new StringBuilder();
                    sb.append("<script type=\"text/javascript\" charset=\"UTF-8\">");
                    sb.append("alert(\"登录超时,请重新登录\");");
                    sb.append("window.top.location.href=\"");
                    sb.append("/login.jsp");
                    sb.append("\";</script>");
                    out.print(sb.toString());
                    out.close();
                }
                //返回false不再调用其他的拦截器和处理器
                return false;
            }
            return true;
        }
    }

     

  6. 注册登录超时拦截器,并设置拦截路径

    /**
     * @Description WebMvcConfigurerAdapter:扩展mvc
     * @Author rongtao
     * @Data 2019/4/24 13:41
     */
    @Configuration
    public class InterceptorConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //注册登录超时拦截器,并排除拦截登录请求
            registry.addInterceptor(new SessionInterceptor()).excludePathPatterns("/**/login");
            super.addInterceptors(registry);
        }
    }

     

  7. 相关源码在我的GitHub上可以下载:https://github.com/FllowY/spring-boot-examples/tree/master/spring-boot-cors

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Vue是一种流行的前端框架,而Spring Boot是一种流行的后端框架。在使用Vue与Spring Boot的组合时,可能会遇到跨域问题,特别是在涉及到会话(Session)管理时。 在传统的Web应用中,会话通常是通过在浏览器端存储会话标识符(Session ID),并将其发送到服务器来验证用户身份和状态的方式来管理的。跨域问题出现在当Vue应用与Spring Boot应用部署在不同的域或端口上时,浏览器会阻止前端应用发送跨域请求,以保护用户的安全和隐私。 为了解决跨域问题,我们可以采取以下措施: 1. 在Spring Boot应用中配置跨域请求支持。可以通过使用CORS(Cross-Origin Resource Sharing)配置来允许来自Vue应用的跨域请求。在Spring Boot的配置文件中,可以添加如下配置来允许Vue应用的跨域请求: ``` @Configuration public class CorsConfiguration { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8080") // Vue应用的地址 .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true); } }; } } ``` 2. 在Vue应用中设置代理。Vue提供了一个配置文件vue.config.js,我们可以使用该文件来设置代理,将所有的API请求转发到Spring Boot应用的地址。可以在vue.config.js中添加如下配置: ``` module.exports = { devServer: { proxy: { '^/api': { target: 'http://localhost:8081', // Spring Boot应用的地址 ws: true, changeOrigin: true } } } } ``` 以上配置将会把以/api开头的请求转发到Spring Boot应用的地址。 通过以上两种方式,我们可以解决Vue与Spring Boot之间的跨域问题,并正常进行会话管理。在这种配置下,Vue应用将能够向Spring Boot应用发送跨域请求,并在其中进行会话管理。 ### 回答2: Vue和Springboot是两个常用的开发框架,Vue用于前端开发,而Springboot用于后端开发。在开发过程中,可能会遇到前端Vue与后端Springboot之间的跨域问题。下面将以300字回答Vue与Springboot Session跨域问题。 首先,跨域(Cross-Origin Resource Sharing,CORS)指的是在浏览器中,由于安全策略的限制,不同域名之间进行数据交互时会出现的问题。Vue和Springboot之间的跨域问题可以通过设置后端的响应头来解决。 在Springboot中,可以通过在后端Controller中使用@CrossOrigin注解来设置允许跨域请求的域名。例如,可以在Controller的类上方加上@CrossOrigin注解,并指定允许访问的域名,如@CrossOrigin(origins = "http://localhost:8080")。这样就可以允许Vue前端通过http://localhost:8080域名进行跨域请求。 另外,在Vue中也可以通过配置代理来解决跨域问题。在Vue的配置文件中(例如vue.config.js),可以设置devServer的proxy选项,将跨域请求转发至后端项目的URL。例如,可以将'/api'的请求代理到Springboot的URL上,以解决跨域问题。具体的配置代码如下: module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', ws: true, changeOrigin: true } } } } 以上是解决Vue和Springboot Session跨域问题的两种常见方法。无论是通过在Springboot后端设置响应头允许跨域访问,还是通过在Vue前端配置代理转发请求,都可以解决两者之间的跨域问题,确保数据的正常交互。 ### 回答3: Vue与Spring Boot是一个常用的前后端分离的组合,它们之间确实存在跨域问题。当Vue项目与Spring Boot项目同时运行在不同的端口或域名下时,浏览器会根据同源策略(Same Origin Policy)限制跨域访问。 为了解决这个问题,我们可以通过在后台配置允许跨域访问的方式。在Spring Boot的Controller类上添加`@CrossOrigin`注解,可以允许指定的域名或端口进行跨域访问。例如,我们可以将注解添加在Controller类上,如下所示: ```java @CrossOrigin(origins = "http://localhost:8080") @RestController public class ExampleController { // ... } ``` 在上面的例子中,我们允许`http://localhost:8080`这个域名进行跨域访问。你可以根据实际情况修改域名和端口。 另外,为了确保session跨域访问正常工作,需要在Vue项目中使用`axios`等HTTP请求库时设置`withCredentials`为`true`。这样可以允许携带cookie信息进行跨域请求,以保持session一致性。你可以在Vue项目的请求配置中添加以下代码: ```javascript // 创建axios实例 const service = axios.create({ // ... withCredentials: true // 允许携带cookie信息 }) // 请求拦截器 service.interceptors.request.use( config => { // ... return config }, error => { // ... return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response => { // ... return response }, error => { // ... return Promise.reject(error) } ) export default service ``` 以上就是Vue与Spring Boot解决session跨域问题的一种常见方式。通过在后台允许跨域访问,并在前端设置`withCredentials`为`true`,可以解决Vue与Spring Boot session跨域问题。当然,还可以使用其他方式来解决跨域问题,具体的选择可以根据实际需求和项目情况进行决定。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涛声依旧叭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值