8.关于Express应用项目实践

上面说了很多概念性的内容,这部分就针对这次项目来说具体应用。这次后台整体下移,也就是从之前写java的portal层接口下移到对接设备的core层接口,出于安全性考虑,前台不能直接访问core层接口,需要用node在中间连接(认证部分这次用的casClient后面再细说)。主要的三个需求是接口转发、静态资源挂载,还有个服务器端口跳转;下面我会针对这三个需求说一下实现的过程吧 : )

这是主要require的几个模块:

var express = require('express')
var proxy = require('http-proxy-middleware')
var request = require('request')
var bodyParser = require('body-parser')
var path = require('path')
var history = require('connect-history-api-fallback')

1.接口转发

这部分是node实现的最基本要求,即前台调用express定义的前台接口,再由中间件向core层调用对应的接口取数据。

// 前台调用接口
this.$axios({
    method: 'GET',
    url: 'http://127.60.0.1:3000/yuhui'
}).then(r=>{
    console.log(r.data)
})

//express捕获到做匹配
var app = express()
app.use('/yuhui', function(req,res,next){
    // 向core层请求数据
    res.jsonp(obj) // 返回给前台
})

app.listen(3000,'127.60.0.2')

2.静态资源挂载

这个需求是为了用docker容器集成到主系统时,为了减少对接模块调试方便(vue+node+nginx到vue+node),就对前端的静态资源调用放到express来做。挂载静态资源是针对于build之后用于生产环境下的dist文件夹资源的调用,而不是开发环境的入口html,这个要注意哈!

app.use(express.static('../dist'))
app.listen(3000,'127.60.0.2') //浏览器访问127.60.0.2:3000就可以访问项目了

这样挂载的项目路径中会带有一个#,这是因为router默认采用hash模式,因此要对router做一下修改:

export default new Router({
  mode: 'history', //修改路由模式
  routes: [
    {
        path: '/',
        component: Index
    },{
        path: '/content/:id',
        component: Content
    },{
        path: '/viewPage',
        component: ViewPage
    }
  ]
})

但是这样做又会带来一个问题,这样的访问是针对于文件夹的静态资源路径而不是我们在开发环境中做的路由路径,会匹配不到资源返回404;所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面(也就是从主页面重进再按照路由的路径显示页面),这个页面就是你 app 依赖的页面。当然这个要自己写一个node处理的中间件可以,不过npm这么好的社区不用就很浪费: )

npm install –save connect-history-api-fallback,源码在github上,有兴趣的同学可以去研究下,然后express可以以第三方中间件的形式使用,很方便。这里在附上官网的其他解决办法,assetsPublicPath路径配置解释

var history = require('connect-history-api-fallback')
app.use(history()) //注意顺序!这个中间件的使用要放在挂载静态资源之前

build: {
  // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

  // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',  
    //因为history默认会修改根目录,未配置前是相对路径;所以在webpack build命令配置需要修改成'/',保证根目录不变,具体解释点上面链接
}

3.服务器端口跳转

这个需求呢是我撸码的时候自己提的= =使用环境就是服务器的一个端口挂了,可以跳转到另一个端口,相当于备用;利用http-proxy-middleware模块起一个代理服务器,用于转发端口或者IP+端口。

var proxy = require('http-proxy-middleware')
app.use('/yuhui', proxy({
    target: 'http://127.60.0.2:1234',
    changeOrigen: true,
    onProxyRes: function(proxyRes,req,res){
        res.header("Access-Control-Allow-Origin", "*");  
        res.header("Access-Control-Allow-Headers", "X-Requested-With, content-type");  
        res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");  
        res.header("X-Powered-By",' 3.2.1')  
        res.header("Content-Type", "application/json;charset=utf-8");  
    },
    cookieDomainRewrite: '' // 修改响应信息中的cookie域名,可以为false
}))

app2.use('/yuhui', function(req,res,next){
    res.send('yuhui')
})
app.listen(3000,'127.60.0.2') // 代理服务器端口
app2.listen(1234,'127.60.0.2')

做完这几个需求,感触比较深的是:基于node开发要有一种模块化和社区的概念,这样无论是执行效率还是开发效率都会有很高的提升。(模块引用和中间件加载太舒服了~)

9.基于casServer的登录认证

1.cas server + cas client 单点登录原理介绍

为了集成到cloudOS系统上需要对现在的项目做单点登录(保持单账号登录所有系统),打算用casServer做单点登录和权限控制以及用docker部署,关于docker的部分我后面研究完了在写上来吧:)
从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server需要独立部署,主要负责对用户的认证工作;CAS Client负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。

CAS基础协议

CAS的详细登录流程

关于cas的详细介绍,我也是从这两篇文章上了解的:CAS基础协议CAS的详细流程

2.配置介绍

这次项目呢,我主要做的就是casClient部分的逻辑,作为客户端和casServer的中间件,在不同情况下的重定向操作对于系统正常运行有很大影响,但考虑到客户端、casClient和casServer之间的数据交换和本地ticket验证等复杂的逻辑,可以考虑用npm安装合适的基于node的casClient,再做一些基本的配置即可。

这次用的是connect-cas2模块,npm官网上的配置介绍是英文的,看着不太方便,附上中文版的吧,这里列出几个比较重要的属性:

var ConnectCas = require('connect-cas2')
var casClient = new ConnectCae({
    servicPrefix: // 网站根目录
    serverPath: // casServer根路径,会和paths.serviceValidate拼接成casServer校验ticket路径
    paths: {
        validate: // 用于Client端校验ST路径,就是cas-connect2这个模块本地验证ST的js文件
        serviceValidate: // 用于casServer校验ticket路径
        login: // 会和serverPath拼接组成CAS的登录页面
        logout: // 注销路径
        ...
    }
    ...
})
app.use(casClient.core()) // 顺序要在bodyPaser之前

写完需求搬blog内容(2020/1/6 tencent)