1. 程式人生 > 程式設計 >Springboot前後端分離專案配置跨域實現過程解析

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,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,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;
  }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。