vue2+express前后端分离跨域session等问题

基于 vue2 + express 的 RBAC 角色权限验证前后端分离项目。前端vue2 负责路由控制,数据渲染,后端 express 负责数据库操作,RBAC权限控制。

前端:

  • 脚手架:vue-cli
  • 选型:vue2 + vuex + vue-router + axios + iview + ES6

后端:

  • 脚手架:express-generator
  • 选型:express4 + mongodb

结构整合:

使用vue-cli生成的项目,默认安装了简易的 express,该 express 的启动文件为build/dev-server.js。执行cnpm start启动该 express,实际主要是启一个服务来运行 Vue。所以我们后端部分不以该文件为入口。
后端部分的目录使用express-generator来生成,由于我们做了前后端分离,所以express 的任务只是提供接口,做数据库操作,所以我们把view、pubic目录删掉。
最后将前后端代码整合一下。在 vue-cli 生成的目录下创建 server 目录用来存放后端代码,将package.json文件合并,然后添加启动后端的脚本”server”: “node server/bin/www”

package.json 文件如下:

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
{
"name": "backend",
"version": "1.0.0",
"description": "backend build by webpack+vue2+express",
"author": "ludis <service@ldsun.com>",
"private": true,
"scripts": {
"dev": "node build/dev-server.js",
"start": "node build/dev-server.js",
"build": "node build/build.js",
"lint": "eslint --ext .js,.vue src",
"server": "node server/bin/www"
},
"dependencies": {
"axios": "^0.16.1",
"iview": "^2.0.0-rc.15",
"moment": "^2.18.1",
"monk": "^5.0.2",
"vue": "^2.3.3",
"vue-awesome": "^2.3.1",
"vue-axios": "^2.0.2",
"vue-router": "^2.3.1",
"vuex": "^2.3.1",
"async": "^2.1.4",
"cors": "^2.8.3",
"body-parser": "~1.15.1",
"cookie-parser": "~1.4.3",
"debug": "~2.2.0",
"express": "~4.13.4",
"express-session": "latest",
"formidable": "^1.1.1",
"morgan": "~1.7.0"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
"babel-core": "^6.22.1",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.10",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^3.19.0",
"eslint-friendly-formatter": "^2.0.7",
"eslint-loader": "^1.7.1",
"eslint-plugin-html": "^2.0.0",
"eslint-config-standard": "^6.2.1",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^2.0.1",
"eventsource-polyfill": "^0.9.6",
"express": "^4.14.1",
"extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.3",
"webpack-bundle-analyzer": "^2.2.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"opn": "^4.0.2",
"optimize-css-assets-webpack-plugin": "^1.3.0",
"ora": "^1.2.0",
"rimraf": "^2.6.0",
"url-loader": "^0.5.8",
"vue-loader": "^12.1.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.3.3",
"webpack": "^2.6.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

这样,我们在执行 cnpm start 及 cnpm run server 后即可分别启动前端和后端服务。项目目录如下:

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
.
├── README.md
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ └── prod.env.js
├── index.html
├── mongo.json
├── node_modules
├── package.json
├── server // 后端代码
│ ├── app.js
│ ├── bin
│ ├── lib
│ ├── routes
│ └── service
├── src
│ ├── App.vue
│ ├── assets
│ ├── components
│ ├── main.js
│ └── router
└── static

这样项目的结构比较清晰。 遇到的问题:

  • 在开发阶段,vue运行与localhost:8080端口,express运行于localhost:3000端口。vue在跨域请求接口时会提示跨域请求不被允许。这里使用express的cros模块,允许8080端口的请求:
1
2
3
4
5
const cors = require('cors');  
app.use(cors({
origin:['http://localhost:8080'],
methods:['GET','POST'],
}));

这样就解决了跨域请求问题。

  • 在 vue 请求后端接口时,我选用了vue官方推荐的 axios 作为http请求组件。涉及到登陆验证,所以 express 使用express-session及connect-redis模块,将session存在redis中。在登录成功后,将登录信息存储于 req.session.user 中,当有其他请求时,通过判断 req.session.user 来判断是否登录及登录是否过期。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const session = require('express-session');  
const redisStore = require('connect-redis')(session);
app.use(session({
secret: 'backend123...',
name: 'backend',
store: new RedisStore(options),
cookie: {
maxAge: 1000 * 60 * 60,
},// 1h
resave: true,
saveUninitialized: false,
}));
// 登录
app.post('/login', function(req, res, next){
...
// 登录成功,拿到用户信息,设置session
req.session.user = userinfo
})
// 有其他请求时判断登陆
app.user('*', function(req, res, next){
if(req.session.user)next() // 如果session存在,是登录状态
else .... // 未登录
})

但是通过调试发现,每次登陆后用户信息确实存到session中了,但是下次请求时session却为空!最后发现是由于跨域问题引起的。由于使用了cros安全代理,默认是拒绝接收浏览器发送的cookie,所以每次请求都不带cookie,sessionID自然对不上,所以每次都会是新的session,导致没法保存登陆状态。给cors配置参数,允许接收cookie即可。

1
2
3
4
5
app.use(cors({  
origin:['http://localhost:8080'],
methods:['GET','POST'],
credentials: true
}));

但是加完了发现问题还是没有解决,那么就是 axios 的问题了,原来axios默认发送http请求时是不会在头信息中附带cookie的,所以也需要配置开启。直接在main.js中全局设置:

1
2
3
4
import axios from 'axios'    // axios  
import VueAxios from 'vue-axios'
axios.defaults.withCredentials = true // 请求携带cookie信息
Vue.use(VueAxios, axios)

这样,问题终于解决了233…心累。 坑先填到这,持续更新。

github:https://github.com/flute/webpackvue2expressmongodbbackend

参考资料:http://www.ruanyifeng.com/blog/2016/04/cors.html

Author

Ludis

Posted on

2017-06-06

Updated on

2018-02-13

Licensed under

Comments