Springboot前后端分离项目配置跨域实现过程解析

 更新时间:2020年08月07日 10:31:47   作者:william_zhao  
这篇文章主要介绍了Springboot前后端分离项目配置跨域实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

项目登录流程如下

用户进入前端登录界面,输入账号密码等,输入完成之后前端发送请求到后端(拦截器不会拦截登录请求),后端验证账号密码等成功之后生成Token并存储到数据库,数据库中包含该Token过期时间,然后返回生成的Token到前端。

前端收到Token,表示登录成功,把这个Token存储本地。然后跳转到用户中心页面,用户中心页面在ajax的请求头中带上Token,跟随请求用户数据接口一起带到后端。

后端通过拦截器拦截到这个请求,去判断这个Token是否有效,有效就放过去做他该做的事情,无效就抛出异常。

跨域配置

先说一下这个前后分离的项目,已经配置过跨域这些问题。我这里后端WebMvcConfig配置的方式如下:

import com.zdyl.devicemanagement.interceptor.AccessInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
  @Resource
  private WebServerConfig webServerConfig;

  @Bean
  public AccessInterceptor getAccessInterceptor() {
    return new AccessInterceptor();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    List<String> excludeUrl = new ArrayList<>();
    excludeUrl.add("/error");
    excludeUrl.add("/v1/zdyl/downloadFile");
    excludeUrl.add("/v1/zdyl/lcoStation/qrcode/**");
    excludeUrl.add("/devicemanagement/images/**/*");
    excludeUrl.add("/upgrade/**");
    excludeUrl.add("/v1/zdyl/login/**");
    excludeUrl.add("/NewsImage/**");
    excludeUrl.add("/v1/zdyl/equipment/alarm/toExcel/test");
    excludeUrl.add("/v1/zdyl/deviceMonitoring/get/alarm/toExcel/**");

    registry.addInterceptor(getAccessInterceptor()).addPathPatterns("/**")
        .excludePathPatterns(excludeUrl);
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    List<String> locations = new ArrayList<String>();
    locations.add("classpath:/META-INF/resources/");
    locations.add("classpath:/resources/");
    locations.add("classpath:/public/");
    locations.add("file:" + webServerConfig.getUploadFileLocation());
    locations.add("file:" + webServerConfig.getPicpath());
    locations.add("file:" + webServerConfig.getProjectsource());

    String[] myArray = new String[locations.size()];
    registry.addResourceHandler("/**").addResourceLocations(locations.toArray(myArray));
  }

  @Bean
  public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
  }

  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
        .allowedHeaders("*")
        .allowCredentials(true)
        .allowedOrigins("*")
        .allowedMethods("POST", "GET", "DELETE", "PUT", "OPTIONS")
        .maxAge(3600);
  }
}

前端每次发送请求也都有在ajax里面设置xhrFields:{withCredentials: true}属性。

拦截器代码

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zdyl.devicemanagement.common.exception.RRException;
import com.zdyl.devicemanagement.common.utils.AccountNumber;
import com.zdyl.devicemanagement.common.utils.RedisSavePrefix;
import com.zdyl.devicemanagement.common.utils.RedisUtils;
import com.zdyl.devicemanagement.common.utils.SystemConstants;
import com.zdyl.devicemanagement.entity.LcoUsers;
import com.zdyl.devicemanagement.entity.Login;
import com.zdyl.devicemanagement.service.LcoUsersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

@Slf4j
public class AccessInterceptor extends HandlerInterceptorAdapter {

  @Resource
  private RedisUtils redisUtils;
  @Resource
  private LcoUsersService lcoUsersService;

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    log.info("------------------------AccessInterceptor-------------------------");
    if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
      return super.preHandle(request, response, handler);
    }
    //获取请求token,如果token不存在,直接返回401
    String token = getRequestToken(request);
    String loginId = getRequestloginId(request);
    if (StringUtils.isEmpty(token)) {
      throw new RRException("token为空", 401);
    }
    if (StringUtils.isEmpty(loginId)) {
      throw new RRException("loginId为空", 401);
    }
    Object users = redisUtils.getObject(redisUtils.getKey(RedisSavePrefix.Login, loginId), AccountNumber.loginDataBase);
    if (users == null) {
      throw new RRException("用户尚未登录", 401);
    }
    Login loginUser = JSONObject.parseObject(JSON.toJSONString(users), Login.class);
    if (!loginUser.getToken().equals(token)) {
      throw new RRException("token不匹配", 401);
    }
    Date loginTime = loginUser.getLoginTime();
    long exitTime = loginTime.getTime() / 1000 + 7200;
    long time = new Date().getTime();
    long nowTime = new Date().getTime() / 1000;
    if (nowTime > exitTime) {
      throw new RRException("token已过期!", 401);
    }
    QueryWrapper<LcoUsers> lcoUsersQueryWrapper = new QueryWrapper<>();
    lcoUsersQueryWrapper.eq("phone", loginUser.getLoginID());
    LcoUsers lcoUsers = lcoUsersService.getOne(lcoUsersQueryWrapper);
    request.setAttribute(SystemConstants.CURRENTUSER, lcoUsers);
    return super.preHandle(request, response, handler);
  }

  /**
   * 获取请求的token
   */
  private String getRequestToken(HttpServletRequest httpRequest) {
    //从header中获取token
    String host = httpRequest.getHeader("token");

    //如果header中不存在token,则从参数中获取token
    if (StringUtils.isEmpty(host)) {
      host = httpRequest.getParameter("token");
    }
//    if (StringUtils.isEmpty(host)) {
//      Cookie[] cks = httpRequest.getCookies();
//      for (Cookie cookie : cks) {
//        if (cookie.getName().equals("yzjjwt")) {
//          host = cookie.getValue();
//          return host;
//        }
//      }
//    }
    return host;
  }

  /**
   * 获取请求的loginId
   */
  private String getRequestloginId(HttpServletRequest httpRequest) {
    //从header中获取token
    String loginId = httpRequest.getHeader("loginId");

    //如果header中不存在token,则从参数中获取token
    if (StringUtils.isEmpty(loginId)) {
      loginId = httpRequest.getParameter("loginId");
    }
//    if (StringUtils.isEmpty(loginId)) {
//      Cookie[] cks = httpRequest.getCookies();
//      for (Cookie cookie : cks) {
//        if (cookie.getName().equals("yzjjwt")) {
//          loginId = cookie.getValue();
//          return loginId;
//        }
//      }
//    }
    return loginId;
  }

