在开发WEB前后端分离的应用时,在开发环境中,前后端应用访问的主机地址(ip:port)不同;但在生产环境,一般都会使用nginx代理前后端应用使得前后端的访问地址为同一个。所以,前后端应用在不同环境中的某些行为也不相同。比如,在开发环境中,由于浏览器的同源策略,导致前端的ajax请求受限——“No ‘Access-Control-Allow-Origin’ header is present on the requested resource. 解决这个问题,只需要在后端应用返回的headers里面加上响应头:Access-Control-Allow-Origin: *
。但由于nginx代理,生产环境中前后端主机地址一致,为了提高安全,后端并不需要这个设置,所以后端应该根据开发环境做不同的处理。
0x01 前端处理
1. 定义函数 isDev
判断是否是开发环境,在/src/main.js中增加如下代码:1
2
3Vue.prototype.isDev = function(){
return process.env.NODE_ENV == "development" ? true : false
}
2. 根据环境不同,获取相应的前后端地址,在/src/main.js中增加如下代码:1
2
3
4
5
6
7
8
9Vue.prototype.getFrontendUrl = function () {
var frontend_url = this.isDev() ? "/" : "/applicationName/"
return frontend_url
}
Vue.prototype.getBackendUrl = function () {
var runenv = process.env.NODE_ENV
var backend_url = this.isDev() ? "http://localhost:8000/" : "/"
return backend_url
}
3. ajax请求: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
37var x = {}
var backend_url = this.getBackendUrl()
var forbidden = false
var connectError = false
$.ajax({
url: backend_url + "disguiserApi/delapi?aid=" + this.aid,
dataType: "JSON",
async: false,
success: function (ret) {
x = ret
},
error: function (e) {
if(e.status==403){
forbidden = true
}else{
connectError = true
}
}
})
if (connectError) {
this.$message.error('服务器异常!');
} else if(forbidden){
var frontend = this.getFrontendUrl()
var next = encodeURIComponent(window.location.pathname.substr(1) + window.location.search)
window.location.href = frontend + "login?next="+next
} else if (x.status != 0) {
this.$message.error(x.msg||"未知异常")
} else {
var frontendurl = this.frontend
this.$message({
type: 'success',
message: '删除成功!'
});
setTimeout(function () {
window.location.href = frontendurl + "dashboard?sid=" + this.systemInfo.sysid
}, 1500)
}
0x02 后端处理
1. 启动应用时,根据命令参数设置环境,默认为prod环境:1
2
3
4
5
6
7
8
9'''1) file: setting.py'''
ENV = "prod"
'''2) file: server.py'''
#启用开发环境 python server.py dev
from setting import settings
if len(sys.argv) == 2 and sys.argv[1] == "dev":
setting.ENV = "dev"
2. 开发环境不对用户做登录认证:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23'''参照tornado.web.authenticated()修饰器函数,不能直接使用该修饰器,
因为该修饰器默认未登录动作会跳转到setting里面的login_url,但是前后端
分离应用中,应该将跳转动作交给前端去处理'''
def myAuthenticated(method):
if ENV.lower() == "dev":
def warpper(*args, **kwargs):
ret = method(*args, **kwargs)
return ret
return warpper
else:
def wrapper(self, *args, **kwargs):
if not self.current_user:
raise HTTPError(403)
return method(self, *args, **kwargs)
return wrapper
#使用如下
class SaveModuleHandler(BaseHandler):
def post(self):
...
3. 开发环境设置响应头解决同源限制:1
2
3
4
5
6
7class BaseHandler(tornado.web.RequestHandler):
def set_default_headers(self):
if ENV.lower() == "dev":
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
self.set_header('Access-Control-Allow-Methods', 'POST,GET,PUT,DELETE,OPTIONS')
4. 开发环境禁用xsrf校验,并设置xsrf白名单:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class BaseHandler(tornado.web.RequestHandler):
_xsrf_WhiteList = ["/uri1","/uri2","/uri3"]
def check_xsrf_cookie(self):
if ENV.lower() == "dev":
return
reqPath = self.request.path
if not any([reqPath in url for url in self._xsrf_WhiteList]):
token = (self.get_argument("_xsrf", None) or
self.request.headers.get("X-Xsrftoken") or
self.request.headers.get("X-Csrftoken"))
if not token:
raise HTTPError(403, "服务器内部错误1")
_, token, _ = self._decode_xsrf_token(token)
_, expected_token, _ = self._get_raw_xsrf_token()
if not token:
raise HTTPError(403, "服务器内部错误2")
if not _time_independent_equals(utf8(token), utf8(expected_token)):
raise HTTPError(403, "服务器内部错误3")
else:
return