第 10 章:云安全
云安全是云原生应用的基石,只有将安全性融入设计与开发流程,才能确保数据与服务的可靠性。本章系统介绍云安全的核心原则、常见模式及实用技术,帮助开发者构建安全的云原生应用。
引言
安全实现起来往往复杂且容易被忽视,许多团队习惯将安全相关代码留到最后,导致上线前手忙脚乱。对于云原生应用,安全必须前置,贯穿设计、开发和运维全过程。
本章将围绕以下内容展开:
- Web 应用安全选项与实现
- 微服务安全保护方法
- 隐私与数据安全设计原则
- 实践练习:安全服务开发
保护 Web 应用程序
Web 应用安全是云安全的基础。合理选择认证与授权机制,避免常见安全误区,是保障云端应用安全的关键。
应用程序安全性选项
常见 Web 应用安全技术包括:
- HTTP 基本验证:用户名密码哈希传输,易受缓存和重放攻击。
- 表单(Cookies)认证:凭据由页面生成,易受 CSRF 等攻击。
- Windows 身份验证:适用于企业内网,不推荐云原生场景。
- SAML(安全断言标记语言):行业标准,支持身份联邦与多语言 API。
- OAuth/OpenID:开放授权标准,支持第三方登录,云原生推荐。
- 习惯性方案:不建议依赖底层操作系统或主机安全机制。
警告
不要依赖底层操作系统的密钥库或用户账户安全机制,系统安全需应用层实现。
本章以 OAuth 为例,介绍安全 Web 应用的实现。
设置 Auth0 账户
OAuth 认证需依赖身份信息提供商(如 Auth0、Google、UAA 等),通过 JWT(JSON Web Token)实现安全认证。推荐使用 Auth0,支持私有数据库和第三方登录,注册流程简单,无需信用卡。
注册后可在仪表板管理用户和认证系统:

构建 OAuth 安全 Web 应用
以 Auth0 为例,构建安全 Web 应用的核心流程如下:
- 配置 Auth0 账户,获取 ClientID、Secret、Domain、CallbackURL。
- 在服务端初始化认证配置与会话管理。
- 配置路由与中间件,保护敏感资源。
- 实现认证回调与用户信息处理。
服务端核心代码示例:
// filepath: server/server.go
package server
import (
"net/http"
"github.com/astaxie/beego/session"
"github.com/cloudfoundry-community/go-cfenv"
"github.com/cloudnativego/cf-tools"
"github.com/codegangsta/negroni"
"github.com/gorilla/mux"
)
type authConfig struct {
ClientID string
ClientSecret string
Domain string
CallbackURL string
}
func NewServer(appEnv *cfenv.App) *negroni.Negroni {
authClientID,_ := cftools.GetVCAPServiceProperty("authzero", "id", appEnv)
authSecret,_:= cftools.GetVCAPServiceProperty("authzero", "secret", appEnv)
authDomain,_:= cftools.GetVCAPServiceProperty("authzero", "domain", appEnv)
authCallback,_:= cftools.GetVCAPServiceProperty("authzero", "callback",appEnv)
config := &authConfig{
ClientID: authClientID,
ClientSecret: authSecret,
Domain: authDomain,
CallbackURL: authCallback,
}
sessionManager, _ := session.NewManager("memory",
'{"cookieName":"gosessionid","gclifetime":3600}')
go sessionManager.GC()
n := negroni.Classic()
mx := mux.NewRouter()
initRoutes(mx, sessionManager, config)
n.UseHandler(mx)
return n
}
func initRoutes(mx *mux.Router, sessionManager *session.Manager,
config *authConfig) {
mx.HandleFunc("/", homeHandler(config))
mx.HandleFunc("/callback", callbackHandler(sessionManager, config))
mx.Handle("/user", negroni.New(
negroni.HandlerFunc(isAuthenticated(sessionManager)),
negroni.Wrap(http.HandlerFunc(userHandler(sessionManager))),
))
mx.PathPrefix("/public/").Handler(http.StripPrefix("/public/",
http.FileServer(http.Dir("public/"))))
}
中间件实现:
// filepath: server/middleware.go
func isAuthenticated(sessionManager *session.Manager) negroni.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
session, _ := sessionManager.SessionStart(w, r)
defer session.SessionRelease(w)
if session.Get("profile") == nil {
http.Redirect(w, r, "/", http.StatusMovedPermanently)
} else {
next(w, r)
}
}
}
主页处理器示例:
// filepath: server/home_handler.go
var bodyTemplate = `
<script src="https://cdn.auth0.com/js/lock-8.2.min.js"></script>
<script type="text/javascript">
var lock = new Auth0Lock('{{.ClientID}}', '{{.Domain}}');
function signin() {
lock.show({
callbackURL: '{{.CallbackURL}}',
responseType: 'code',
authParams: { scope: 'openid profile' }
});
}
</script>
<button onclick="window.signin();">Login</button>
`
func homeHandler(config *authConfig) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
t := template.Must(template.New("htmlz").Parse(bodyTemplate))
t.Execute(w, config)
}
}
用户信息处理:
// filepath: server/user_handler.go
func userHandler(sessionManager *session.Manager) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, _ := sessionManager.SessionStart(w, r)
defer session.SessionRelease(w)
// Getting the profile from the session
profile := session.Get("profile")
fmt.Fprintf(w, "USER DATA: %+v", profile)
}
}
会话状态警告
示例采用内存会话,生产环境建议使用 Redis 或分布式缓存。
运行方式:
./runlocal local_config/env
配置环境变量:
X_AUTHZERO_ID=(your auth0 ID)
X_AUTHZERO_SECRET=(your auth0 secret)
X_AUTHZERO_DOMAIN=(your auth0 domain, e.g. foo.auth0.com)
X_AUTHZERO_CALLBACK=http://192.168.99.100/callback
登录后可在 /user
页面查看用户 profile 信息:

