前后端独立开发,动静分开部署
好处:
1、让前后端的开发人员可以更方便的开发和调试,各自专注于处理自己领域的问题;前端开发人员不需要知道后台使用什么技术实现,前后台通信都是基于Rest接口,像是两个独立的系统;
2、前端程序都是属于静态资源文件,单独部署,有利于提高访问性能;从Nginx访问静态资源比从Tomcat访问要更快速;
3、在针对静态资源文件做CDN优化时,独立部署一台静态资源服务器即可,CDN回源不会对应用程序服务器造成影响。
解决安全验证问题
后端是基于Shiro的安全验证框架
前端通过AJAX访问后端Rest接口时,如果后端进行安全验证时,在没有验证通过的情况下,给返回:
{ error : 'NOT_AUTHENTICATED', loginUrl : '{url}' }
前端要有一个针对AJAX请求的全局处理,来检测该情况。
axios.interceptors.response.use(function (response) { if (response.data.error === 'NOT_AUTHENTICATED') { redirect(response.data.loginUrl) return Promise.reject(response.data.error) } return response }, function (error) { return Promise.reject(error) })
重定向到登录页面去验证,需要传一个backUrl参数,以便于完成登录验证后能跳转回原来的页面。
如何来处理这个backUrl呢?有两种方案:
1、前端在发出AJAX请求时,将当前页面地址(包括路由地址)发送给服务端,由服务端来完成整个loginUrl的装配工作。
axios.interceptors.request.use(function (config) { config.headers.hash = window.location.hash return config }, function (error) { return Promise.reject(error) })
服务端要获取当前客户端页面的完整地址(包括路由地址)就是通过下面的代码:
// 获取发送请求的来源网页地址 String sourceUrl = request.getHeader("referer"); if (StringUtils.isNotEmpty(sourceUrl)) { sourceUrl += request.getHeader("hash"); sourceUrl = URLEncoder.encode(sourceUrl, "utf-8"); }
这里是在request的head部分增加了个一项hash,但是这个自定义的head项目在跨域访问时又不被允许。
在跨域访问的情况下,默认客户端会发送两次请求,第一次是探查,Method=OPTION,此时服务端要设置所允许的head项:
response.setHeader("Access-Control-Allow-Headers", "accept,content-type,hash");
2、另一种方式就是服务端下发loginUrl,里面带有一个占位符,比如“${hash}”,到达客户端后,由客户端去替换成当前页面的完整地址。
这样就不需要自定义request的head项了。
当跳转到一个loginUrl进行登录验证后,如果验证通过了建立了Session,这个Session如何跟静态资源网站共享呢?
为了在应用程序和前端静态页面之间共享Session,需要把Shiro的SessionCookie保存在根域名之下(动静两个应用必须是相同的根域名)。
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="sid"/> <property name="httpOnly" value="false"/> <property name="maxAge" value="-1"/> <property name="domain" value=".6655.la"/> </bean>
当设置到这一步时,又会发现跨域情况下,向服务端发送请求,Cookie是不会携带的。
必须在第一次发送OPTION探查时,要明确告诉客户端允许携带Cookie
response.setHeader("Access-Control-Allow-Credentials", "true");