mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 21:50:28 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			509 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			509 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
Vue.component("ssl-config-box", {
 | 
						||
	props: ["v-ssl-policy", "v-protocol", "v-server-id"],
 | 
						||
	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
 | 
						||
			let selectedCertIds = []
 | 
						||
			if (this.policy != null && this.policy.certs.length > 0) {
 | 
						||
				this.policy.certs.forEach(function (cert) {
 | 
						||
					selectedCertIds.push(cert.id.toString())
 | 
						||
				})
 | 
						||
			}
 | 
						||
			teaweb.popup("/servers/certs/selectPopup?selectedCertIds=" + selectedCertIds, {
 | 
						||
				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/certs/uploadPopup", {
 | 
						||
				height: "28em",
 | 
						||
				callback: function (resp) {
 | 
						||
					teaweb.success("上传成功", function () {
 | 
						||
						that.policy.certRefs.push(resp.data.certRef)
 | 
						||
						that.policy.certs.push(resp.data.cert)
 | 
						||
					})
 | 
						||
				}
 | 
						||
			})
 | 
						||
		},
 | 
						||
 | 
						||
		// 申请证书
 | 
						||
		requestCert: function () {
 | 
						||
			// 已经在证书中的域名
 | 
						||
			let excludeServerNames = []
 | 
						||
			if (this.policy != null && this.policy.certs.length > 0) {
 | 
						||
				this.policy.certs.forEach(function (cert) {
 | 
						||
					excludeServerNames.$pushAll(cert.dnsNames)
 | 
						||
				})
 | 
						||
			}
 | 
						||
 | 
						||
			let that = this
 | 
						||
			teaweb.popup("/servers/server/settings/https/requestCertPopup?serverId=" + this.vServerId + "&excludeServerNames=" + excludeServerNames.join(","), {
 | 
						||
				callback: 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/certs/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/certs/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 v-show="vProtocol == 'https'">
 | 
						||
				<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 class="title">选择证书</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>  
 | 
						||
					<button class="ui button tiny" type="button" @click.prevent="requestCert()" v-if="vServerId != null && vServerId > 0">申请免费证书</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 basic" 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 basic" 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>`
 | 
						||
}) |