保护微服务
微服务安全需采用无 UI 的认证方式,推荐客户端凭据模式(API key/secret),避免重定向和复杂认证流程。
客户端凭据模式概述
客户端凭据模式适用于服务间认证,常见于 API key/secret 方案。需区分:
- 请求发起者身份
- 服务是否授权
正确使用 HTTP 状态码:
- 401 Unauthorized:未提供凭据
- 403 Forbidden:凭据正确但权限不足
使用客户端凭据保护微服务
通过中间件保护 API 路由,示例代码:
// filepath: server/server.go
func NewServer() *negroni.Negroni {
formatter := render.New(render.Options{IndentJSON: true})
n := negroni.Classic()
router := mux.NewRouter()
router.HandleFunc("/", homeHandler)
apiRouter := mux.NewRouter()
apiRouter.HandleFunc("/api/get", apiGetHandler(formatter)).Methods("GET")
apiRouter.HandleFunc("/api/post", apiPostHandler(formatter)).Methods("POST")
router.PathPrefix("/api").Handler(negroni.New(
negroni.HandlerFunc(isAuthorized(formatter)),
negroni.Wrap(apiRouter),
))
n.UseHandler(router)
return n
}
认证中间件:
// filepath: server/middleware.go
func isAuthorized(formatter *render.Render) negroni.HandlerFunc {
apikey := os.Getenv(APIKey)
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
providedKey := r.Header.Get(APIKey)
if providedKey == "" {
formatter.JSON(w, http.StatusUnauthorized, struct{ Error string }{"Unauthorized."})
} else if providedKey != apikey {
formatter.JSON(w, http.StatusForbidden, struct{ Error string }{"Insufficient credentials."})
} else {
next(w, r)
}
}
}
关于 SSL 的注意事项
SSL 终端建议由基础设施层(如反向代理、云平台)统一管理,应用层无需直接处理 SSL 证书,避免代码耦合和安全隐患。
隐私和数据安全
数据安全是云安全的重要组成部分。设计安全系统时应遵循以下原则:
- 不在用户数据库存储敏感信息,推荐使用第三方令牌。
- 敏感数据存储与传输均需加密。
- 不与加密数据同存解密密钥。
- 日志中避免输出个人身份信息(PII)。
- 机密信息处理后立即销毁,避免内存泄漏。
- 实践 3R 法则:轮换(Rotate)、翻修(Repave)、修复(Repair)。
读者练习
建议读者实践以下安全服务开发任务:
- 在安全服务中添加资源,如
/customer/{email}/orders
,响应中包含 email 字段。 - 在安全 Web 应用中新增页面,展示安全服务返回的订单列表。
- 从会话状态提取用户 email,构建 REST 请求。
- 使用用户提供服务配置,避免硬编码后台服务 URL。
通过练习,深入理解安全 Web 应用与安全服务的集成与保护方法。
总结
安全性应贯穿云原生应用的设计与开发全过程,绝不能事后补救。通过合理选择认证机制、保护微服务、加强数据安全和隐私保护,开发者可有效提升云端应用的安全性。建议结合实际项目,深入理解安全设计原则,为后续复杂系统开发打下坚实基础。
参考文献
- Auth0 官方文档 - auth0.com
- OAuth 2.0 规范 - oauth.net
- JSON Web Token (JWT) - jwt.io
- Cloud Foundry CLI 文档 - docs.run.pivotal.io
- Building Microservices - oreilly.com
- Go 官方网站 - golang.org