好处:

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");