mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-05 22:30:28 +08:00
实现HTTPS配置
This commit is contained in:
483
web/public/js/components/server/ssl-config-box.js
Normal file
483
web/public/js/components/server/ssl-config-box.js
Normal file
@@ -0,0 +1,483 @@
|
||||
Vue.component("ssl-config-box", {
|
||||
props: ["v-ssl-policy", "v-protocol"],
|
||||
created: function () {
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.sortableCipherSuites()
|
||||
}, 100)
|
||||
},
|
||||
data: function () {
|
||||
let policy = this.vSslPolicy
|
||||
if (policy == null) {
|
||||
policy = {
|
||||
id: 0,
|
||||
isOn: true,
|
||||
certRefs: [],
|
||||
certs: [],
|
||||
clientCARefs: [],
|
||||
clientCACerts: [],
|
||||
clientAuthType: 0,
|
||||
minVersion: "TLS 1.1",
|
||||
hsts: null,
|
||||
cipherSuitesIsOn: false,
|
||||
cipherSuites: [],
|
||||
http2Enabled: true
|
||||
}
|
||||
} else {
|
||||
if (policy.certRefs == null) {
|
||||
policy.certRefs = []
|
||||
}
|
||||
if (policy.certs == null) {
|
||||
policy.certs = []
|
||||
}
|
||||
if (policy.clientCARefs == null) {
|
||||
policy.clientCARefs = []
|
||||
}
|
||||
if (policy.clientCACerts == null) {
|
||||
policy.clientCACerts = []
|
||||
}
|
||||
if (policy.cipherSuites == null) {
|
||||
policy.cipherSuites = []
|
||||
}
|
||||
}
|
||||
|
||||
let hsts = policy.hsts
|
||||
if (hsts == null) {
|
||||
hsts = {
|
||||
isOn: false,
|
||||
maxAge: 0,
|
||||
includeSubDomains: false,
|
||||
preload: false,
|
||||
domains: []
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
policy: policy,
|
||||
|
||||
// hsts
|
||||
hsts: hsts,
|
||||
hstsOptionsVisible: false,
|
||||
hstsDomainAdding: false,
|
||||
addingHstsDomain: "",
|
||||
hstsDomainEditingIndex: -1,
|
||||
|
||||
// 相关数据
|
||||
allVersions: window.SSL_ALL_VERSIONS,
|
||||
allCipherSuites: window.SSL_ALL_CIPHER_SUITES.$copy(),
|
||||
modernCipherSuites: window.SSL_MODERN_CIPHER_SUITES,
|
||||
intermediateCipherSuites: window.SSL_INTERMEDIATE_CIPHER_SUITES,
|
||||
allClientAuthTypes: window.SSL_ALL_CLIENT_AUTH_TYPES,
|
||||
cipherSuitesVisible: false,
|
||||
|
||||
// 高级选项
|
||||
moreOptionsVisible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
hsts: {
|
||||
deep: true,
|
||||
handler: function () {
|
||||
this.policy.hsts = this.hsts
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 删除证书
|
||||
removeCert: function (index) {
|
||||
let that = this
|
||||
teaweb.confirm("确定删除此证书吗?证书数据仍然保留,只是当前服务不再使用此证书。", function () {
|
||||
that.policy.certRefs.$remove(index)
|
||||
that.policy.certs.$remove(index)
|
||||
})
|
||||
},
|
||||
|
||||
// 选择证书
|
||||
selectCert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/components/ssl/selectPopup", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传证书
|
||||
uploadCert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/components/ssl/uploadPopup", {
|
||||
height: "28em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("上传成功", function () {
|
||||
that.policy.certRefs.push(resp.data.certRef)
|
||||
that.policy.certs.push(resp.data.cert)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 更多选项
|
||||
changeOptionsVisible: function () {
|
||||
this.moreOptionsVisible = !this.moreOptionsVisible
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime: function (timestamp) {
|
||||
return new Date(timestamp * 1000).format("Y-m-d")
|
||||
},
|
||||
|
||||
// 格式化加密套件
|
||||
formatCipherSuite: function (cipherSuite) {
|
||||
return cipherSuite.replace(/(AES|3DES)/, "<var style=\"font-weight: bold\">$1</var>")
|
||||
},
|
||||
|
||||
// 添加单个套件
|
||||
addCipherSuite: function (cipherSuite) {
|
||||
if (!this.policy.cipherSuites.$contains(cipherSuite)) {
|
||||
this.policy.cipherSuites.push(cipherSuite)
|
||||
}
|
||||
this.allCipherSuites.$removeValue(cipherSuite)
|
||||
},
|
||||
|
||||
// 删除单个套件
|
||||
removeCipherSuite: function (cipherSuite) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除此套件吗?", function () {
|
||||
that.policy.cipherSuites.$removeValue(cipherSuite)
|
||||
that.allCipherSuites = window.SSL_ALL_CIPHER_SUITES.$findAll(function (k, v) {
|
||||
return !that.policy.cipherSuites.$contains(v)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 清除所选套件
|
||||
clearCipherSuites: function () {
|
||||
let that = this
|
||||
teaweb.confirm("确定要清除所有已选套件吗?", function () {
|
||||
that.policy.cipherSuites = []
|
||||
that.allCipherSuites = window.SSL_ALL_CIPHER_SUITES.$copy()
|
||||
})
|
||||
},
|
||||
|
||||
// 批量添加套件
|
||||
addBatchCipherSuites: function (suites) {
|
||||
var that = this
|
||||
teaweb.confirm("确定要批量添加套件?", function () {
|
||||
suites.$each(function (k, v) {
|
||||
if (that.policy.cipherSuites.$contains(v)) {
|
||||
return
|
||||
}
|
||||
that.policy.cipherSuites.push(v)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 套件拖动排序
|
||||
*/
|
||||
sortableCipherSuites: function () {
|
||||
var box = document.querySelector(".cipher-suites-box")
|
||||
Sortable.create(box, {
|
||||
draggable: ".label",
|
||||
handle: ".icon.handle",
|
||||
onStart: function () {
|
||||
|
||||
},
|
||||
onUpdate: function (event) {
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 显示所有套件
|
||||
showAllCipherSuites: function () {
|
||||
this.cipherSuitesVisible = !this.cipherSuitesVisible
|
||||
},
|
||||
|
||||
// 显示HSTS更多选项
|
||||
showMoreHSTS: function () {
|
||||
this.hstsOptionsVisible = !this.hstsOptionsVisible;
|
||||
if (this.hstsOptionsVisible) {
|
||||
this.changeHSTSMaxAge()
|
||||
}
|
||||
},
|
||||
|
||||
// 监控HSTS有效期修改
|
||||
changeHSTSMaxAge: function () {
|
||||
var v = this.hsts.maxAge
|
||||
if (isNaN(v)) {
|
||||
this.hsts.days = "-"
|
||||
return
|
||||
}
|
||||
this.hsts.days = parseInt(v / 86400)
|
||||
if (isNaN(this.hsts.days)) {
|
||||
this.hsts.days = "-"
|
||||
} else if (this.hsts.days < 0) {
|
||||
this.hsts.days = "-"
|
||||
}
|
||||
},
|
||||
|
||||
// 设置HSTS有效期
|
||||
setHSTSMaxAge: function (maxAge) {
|
||||
this.hsts.maxAge = maxAge
|
||||
this.changeHSTSMaxAge()
|
||||
},
|
||||
|
||||
// 添加HSTS域名
|
||||
addHstsDomain: function () {
|
||||
this.hstsDomainAdding = true
|
||||
this.hstsDomainEditingIndex = -1
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.addingHstsDomain.focus()
|
||||
}, 100)
|
||||
},
|
||||
|
||||
// 修改HSTS域名
|
||||
editHstsDomain: function (index) {
|
||||
this.hstsDomainEditingIndex = index
|
||||
this.addingHstsDomain = this.hsts.domains[index]
|
||||
this.hstsDomainAdding = true
|
||||
let that = this
|
||||
setTimeout(function () {
|
||||
that.$refs.addingHstsDomain.focus()
|
||||
}, 100)
|
||||
},
|
||||
|
||||
// 确认HSTS域名添加
|
||||
confirmAddHstsDomain: function () {
|
||||
this.addingHstsDomain = this.addingHstsDomain.trim()
|
||||
if (this.addingHstsDomain.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (this.hstsDomainEditingIndex > -1) {
|
||||
this.hsts.domains[this.hstsDomainEditingIndex] = this.addingHstsDomain
|
||||
} else {
|
||||
this.hsts.domains.push(this.addingHstsDomain)
|
||||
}
|
||||
this.cancelHstsDomainAdding()
|
||||
},
|
||||
|
||||
// 取消HSTS域名添加
|
||||
cancelHstsDomainAdding: function () {
|
||||
this.hstsDomainAdding = false
|
||||
this.addingHstsDomain = ""
|
||||
this.hstsDomainEditingIndex = -1
|
||||
},
|
||||
|
||||
// 删除HSTS域名
|
||||
removeHstsDomain: function (index) {
|
||||
this.cancelHstsDomainAdding()
|
||||
this.hsts.domains.$remove(index)
|
||||
},
|
||||
|
||||
// 选择客户端CA证书
|
||||
selectClientCACert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/components/ssl/selectPopup?isCA=1", {
|
||||
width: "50em",
|
||||
height: "30em",
|
||||
callback: function (resp) {
|
||||
that.policy.clientCARefs.push(resp.data.certRef)
|
||||
that.policy.clientCACerts.push(resp.data.cert)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传CA证书
|
||||
uploadClientCACert: function () {
|
||||
let that = this
|
||||
teaweb.popup("/servers/components/ssl/uploadPopup?isCA=1", {
|
||||
height: "28em",
|
||||
callback: function (resp) {
|
||||
teaweb.success("上传成功", function () {
|
||||
that.policy.clientCARefs.push(resp.data.certRef)
|
||||
that.policy.clientCACerts.push(resp.data.cert)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 删除客户端CA证书
|
||||
removeClientCACert: function (index) {
|
||||
let that = this
|
||||
teaweb.confirm("确定删除此证书吗?证书数据仍然保留,只是当前服务不再使用此证书。", function () {
|
||||
that.policy.clientCARefs.$remove(index)
|
||||
that.policy.clientCACerts.$remove(index)
|
||||
})
|
||||
}
|
||||
},
|
||||
template: `<div>
|
||||
<h4>SSL/TLS相关配置</h4>
|
||||
<input type="hidden" name="sslPolicyJSON" :value="JSON.stringify(policy)"/>
|
||||
<table class="ui table definition selectable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="title">用HTTP/2</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="1" v-model="policy.http2Enabled"/>
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>选择证书</td>
|
||||
<td>
|
||||
<div v-if="policy.certs != null && policy.certs.length > 0">
|
||||
<div class="ui label small" v-for="(cert, index) in policy.certs">
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeCert()"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="red">选择或上传证书后<span v-if="vProtocol == 'https'">HTTPS</span><span v-if="vProtocol == 'tls'">TLS</span>服务才能生效。</span>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<button class="ui button tiny" type="button" @click.prevent="selectCert()">选择已有证书</button>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadCert()">上传新证书</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TLS最低版本</td>
|
||||
<td>
|
||||
<select v-model="policy.minVersion" class="ui dropdown auto-width">
|
||||
<option v-for="version in allVersions" :value="version">{{version}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<more-options-tbody @change="changeOptionsVisible"></more-options-tbody>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<!-- 加密套件 -->
|
||||
<tr>
|
||||
<td>加密算法套件<em>(CipherSuites)</em></td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="1" v-model="policy.cipherSuitesIsOn" />
|
||||
<label>是否要自定义</label>
|
||||
</div>
|
||||
<div v-show="policy.cipherSuitesIsOn">
|
||||
<div class="ui divider"></div>
|
||||
<div class="cipher-suites-box">
|
||||
已添加套件({{policy.cipherSuites.length}}):
|
||||
<div v-for="cipherSuite in policy.cipherSuites" class="ui label tiny" style="margin-bottom: 0.5em">
|
||||
<input type="hidden" name="cipherSuites" :value="cipherSuite"/>
|
||||
<span v-html="formatCipherSuite(cipherSuite)"></span> <a href="" title="删除套件" @click.prevent="removeCipherSuite(cipherSuite)"><i class="icon remove"></i></a>
|
||||
<a href="" title="拖动改变顺序"><i class="icon bars handle"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui divider"></div>
|
||||
<span v-if="policy.cipherSuites.length > 0"><a href="" @click.prevent="clearCipherSuites()">[清除所有已选套件]</a> </span>
|
||||
<a href="" @click.prevent="addBatchCipherSuites(modernCipherSuites)">[添加推荐套件]</a>
|
||||
<a href="" @click.prevent="addBatchCipherSuites(intermediateCipherSuites)">[添加兼容套件]</a>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
|
||||
<div class="cipher-all-suites-box">
|
||||
<a href="" @click.prevent="showAllCipherSuites()"><span v-if="policy.cipherSuites.length == 0">所有</span>可选套件({{allCipherSuites.length}}) <i class="icon angle" :class="{down:!cipherSuitesVisible, up:cipherSuitesVisible}"></i></a>
|
||||
<a href="" v-if="cipherSuitesVisible" v-for="cipherSuite in allCipherSuites" class="ui label tiny" title="点击添加到自定义套件中" @click.prevent="addCipherSuite(cipherSuite)" v-html="formatCipherSuite(cipherSuite)" style="margin-bottom:0.5em"></a>
|
||||
</div>
|
||||
<p class="comment" v-if="cipherSuitesVisible">点击可选套件添加。</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- HSTS -->
|
||||
<tr v-show="vProtocol == 'https'">
|
||||
<td :class="{'color-border':hsts.isOn}">是否开启HSTS</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="hstsOn" v-model="hsts.isOn" value="1"/>
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">
|
||||
开启后,会自动在响应Header中加入
|
||||
<span class="ui label small">Strict-Transport-Security:
|
||||
<var v-if="!hsts.isOn">...</var>
|
||||
<var v-if="hsts.isOn"><span>max-age=</span>{{hsts.maxAge}}</var>
|
||||
<var v-if="hsts.isOn && hsts.includeSubDomains">; includeSubDomains</var>
|
||||
<var v-if="hsts.isOn && hsts.preload">; preload</var>
|
||||
</span>
|
||||
<span v-if="hsts.isOn">
|
||||
<a href="" @click.prevent="showMoreHSTS()">修改<i class="icon angle" :class="{down:!hstsOptionsVisible, up:hstsOptionsVisible}"></i> </a>
|
||||
</span>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="hsts.isOn && hstsOptionsVisible">
|
||||
<td class="color-border">HSTS包含子域名<em>(includeSubDomains)</em></td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="hstsIncludeSubDomains" value="1" v-model="hsts.includeSubDomains"/>
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="hsts.isOn && hstsOptionsVisible">
|
||||
<td class="color-border">HSTS预加载<em>(preload)</em></td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="hstsPreload" value="1" v-model="hsts.preload"/>
|
||||
<label></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="hsts.isOn && hstsOptionsVisible">
|
||||
<td class="color-border">HSTS生效的域名</td>
|
||||
<td colspan="2">
|
||||
<div class="names-box">
|
||||
<span class="ui label tiny" v-for="(domain, arrayIndex) in hsts.domains" :class="{blue:hstsDomainEditingIndex == arrayIndex}">{{domain}}
|
||||
<input type="hidden" name="hstsDomains" :value="domain"/>
|
||||
<a href="" @click.prevent="editHstsDomain(arrayIndex)" title="修改"><i class="icon pencil"></i></a>
|
||||
<a href="" @click.prevent="removeHstsDomain(arrayIndex)" title="删除"><i class="icon remove"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="ui fields inline" v-if="hstsDomainAdding" style="margin-top:0.8em">
|
||||
<div class="ui field">
|
||||
<input type="text" name="addingHstsDomain" ref="addingHstsDomain" style="width:16em" maxlength="100" placeholder="域名,比如example.com" @keyup.enter="confirmAddHstsDomain()" @keypress.enter.prevent="1" v-model="addingHstsDomain" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button tiny" type="button" @click="confirmAddHstsDomain()">确定</button>
|
||||
<a href="" @click.prevent="cancelHstsDomainAdding()">取消</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui field" style="margin-top: 1em">
|
||||
<button class="ui button tiny" type="button" @click="addHstsDomain()">+</button>
|
||||
</div>
|
||||
<p class="comment">如果没有设置域名的话,则默认支持所有的域名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 客户端认证 -->
|
||||
<tr>
|
||||
<td>客户端认证方式</td>
|
||||
<td>
|
||||
<select name="clientAuthType" v-model="policy.clientAuthType" class="ui dropdown auto-width">
|
||||
<option v-for="authType in allClientAuthTypes" :value="authType.type">{{authType.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>客户端认证CA证书</td>
|
||||
<td>
|
||||
<div v-if="policy.clientCACerts != null && policy.clientCACerts.length > 0">
|
||||
<div class="ui label small" v-for="(cert, index) in policy.clientCACerts">
|
||||
{{cert.name}} / {{cert.dnsNames}} / 有效至{{formatTime(cert.timeEndAt)}} <a href="" title="删除" @click.prevent="removeClientCACert()"><i class="icon remove"></i></a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
<button class="ui button tiny" type="button" @click.prevent="selectClientCACert()">选择已有证书</button>
|
||||
<button class="ui button tiny" type="button" @click.prevent="uploadClientCACert()">上传新证书</button>
|
||||
<p class="comment">用来校验客户端证书以增强安全性,通常不需要设置。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="ui margin"></div>
|
||||
</div>`
|
||||
})
|
||||
Reference in New Issue
Block a user