/**
 * 对跨域提供支持
 */
protected boolean addCors(ServletRequest request, ServletResponse response) throws Exception {
  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
  httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
  httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
  // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
  if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
    httpServletResponse.setStatus(HttpStatus.OK.value());
    return false;
  }
  return super.preHandle(request, response);
}


}

自定义异常RRException代码

/**
 * 自定义异常
 */
public class RRException extends RuntimeException {
  private static final long serialVersionUID = 1L;

  private String message;
  private String code = "INVALID";
  private int status = 0;

  public RRException(String msg) {
    super(msg);
    this.message = msg;
  }

  public RRException(String msg, Throwable e) {
    super(msg, e);
    this.message = msg;
  }

  public RRException(String msg, String code) {
    super(msg);
    this.message = msg;
    this.code = code;
  }
  public RRException(String msg, int status) {
    super(msg);
    this.message = msg;
    this.status = status;
  }

  public RRException(String msg, String code, Throwable e) {
    super(msg, e);
    this.message = msg;
    this.code = code;
  }

  public String getMsg() {
    return message;
  }

  public void setMsg(String msg) {
    this.message = msg;
  }

  public String getCode() {
    return code;
  }

  public void setCode(String code) {
    this.code = code;
  }

  public int getStatus() {
    return status;
  }

  public void setStatus(int status) {
    this.status = status;
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • java微信企业号开发之发送消息(文本、图片、语音)

    java微信企业号开发之发送消息(文本、图片、语音)

    这篇文章主要为大家详细介绍了java微信企业号开发之发送消息,发送类型包括文本、图片、语音,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • SpringBoot中Token登录授权、续期和主动终止的方案流程分析

    SpringBoot中Token登录授权、续期和主动终止的方案流程分析

    SpringBoot项目中,基于Token的登录授权方案主要有两种:利用Session/Cookie和JWT,Cookie/Session方案有状态,不适合分布式架构,而JWT虽无状态,但存在过期时间不可强制失效、一次性等缺点,本文介绍SpringBoot中Token登录授权、续期和主动终止的方案,感兴趣的朋友一起看看吧
    2024-09-09
  • 在SSM中配置了事务控制但没生效的问题

    在SSM中配置了事务控制但没生效的问题

    这篇文章主要介绍了在SSM中配置了事务控制但没生效的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • SpringBoot MDC全局链路最新完美解决方案

    SpringBoot MDC全局链路最新完美解决方案

    MDC 在 Spring Boot 中的作用是为日志事件提供上下文信息,并将其与特定的请求、线程或操作关联起来,通过使用 MDC,可以更好地理解和分析日志,并在多线程环境中确保日志的准确性和一致性,这篇文章主要介绍了SpringBoot MDC全局链路解决方案,需要的朋友可以参考下
    2023-08-08
  • Java如何设置PDF文档背景色详解

    Java如何设置PDF文档背景色详解

    这篇文章主要介绍了Java如何设置PDF文档背景色详解,一般生成的PDF文档默认的文档底色为白色,我们可以通过一定方法来更改文档的背景色,以达到文档美化以及保护双眼的作用。 以下内容提供了Java编程来设置PDF背景色的方法,需要的朋友可以参考下
    2019-07-07
  • Java实现三子棋游戏

    Java实现三子棋游戏

    这篇文章主要为大家详细介绍了Java实现三子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 如何通过Maven仓库安装Spire系列的Java产品

    如何通过Maven仓库安装Spire系列的Java产品

    这篇文章主要介绍了如何通过Maven仓库安装Spire系列的Java产品,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • SpringBoot3如何集成Hazelcast

    SpringBoot3如何集成Hazelcast

    Hazelcast是一款优秀的开源内存数据网格平台,它能够提供分布式数据存储和缓存解决方案,通过与SpringBoot3的整合,开发者可以轻松实现分布式缓存、数据共享和会话管理等功能,Hazelcast的内存数据网格特性支持高性能的缓存系统,能够减少数据库访问次数,提升应用性能
    2024-10-10
  • Java实现文件压缩为zip和解压zip压缩包

    Java实现文件压缩为zip和解压zip压缩包

    这篇文章主要为大家介绍了Java如何实现将文件压缩为zip以及解压zip压缩包,文中的示例代码讲解详细,感兴趣的小伙伴可以动手尝试一下
    2022-06-06
  • Java常用的数据脱敏方法(手机、邮箱、身份证号)

    Java常用的数据脱敏方法(手机、邮箱、身份证号)

    这篇文章主要给大家介绍了关于Java常用的数据脱敏(手机、邮箱、身份证号)的相关资料,信息脱敏对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护,需要的朋友可以参考下
    2023-07-07

最新评论