後管平臺的權限控制篇

隱藏真實接口地址,node 作權限轉發到內網接口

因爲開發前期 java 任務緊,沒時間作,全部java接口就沒作相關的權限認證和角色區分。因此這部分工做就由node去實現了,node 轉發到java內網接口,並處理權限的相關認證javascript

前端接口的統一

前端訪問的接口:domain.com/api/__proxy (get,post,put,delete,patch 目前這幾種就夠用了)css

/* 參數含義:name (在node config中定義的接口) 參數含義:type (在node config所對應的服務,好比說py服務仍是java服務) 參數含義:p1 (在node config所對應的服務,用來完成restful風格的接口拼接,多層拼接本身隨便定義) 示例:/api/__proxy?__gateway_method_id=${name}&__gateway_place=${type}&__gateway_p1=${p1} */


// axios 的封裝代碼就不貼出來了,代碼量很少。如下是前端的調用方式

// restful 拼接的狀況
this.$http.gateway.get({
    name: "GET_STATUS_LIST",
    p1: "pickItem"
})

// 無拼接的狀況
this.$http.gateway.get("GET_STATUS_LIST")

複製代碼

node 定義路由,權限驗證,參數處理 (使用的阿里的egg)

// 路由的定義
router.get("/api/__proxy", controller.api.proxy.__proxy)
router.post("/api/__proxy", controller.api.proxy.__proxy)
router.put("/api/__proxy", controller.api.proxy.__proxy)
router.delete("/api/__proxy", controller.api.proxy.__proxy)
router.patch("/api/__proxy", controller.api.proxy.__proxy)

// Controller 層參數的處理
async __proxy() {
		const { method, body } = this.ctx.request
		let query = this.ctx.query
		const apiName = query.__gateway_method_id // 接口的定義
		delete query.__gateway_method_id
		const apiPlace = this.ctx.query.__gateway_place || "ADMIN" // 服務的類型
		delete this.ctx.query.__gateway_place
		const isGetOrDel =  method === "GET" || method === "DELETE"
		let data = isGetOrDel ? this.ctx.query : body
		const options = { method, data }
		if (isGetOrDel) {
			options.dataAsQueryString = true
		} else {
			options.contentType = "json"
		}
		const ret = await this.ctx.service.http.request(apiName, options, apiPlace)
		this.ctx.body = ret
	}

/* service 層 代碼 我大概講下作了什麼 1.根據參數找到config 文件對應得接口完成拼接 2.根據請求類型傳參數 3.發出請求與返回數據統一結構輸出 4.異常狀態嗎異步發送通知 5.而且支持mock數據 */

// 部分代碼示例
async __request(apiName, options = {}, type = "ADMIN") {
        // mock 數據
		let realApiName = typeof apiName === "object" ? apiName.name : apiName
		if (this.ctx.app.config.env === "local" && isJavaAdmin) {
			const hasMockModules = await utils.hasMockModule(this, realApiName)
			if (hasMockModules) {
				const mockModule = utils.getMockModule(this, realApiName)
				if (mockModule.enable && mockModule.mockFn) {
					let mockResult = await mockModule.mockFn(options.data || {})
					mockResult.mockTips = "請注意,這個是本地mock的假數據"
					return mockResult
				}
			}
        }
	
// 根絕參數獲得真實得內網請求地址
            const apiPath = utils.getApi(this.ctx, apiName, type, options)
            
            const result = await this.ctx.curl(apiPath, {
            	method: "POST",
             	dataType: "json",
          	...options
            })
	
// result 包裝過程省略...
}


// 中間件權限的攔截

const utils = require("../lib/utils")
const WHITE_API = ["/api/loginAccount", "/api/loginForDingDing", "/api/baseInfo", "/api/loginOut"]
module.exports = () => {
	return async function(ctx, next) {
		if (ctx.request.path.indexOf("/api/") > 0 && WHITE_API.indexOf(ctx.request.path) === -1) {
			let { user = {}, userIp = "" } = ctx.session || {}
			const currentUserIp = ctx.ips.length > 0 ? ctx.ips[ctx.ips.length - 1] : ctx.ip
			const sameUserIp = currentUserIp === userIp
			if (Object.keys(user).length && sameUserIp) {
				let { user_info = {}, super_admin = false } = user
				let { __gateway_method_id = "", __gateway_place = "" } = ctx.request.query
				let apiKey = utils.getApiKey(ctx, __gateway_method_id, __gateway_place)
				if (apiKey) {
					if (
						((user_info.menus && user_info.menus.indexOf(apiKey)) !== -1 || super_admin) &&
						user_info.status !== 2
					) {
						await next()
					} else {
						ctx.body = {
							code: 4001,
							msg: "你沒有該接口的操做使用權限"
						}
					}
				} else {
					await next()
				}
			} else {
				ctx.status = 401
				ctx.logger.warn("401權限攔截", `接口地址:${ctx.request.url}`)
				ctx.session = null
				ctx.body = { code: 401, msg: "你有權限嘛?就想訪問!" }
			}
		} else {
			await next()
		}
	}
}

複製代碼

權限key輸出到html入口文件 (njk模板)

<!DOCTYPE html>
<html lang="cn">
<head>
    <title>{{title}}</title>
    {% for key, item in meta -%}
    <meta {{item.key}}="{{key}}" content="{{item.value}}">
    {%- endfor %}
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>

    <!--當前的訪問的終端:{{userAgent}}-->
    {% for item in cdn.js -%}
    <link href="{{item}}" rel="preload" as="script">
    {%- endfor %}

    {% if env !== "local" %}
    {% for item in preload.js -%}
    <link href="{{item}}" rel="prefetch" as="script">
    {%- endfor %}
    {% for item in preload.css -%}
    <link rel="prefetch" href="{{item}}" as="style">
    {%- endfor %}
    {% for item in preload.font -%}
    <link rel="preload" crossorigin as="font" href="{{item}}">
    {%- endfor %}
    {% endif %}

    {% for item in cdn.css -%}
    <link rel="stylesheet" href="{{item}}" />
    {%- endfor %}

    {% for item in asset.css -%}
    <link rel="stylesheet" href="{{item}}" />
    {%- endfor %}
</head>
<body>
    <div id="app"></div>
    <script> window.__INITIAL_STATE__ = {baseInfo:{{baseInfo | dump | safe }},userInfo:{{userInfo | dump| safe }}}; </script>
    {% for item in cdn.js -%}
    <script src="{{item}}"></script>
    {%- endfor %}
    {% for item in asset.js -%}
    <script src="{{item}}"></script>
    {%- endfor %}
</body>
</html>
複製代碼

在有時間,有可用服務器的資源狀況下建議你們去折騰一波。node寫內部管理後臺是很好的解決方案,先後端都是js寫起來多舒服鴨!html

egg 的啓動和中止的釘釘通知

剛開始使用egg的時候,以爲每次停服更新還要通知他人也挺不方便的,乾脆就拉個羣,監控,接口問題都自動發送釘釘羣通知。可是通知會有個一個小問題,就是egg再啓動時默認會根據cpu數來啓動對應數量的worker,會致使釘釘通知發送屢次,針對這個問題只須要在啓動的時候隨便記錄一個pid,而後再中止的時候指定worker執行發送信息就能夠了前端

gitlab 鉤子 + Jenkins 構建部署仍是很省心了java

相關文章
相關標籤/搜索