首页 > 编程语言 > Springboot前后端分离项目配置跨域实现过程解析
2020
10-09

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

项目登录流程如下

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

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

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

跨域配置

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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}属性。

拦截器代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
 * 自定义异常
 */
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;
  }
}

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

编程技巧