瑩澍 2026-04-13 05:29 采纳率: 0%
浏览 7

跨域问题不知道怎么解决(语言-java)


package com.interceptor;

import java.io.IOException;
import java.io.PrintWriter;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.annotation.IgnoreAuth;
import com.entity.TokenEntity;
import com.service.TokenService;
import com.utils.R;

@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

    public static final String LOGIN_TOKEN_KEY = "Token";

    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // ========== 1. 先设置跨域响应头 ==========
        String origin = request.getHeader("Origin");
        if (StringUtils.isNotBlank(origin)) {
            response.setHeader("Access-Control-Allow-Origin", origin);
        } else {
            response.setHeader("Access-Control-Allow-Origin", "http://localhost:8082");
        }
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");

        // ========== 2. 放行 OPTIONS 预检请求 ==========
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;   // ← 改这里
        }

        // ========== 3. Token 校验逻辑 ==========
        IgnoreAuth annotation;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
        } else {
            return true;
        }

        String token = request.getHeader(LOGIN_TOKEN_KEY);

        if (annotation != null) {
            return true;
        }

        TokenEntity tokenEntity = null;
        if (StringUtils.isNotBlank(token)) {
            tokenEntity = tokenService.getTokenEntity(token);
        }

        if (tokenEntity != null) {
            request.getSession().setAttribute("userId", tokenEntity.getUserid());
            request.getSession().setAttribute("role", tokenEntity.getRole());
            request.getSession().setAttribute("tableName", tokenEntity.getTablename());
            request.getSession().setAttribute("username", tokenEntity.getUsername());
            return true;
        }

        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONObject.toJSONString(R.error(401, "请先登录")));
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
        return false;
    }
}
  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-04-13 05:30
    关注

    上午好☀️☀️☀️️
    本答案参考通义千问

    跨域问题(CORS, Cross-Origin Resource Sharing)是前端与后端进行通信时常见的问题,尤其是在前后端分离的架构中。你提供的代码是一个 Spring Boot 的拦截器类 AuthorizationInterceptor,用于处理 Token 验证和跨域配置。但是当前的跨域设置方式存在一些 不规范潜在的问题,需要进行优化。


    ✅ 一、为什么会出现跨域问题?

    当你的前端应用(例如部署在 http://localhost:8082)请求后端服务(例如部署在 http://localhost:8081),浏览器会根据同源策略判断是否允许该请求。如果 域名、协议或端口不同,就会触发 CORS 预检请求(OPTIONS),并要求后端返回相应的响应头信息。


    ✅ 二、当前代码中的跨域配置问题

    你目前的跨域配置是在 preHandle 方法中手动设置的响应头,但这种做法有以下 问题

    1. 不规范:Spring Boot 提供了更标准的 CORS 配置方式。
    2. 无法处理 OPTIONS 请求:虽然你对 OPTIONS 请求做了处理,但可能没有完全覆盖所有情况。
    3. 动态 Origin 设置不稳定:使用 request.getHeader("Origin") 来动态设置 Access-Control-Allow-Origin 可能导致安全风险。
    4. 无法处理复杂请求(如 Cookie、认证等):需要额外设置 Access-Control-Allow-Credentials 等字段。

    ✅ 三、推荐解决方案(标准做法)

    🔧 方案 1:通过 @CrossOrigin 注解全局设置(适用于简单项目)

    如果你的项目规模较小,可以在主类或 Controller 上添加注解:

    @SpringBootApplication
    @CrossOrigin(origins = "http://localhost:8082", allowCredentials = "true")
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    📌 注意:这种方式只适用于简单的接口,不适合复杂的场景。


    🔧 方案 2:通过 WebMvcConfigurer 全局配置 CORS(推荐)

    这是最标准的做法,适用于大多数 Spring Boot 项目。

    步骤 1:创建一个配置类
    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("http://localhost:8082") // 允许的来源
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的方法
                    .allowedHeaders("*") // 允许的请求头
                    .exposedHeaders("Token") // 暴露给客户端的响应头
                    .allowCredentials(true); // 是否允许凭证(Cookie)
        }
    }
    

    📌 重点说明

    • allowedOrigins 是你前端的地址,可以是多个(用逗号分隔)。
    • allowCredentials 必须设为 true,如果你的请求中包含 Cookie 或 Token。
    • 如果你使用了 JWT 或其他 Token 认证,确保 Token 响应头被正确暴露。

    🔧 方案 3:修改你的拦截器逻辑(如果必须保留)

    如果你确实要通过拦截器处理跨域,建议优化如下:

    修改后的 AuthorizationInterceptor 代码:
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
        // ========== 1. 跨域配置(推荐统一通过 WebMvcConfigurer 处理) ==========
        String origin = request.getHeader("Origin");
        if (StringUtils.isNotBlank(origin)) {
            response.setHeader("Access-Control-Allow-Origin", origin);
        } else {
            response.setHeader("Access-Control-Allow-Origin", "http://localhost:8082");
        }
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
    
        // ========== 2. 放行 OPTIONS 预检请求 ==========
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
    
        // ========== 3. Token 校验逻辑(保持不变) ==========
        IgnoreAuth annotation;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
        } else {
            return true;
        }
    
        String token = request.getHeader(LOGIN_TOKEN_KEY);
    
        if (annotation != null) {
            return true;
        }
    
        TokenEntity tokenEntity = null;
        if (StringUtils.isNotBlank(token)) {
            tokenEntity = tokenService.getTokenEntity(token);
        }
    
        if (tokenEntity != null) {
            request.getSession().setAttribute("userId", tokenEntity.getUserid());
            request.getSession().setAttribute("role", tokenEntity.getRole());
            request.getSession().setAttribute("tableName", tokenEntity.getTablename());
            request.getSession().setAttribute("username", tokenEntity.getUsername());
            return true;
        }
    
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONObject.toJSONString(R.error(401, "请先登录")));
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
        return false;
    }
    

    ⚠️ 注意:

    • 不推荐在拦截器中处理跨域,因为这可能导致重复设置响应头,或者在某些情况下失效。
    • 如果你仍然希望在拦截器中处理跨域,请确保 只在预检请求中处理,避免干扰正常业务逻辑。

    ✅ 四、总结:推荐做法

    | 方案 | 推荐程度 | 说明 | |------|----------|------| | 使用 @CrossOrigin 注解 | 中 | 适合小型项目,简单快捷 | | 使用 WebMvcConfigurer 配置 CORS | ⭐⭐⭐⭐⭐ | 最标准、最推荐的方式 | | 在拦截器中处理跨域 | ❌ | 不推荐,容易出错 |


    ✅ 五、最终建议

    强烈建议你采用方案 2:通过 WebMvcConfigurer 全局配置 CORS。

    这样不仅能够统一管理跨域规则,还能保证安全性、可维护性和性能。同时,它不会干扰你的 Token 验证逻辑。


    如有更多关于 Token 认证或跨域的疑问,欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 4月13日