first commits
This commit is contained in:
809
public/2018/05/23/external_system_auth/index.html
Normal file
809
public/2018/05/23/external_system_auth/index.html
Normal file
@@ -0,0 +1,809 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head><script src="/livereload.js?mindelay=10&v=2&port=1313&path=livereload" data-no-instant defer></script>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
|
||||
const autoTheme = false;
|
||||
if (autoTheme) {
|
||||
document.documentElement.setAttribute('data-auto-theme', 'true');
|
||||
}
|
||||
|
||||
const theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<meta property="og:site_name" content="David Young">
|
||||
<meta property="og:type" content="article">
|
||||
|
||||
|
||||
<meta property="og:image" content="http://localhost:1313//img/2018-05-23-external_system_auth/background.jpg">
|
||||
<meta property="twitter:image" content="http://localhost:1313//img/2018-05-23-external_system_auth/background.jpg" />
|
||||
|
||||
|
||||
|
||||
<meta name="title" content="微服务安全沉思录之三" />
|
||||
<meta property="og:title" content="微服务安全沉思录之三" />
|
||||
<meta property="twitter:title" content="微服务安全沉思录之三" />
|
||||
|
||||
|
||||
|
||||
<meta name="description" content="一些外部的第三方系统可能需要访问系统内部的微服务。例如在网上商店的例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。">
|
||||
<meta property="og:description" content="一些外部的第三方系统可能需要访问系统内部的微服务。例如在网上商店的例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。" />
|
||||
<meta property="twitter:description" content="一些外部的第三方系统可能需要访问系统内部的微服务。例如在网上商店的例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。" />
|
||||
|
||||
|
||||
<meta property="og:url" content="http://localhost:1313/2018/05/23/external_system_auth/" />
|
||||
|
||||
|
||||
<meta property="twitter:card" content="summary" />
|
||||
|
||||
|
||||
|
||||
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
|
||||
<link rel="shortcut icon" href="/img/favicon.ico">
|
||||
|
||||
<title>微服务安全沉思录之三 | David Young Blog</title>
|
||||
|
||||
<link rel="canonical" href="/2018/05/23/external_system_auth/">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/theme-variables.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/zanshang.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/hux-blog.min.js"></script>
|
||||
|
||||
|
||||
<script src="/js/lazysizes.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="navbar-header page-scroll">
|
||||
<button type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">David Young</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="huxblog_navbar">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="/">All Posts</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="/categories/life/">life</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tech/">tech</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/categories/tips/">tips</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li><a href="/archive//">ARCHIVE</a></li>
|
||||
|
||||
<li><a href="/notes//">NOTES</a></li>
|
||||
|
||||
<li><a href="/about//">ABOUT</a></li>
|
||||
|
||||
<li>
|
||||
<a href="/search"><i class="fa fa-search"></i></a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
|
||||
<i class="fa fa-moon"></i>
|
||||
<i class="fa fa-sun" style="display: none;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<script>
|
||||
(function() {
|
||||
|
||||
var theme = localStorage.getItem('cleanwhite-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
var toggleBtn = document.getElementById('theme-toggle');
|
||||
if (toggleBtn) {
|
||||
var moonIcon = toggleBtn.querySelector('.fa-moon');
|
||||
var sunIcon = toggleBtn.querySelector('.fa-sun');
|
||||
|
||||
if (theme === 'dark') {
|
||||
if (moonIcon) moonIcon.style.display = 'none';
|
||||
if (sunIcon) sunIcon.style.display = 'inline';
|
||||
toggleBtn.setAttribute('title', 'Switch to light mode');
|
||||
} else {
|
||||
if (moonIcon) moonIcon.style.display = 'inline';
|
||||
if (sunIcon) sunIcon.style.display = 'none';
|
||||
toggleBtn.setAttribute('title', 'Switch to dark mode');
|
||||
}
|
||||
|
||||
|
||||
requestAnimationFrame(function() {
|
||||
toggleBtn.style.transition = 'opacity 0.2s ease';
|
||||
toggleBtn.style.opacity = '1';
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
var $body = document.body;
|
||||
var $toggle = document.querySelector('.navbar-toggle');
|
||||
var $navbar = document.querySelector('#huxblog_navbar');
|
||||
var $collapse = document.querySelector('.navbar-collapse');
|
||||
|
||||
$toggle.addEventListener('click', handleMagic)
|
||||
function handleMagic(e){
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}else{
|
||||
|
||||
$collapse.style.height = "auto"
|
||||
$navbar.className += " in";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var navLinks = document.querySelectorAll('.navbar-collapse a');
|
||||
navLinks.forEach(function(link) {
|
||||
link.addEventListener('click', function() {
|
||||
if ($navbar.className.indexOf('in') > 0) {
|
||||
|
||||
$navbar.className = " ";
|
||||
setTimeout(function(){
|
||||
if($navbar.className.indexOf('in') < 0) {
|
||||
$collapse.style.height = "0px"
|
||||
}
|
||||
},400)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
header.intro-header {
|
||||
background-image: url('/img/2018-05-23-external_system_auth/background.jpg')
|
||||
}
|
||||
</style>
|
||||
|
||||
<header class="intro-header" >
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<div class="post-heading">
|
||||
<div class="tags">
|
||||
|
||||
<a class="tag" href="/tags/microservice" title="Microservice">
|
||||
Microservice
|
||||
</a>
|
||||
|
||||
<a class="tag" href="/tags/security" title="Security">
|
||||
Security
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<h1>微服务安全沉思录之三</h1>
|
||||
<h2 class="subheading">外部系统访问控制</h2>
|
||||
<span class="meta">
|
||||
|
||||
Posted by
|
||||
|
||||
"赵化冰"
|
||||
|
||||
on
|
||||
Wednesday, May 23, 2018
|
||||
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
|
||||
<article>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
post-container">
|
||||
|
||||
|
||||
<h2 id="外部系统访问控制">外部系统访问控制</h2>
|
||||
<p>除用户访问和微服务之间的相互访问外,外部的第三方系统也可能需要访问系统内部的微服务。例如在上一篇博客的网上商店例子中,外部的推荐服务可能需要接入系统,以获取商店的商品目录信息。相对于内部服务之间的访问而言,外部系统的访问需要进行严格的安全控制。</p>
|
||||
<h3 id="使用账号进行控制">使用账号进行控制</h3>
|
||||
<p>可以为外部系统创建一个用户账号,类似普通用户一样对外部系统的账号进行管理,并使用该账号对外部系统进行认证和权限控制。</p>
|
||||
<p>采用这种方式的问题是难以处理用户相关的敏感数据。因为外部系统自身也是微服务系统中的一个用户账号,因此该外部系统只能访问该账号自身的数据和一些不敏感的公共数据,而不能访问和用户相关的数据。例如在网上商店的例子中,外部系统可以采用该方式访问商品目录信息,但不应允许访问用户历史购买记录,用户余额等信息。</p>
|
||||
<h3 id="api-token">API Token</h3>
|
||||
<p>是一个API Token(又称API Key)可以控制对用户敏感数据的访问。微服务应用提供一个API Token的生成界面,用户登录后可以生成自己的API Token,并在第三方应用使用该API Token访问微服务的API。在这种情况下,一般只允许第三方应用访问该Token所属用户自身的数据,而不能访问其他用户的敏感私有数据。</p>
|
||||
<p>例如Github就提供了Personal API Token功能,用户可以在<a href="https://github.com/settings/tokens">Github的开发者设置界面</a>中创建Token,然后使用该Token来访问Github的API。在创建Token时,可以设置该Token可以访问用户的哪些数据,如查看Repo信息,删除Repo,查看用户信息,更新用户信息等。</p>
|
||||
<p>使用API Token来访问Github API</p>
|
||||
<pre tabindex="0"><code>curl -u zhaohuabing:fbdf8e8862252ed0f3ba9dba4e328c01ac93aeec https://api.github.com/user
|
||||
</code></pre><blockquote>
|
||||
<p>不用试了,这不是我的真实API Token, just for demonstration :-)</p>
|
||||
</blockquote>
|
||||
<p>使用API Token而不是直接使用用户名/密码来访问API的好处是降低了用户密码暴露的风险,并且可以随时收回Token的权限而不用修改密码。</p>
|
||||
<p>由于API Token只能访问指定用户的数据,因此适合于用户自己开发一些脚本或小程序对应用中自己的数据进行操作。</p>
|
||||
<h3 id="oauth">OAuth</h3>
|
||||
<p>某些第三方应用需要访问不同用户的数据,或者对多个用户的数据进行整合处理,则可以考虑采用OAuth。采用OAuth,当第三方应用访问服务时,应用会提示用户授权第三方应用相应的访问权限,根据用户的授权操作结果生成用于访问的Token,以对第三方应用的操作请求进行访问控制。</p>
|
||||
<p>同样以Github为例,一些第三方应用如Travis CI,GitBook等就是通过OAuth和Github进行集成的。
|
||||
OAuth针对不同场景有不同的认证流程,一个典型的认证流程如下图所示:</p>
|
||||
<ul>
|
||||
<li>用户向OAuth客户端程序发起一个请求,OAuth客户端程序在处理该请求时发现需要访问用户在资源服务器中的数据。</li>
|
||||
<li>客户端程序将用户请求重定向到认证服务器,该请求中包含一个callback的URL。</li>
|
||||
<li>认证服务器返回授权页面,要求用户对OAuth客户端的资源请求进行授权。</li>
|
||||
<li>用户对该操作进行授权后,认证服务器将请求重定向到客户端程序的callback url,将授权码返回给客户端程序。</li>
|
||||
<li>客户端程序将授权码发送给认证服务器,请求token。</li>
|
||||
<li>认证服务器验证授权码后将token颁发给客户端程序。</li>
|
||||
<li>客户端程序采用颁发的token访问资源,完成用户请求。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>备注:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>OAuth中按照功能区分了资源服务器和认证服务器这两个角色,在实现时这两个角色常常是同一个应用。将该流程图中的各个角色对应到Github的例子中,资源服务器和认证服务器都是Github,客户端程序是Travis CI或者GitBook,用户则是使用Travis CI或者GitBook的直接用户。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>有人可能会疑惑在该流程中为何要使用一个授权码(Authorization Code)来申请Token,而不是由认证服务器直接返回Token给客户端。OAuth这样设计的原因是在重定向到客户端Callback URL的过程中会经过用户代理(浏览器),如果直接传递Token存在被窃取的风险。采用授权码的方式,申请Token时客户端直接和认证服务器进行交互,并且认证服务期在处理客户端的Token申请请求时还会对客户端进行身份认证,避免其他人伪造客户端身份来使用认证码申请Token。
|
||||
下面是一个客户端程序采用Authorization Code来申请Token的示例,client_id和client_secret被用来验证客户端的身份。</p>
|
||||
</li>
|
||||
</ol>
|
||||
<pre tabindex="0"><code>POST /oauth/token HTTP/1.1
|
||||
Host: authorization-server.com
|
||||
|
||||
grant_type=authorization_code
|
||||
&code=xxxxxxxxxxx
|
||||
&redirect_uri=https://example-app.com/redirect
|
||||
&client_id=xxxxxxxxxx
|
||||
&client_secret=xxxxxxxxxx
|
||||
</code></pre></blockquote>
|
||||
<p>
|
||||
<img src="/img/2018-05-23-external_system_auth/oauth_web_server_flow.png" alt="OAuth认证流程">
|
||||
|
||||
</p>
|
||||
<center>OAuth认证流程</center>
|
||||
<p>另外在谈及OAuth时,我们需要注意微服务应用作为OAuth客户端和OAuth服务器的两种不同场景:</p>
|
||||
<p>在实现微服务自身的用户认证时,也可以采用OAuth将微服务的用户认证委托给一个第三方的认证服务提供商,例如很多应用都将用户登录和微信或者QQ的OAuth服务进行了集成。</p>
|
||||
<p>第三方应用接入和微服务自身用户认证采用OAuth的目的是不同的,前者是为了将微服务中用户的私有数据访问权限授权给第三方应用,微服务在OAuth架构中是认证和资源服务器的角色;而后者的目的是集成并利用知名认证提供服务商提供的OAuth认证服务,简化繁琐的注册操作,微服务在OAuth架构中是客户端的角色。</p>
|
||||
<p>因此在我们需要区分这两种不同的场景,以免造成误解。</p>
|
||||
<h2 id="后记">后记</h2>
|
||||
<p>前两篇文章在在公众号发布后,有朋友提到还要注意登录密码明文问题、防止重放攻击、防止时间差攻击、防止脱裤后的彩虹表攻击…。的确,安全是一个庞大的话题,本系列文章只阐述了我关于微服务架构对应用安全带来的影响的一点小小思考。在产品开发和运维中,还需要对安全进行全方面的考虑,最好遵循一些业界的最佳实践,如采用完善的防火墙对外部流量进行隔离,采用加盐hash对用户密码进行存储,采用tls进行加密传输,对用户输入进行严格检查防止sql注入,采用经过验证的通用加密算法等等。</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="entry-shang text-center">
|
||||
|
||||
<p>「真诚赞赏,手留余香」</p>
|
||||
|
||||
<button class="zs show-zs btn btn-bred">赞赏支持</button>
|
||||
</div>
|
||||
<div class="zs-modal-bg"></div>
|
||||
<div class="zs-modal-box">
|
||||
<div class="zs-modal-head">
|
||||
<button type="button" class="close">×</button>
|
||||
<span class="author"><a href="http://localhost:1313/"><img src="/img/favicon.png" />David Young</a></span>
|
||||
|
||||
<p class="tip"><i></i><span>真诚赞赏,手留余香</span></p>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="zs-modal-body">
|
||||
<div class="zs-modal-btns">
|
||||
<button class="btn btn-blink" data-num="2">2元</button>
|
||||
<button class="btn btn-blink" data-num="5">5元</button>
|
||||
<button class="btn btn-blink" data-num="10">10元</button>
|
||||
<button class="btn btn-blink" data-num="50">50元</button>
|
||||
<button class="btn btn-blink" data-num="100">100元</button>
|
||||
<button class="btn btn-blink" data-num="1">任意金额</button>
|
||||
</div>
|
||||
<div class="zs-modal-pay">
|
||||
<button class="btn btn-bred" id="pay-text">2元</button>
|
||||
<p>使用<span id="pay-type">微信</span>扫描二维码完成支付</p>
|
||||
<img src="/img/reward/wechat-2.png" id="pay-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zs-modal-footer">
|
||||
<label><input type="radio" name="zs-type" value="wechat" class="zs-type" checked="checked"><span ><span class="zs-wechat"><img src="/img/reward/wechat-btn.png"/></span></label>
|
||||
<label><input type="radio" name="zs-type" value="alipay" class="zs-type" class="zs-alipay"><img src="/img/reward/alipay-btn.png"/></span></label>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/js/reward.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<ul class="pager">
|
||||
|
||||
<li class="previous">
|
||||
<a href="/2018/05/23/service_2_service_auth/" data-toggle="tooltip" data-placement="top" title="微服务安全沉思录之二">←
|
||||
Previous Post</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="next">
|
||||
<a href="/2018/05/24/set_up_my_ubuntu_desktop/" data-toggle="tooltip" data-placement="top" title="Everything about Setting Up My Ubuntu Desktop">Next
|
||||
Post →</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
|
||||
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
|
||||
|
||||
|
||||
<div id="Comments"></div>
|
||||
|
||||
<script>
|
||||
Artalk.init({
|
||||
el: '#Comments',
|
||||
pageKey: 'http:\/\/localhost:1313\/2018\/05\/23\/external_system_auth\/',
|
||||
pageTitle: '微服务安全沉思录之三',
|
||||
server: 'https:\/\/xxx.xxx.com',
|
||||
site: 'xxx blog',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-2 col-lg-offset-0
|
||||
visible-lg-block
|
||||
sidebar-container
|
||||
catalog-container">
|
||||
<div class="side-catalog">
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5>
|
||||
<a class="catalog-toggle" href="#">CATALOG</a>
|
||||
</h5>
|
||||
<ul class="catalog-body"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="
|
||||
col-lg-8 col-lg-offset-2
|
||||
col-md-10 col-md-offset-1
|
||||
sidebar-container">
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr class="hidden-sm hidden-xs">
|
||||
<h5><a href="/tags/">FEATURED TAGS</a></h5>
|
||||
<div class="tags">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/docker" title="docker">
|
||||
docker
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/istio" title="istio">
|
||||
istio
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/kubernetes" title="kubernetes">
|
||||
kubernetes
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/microservice" title="microservice">
|
||||
microservice
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/tags/security" title="security">
|
||||
security
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/service-mesh" title="service mesh">
|
||||
service mesh
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="/tags/tips" title="tips">
|
||||
tips
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<hr>
|
||||
<h5>FRIENDS</h5>
|
||||
<ul class="list-inline">
|
||||
|
||||
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
|
||||
<ul class="list-inline text-center">
|
||||
|
||||
<li>
|
||||
<a href="mailto:youremail@gmail.com">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/yourgithub">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li>
|
||||
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fas fa-circle fa-stack-2x"></i>
|
||||
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p class="copyright text-muted">
|
||||
Copyright © David Young 2026
|
||||
|
||||
<br>
|
||||
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
|
||||
<iframe
|
||||
style="margin-left: 2px; margin-bottom:-5px;"
|
||||
frameborder="0" scrolling="0" width="100px" height="20px"
|
||||
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
|
||||
</iframe>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
function loadAsync(u, c) {
|
||||
var d = document, t = 'script',
|
||||
o = d.createElement(t),
|
||||
s = d.getElementsByTagName(t)[0];
|
||||
o.src = u;
|
||||
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
|
||||
s.parentNode.insertBefore(o, s);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
if($('#tag_cloud').length !== 0){
|
||||
loadAsync("/js/jquery.tagcloud.js",function(){
|
||||
$.fn.tagcloud.defaults = {
|
||||
|
||||
color: {start: '#bbbbee', end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function updateTagcloudColors() {
|
||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
const startColor = isDark ? '#808080' : '#bbbbee';
|
||||
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
|
||||
$.fn.tagcloud.defaults = {
|
||||
color: {start: startColor, end: '#0085a1'},
|
||||
};
|
||||
$('#tag_cloud a').tagcloud();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
updateTagcloudColors();
|
||||
});
|
||||
|
||||
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.attributeName === 'data-theme') {
|
||||
updateTagcloudColors();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme']
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
|
||||
var $nav = document.querySelector("nav");
|
||||
if($nav) FastClick.attach($nav);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<script src="/js/theme-toggle.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
function generateCatalog(selector) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_containerSelector = 'div.post-container'
|
||||
|
||||
|
||||
|
||||
var P = $(_containerSelector), a, n, t, l, i, c;
|
||||
a = P.find('h1,h2,h3,h4,h5,h6');
|
||||
|
||||
|
||||
$(selector).html('')
|
||||
|
||||
|
||||
a.each(function () {
|
||||
n = $(this).prop('tagName').toLowerCase();
|
||||
i = "#" + $(this).prop('id');
|
||||
t = $(this).text();
|
||||
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
|
||||
l = $('<li class="' + n + '_nav"></li>').append(c);
|
||||
$(selector).append(l);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
generateCatalog(".catalog-body");
|
||||
|
||||
|
||||
$(".catalog-toggle").click((function (e) {
|
||||
e.preventDefault();
|
||||
$('.side-catalog').toggleClass("fold")
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
loadAsync("\/js\/jquery.nav.js", function () {
|
||||
$('.catalog-body').onePageNav({
|
||||
currentClass: "active",
|
||||
changeHash: !1,
|
||||
easing: "swing",
|
||||
filter: "",
|
||||
scrollSpeed: 700,
|
||||
scrollOffset: 0,
|
||||
scrollThreshold: .2,
|
||||
begin: null,
|
||||
end: null,
|
||||
scrollChange: null,
|
||||
padding: 80
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user