u
@@ -0,0 +1,694 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="icon" href="/static/favicon.png">
|
||||
|
||||
<title>00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md</title>
|
||||
|
||||
<!-- Spectre.css framework -->
|
||||
|
||||
<link rel="stylesheet" href="/static/index.css">
|
||||
|
||||
<!-- theme css & js -->
|
||||
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div class="book-container">
|
||||
|
||||
<div class="book-sidebar">
|
||||
|
||||
<div class="book-brand">
|
||||
|
||||
<a href="/">
|
||||
|
||||
<img src="/static/favicon.png">
|
||||
|
||||
<span>技术文章摘抄</span>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-menu uncollapsible">
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="../">上一级</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/Spring Security 详解与实操/00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md">00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">01 顶级框架:Spring Security 是一款什么样的安全性框架?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/02 用户认证:如何使用 Spring Security 构建用户认证体系?.md">02 用户认证:如何使用 Spring Security 构建用户认证体系?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/03 认证体系:如何深入理解 Spring Security 用户认证机制?.md">03 认证体系:如何深入理解 Spring Security 用户认证机制?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/04 密码安全:Spring Security 中包含哪些加解密技术?.md">04 密码安全:Spring Security 中包含哪些加解密技术?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/05 访问授权:如何对请求的安全访问过程进行有效配置?.md">05 访问授权:如何对请求的安全访问过程进行有效配置?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/06 权限管理:如何剖析 Spring Security 的授权原理?.md">06 权限管理:如何剖析 Spring Security 的授权原理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md">07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md">08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md">09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/10 全局方法:如何确保方法级别的安全访问?.md">10 全局方法:如何确保方法级别的安全访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md">11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/12 开放协议:OAuth2 协议解决的是什么问题?.md">12 开放协议:OAuth2 协议解决的是什么问题?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md">13 授权体系:如何构建 OAuth2 授权服务器?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/14 资源保护:如何基于 OAuth2 协议配置授权过程?.md">14 资源保护:如何基于 OAuth2 协议配置授权过程?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/15 令牌扩展:如何使用 JWT 实现定制化 Token?.md">15 令牌扩展:如何使用 JWT 实现定制化 Token?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md">16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md">17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md">18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/19 测试驱动:如何基于 Spring Security 测试系统安全性?.md">19 测试驱动:如何基于 Spring Security 测试系统安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/20 结束语 以终为始,Spring Security 的学习总结.md">20 结束语 以终为始,Spring Security 的学习总结.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function add_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function remove_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sidebar_toggle() {
|
||||
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let content = document.querySelector('.off-canvas-content')
|
||||
|
||||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||||
|
||||
sidebar_toggle.classList.remove('extend')
|
||||
|
||||
sidebar.classList.remove('hide')
|
||||
|
||||
content.classList.remove('extend')
|
||||
|
||||
} else { // hide
|
||||
|
||||
sidebar_toggle.classList.add('extend')
|
||||
|
||||
sidebar.classList.add('hide')
|
||||
|
||||
content.classList.add('extend')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.add('show')
|
||||
|
||||
overlay.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
function hide_canvas() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.remove('show')
|
||||
|
||||
overlay.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="off-canvas-content">
|
||||
|
||||
<div class="columns">
|
||||
|
||||
<div class="column col-12 col-lg-12">
|
||||
|
||||
<div class="book-navbar">
|
||||
|
||||
<!-- For Responsive Layout -->
|
||||
|
||||
<header class="navbar">
|
||||
|
||||
<section class="navbar-section">
|
||||
|
||||
<a onclick="open_sidebar()">
|
||||
|
||||
<i class="icon icon-menu"></i>
|
||||
|
||||
</a>
|
||||
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
overflow-y: hidden;">
|
||||
|
||||
<div class="book-post">
|
||||
|
||||
<p id="tip" align="center"></p>
|
||||
|
||||
<div><h1>00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航</h1>
|
||||
|
||||
<p>你好,我是鉴湘,拉勾教育专栏《Spring Cloud 原理与实战》《Spring Boot 实战开发》《Spring 响应式编程实战》《ShardingSphere 核心原理精讲》的作者。</p>
|
||||
|
||||
<p>我拥有 10 年以上大型 Java EE 和分布式系统构建及优化经验,曾带领 100+ 人团队完成了基于 Spring 家族技术体系的亿级用户应用系统建设,对基于 Spring 框架进行系统开发和维护有着丰富的实践经验。</p>
|
||||
|
||||
<p>在从业生涯中,我带过不少项目,无论是传统的电商类系统,或者是互联网相关的智能终端平台,都需要考虑安全性问题。<strong>任何一个安全漏洞,都可能给一个产品乃至整个公司带来无法挽回的损失</strong>。</p>
|
||||
|
||||
<p>因此,作为系统的设计者和实现者,如何时刻确保系统具备高度的安全性,成了一个非常重要的问题。</p>
|
||||
|
||||
<p>说到安全性,你可能会想到加解密算法、HTTPS 协议等常见的技术体系,<strong>但系统安全是一个综合性的主题,并非简单采用一些技术体系就能构建有效的解决方案</strong>。</p>
|
||||
|
||||
<p>我们以一个分布式环境下的应用场景为例。假设你要开发一个工单系统,而生成工单所依赖的用户订单信息维护在第三方订单系统中。为了生成工单,就必须让工单系统读取订单系统中的用户订单信息。</p>
|
||||
|
||||
<p>那么问题来了,工单系统如何获得用户的授权呢?一般我们想到的方法是用户将自己在订单管理平台上用户名和密码告诉工单系统,然后工单系统通过用户名和密码登录到订单管理平台并读取用户的订单记录,整个过程如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC3WquAXuQVAACRr8Z_UXc476.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>订单系统用户认证和授权交互示意图</p>
|
||||
|
||||
<p>上述方案看起来没有什么问题,但如果你仔细分析一下,就会发现这个流程在安全性上存在一些漏洞。</p>
|
||||
|
||||
<p>比如,一旦用户修改了订单管理平台的密码,工单系统就无法正常访问了。为此,我们<strong>需要引入诸如 OAuth2 协议完成分布式环境下的认证和授权</strong>。</p>
|
||||
|
||||
<p>我们通过一张图简单对比一下 OAuth2 协议与传统实现方案:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC3WrKADX7QAACv6cIqFgQ494.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<p>OAuth2 协议与传统实现方案的对比图</p>
|
||||
|
||||
<p>但是想要实现 OAuth2 协议并没有那么简单。OAuth2 协议涉及的技术体系非常复杂,需要综合考虑用户认证、密码加解密和存储、Token 生成和校验、分布式 Session 和公私钥管理,以及完成各个客户端的权限管理。这时就<strong>需要引入专门的安全性开发框架</strong>,而<strong>Spring Security 就是这样一款开发框架</strong>。</p>
|
||||
|
||||
<p>Spring Security 专门提供了 UAA(User Account and Authentication,用户账户和认证)服务,封装了 OAuth2 协议,用于管理用户账户、OAuth2 客户端以及用于鉴权的 Token。而 UAA 也只是 Spring Security 众多核心功能中的一部分。</p>
|
||||
|
||||
<h3>你为什么需要学习这门课程?</h3>
|
||||
|
||||
<p>Spring Security 是 Spring 家族中一款历史比较悠久的开发框架,针对 Web 应用程序提供了一系列强大的安全性功能体系。</p>
|
||||
|
||||
<p>事实上,对于 Web 应用程序而言,除了分布式环境下的认证和授权漏洞之外,常见的安全性问题还包括跨站点脚本攻击、跨站点请求伪造、敏感数据暴露、缺乏方法级访问控制等。针对这些安全性问题,我们需要全面设计并实现对应的安全性功能,而 Spring Security 已经为开发人员提供了相应的解决方案,包括:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>用户信息管理;</li>
|
||||
|
||||
<li>敏感信息加解密;</li>
|
||||
|
||||
<li>用户认证;</li>
|
||||
|
||||
<li>权限控制;</li>
|
||||
|
||||
<li>跨站点请求伪造保护;</li>
|
||||
|
||||
<li>跨域支持;</li>
|
||||
|
||||
<li>全局安全方法;</li>
|
||||
|
||||
<li>单点登录;</li>
|
||||
|
||||
<li>……</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>同时,<strong>在普遍倡导用户隐私和数据价值的当下,掌握各种安全性相关技术的开发人员和架构师也成了稀缺人才,越发受到行业的认可和重视</strong>。</p>
|
||||
|
||||
<p><img src="assets/CioPOWC3WrqAGPu1AAClhPaa34E826.png" alt="Drawing 2.png" /></p>
|
||||
|
||||
<p>(职位信息来源:拉勾网)</p>
|
||||
|
||||
<p>对于开发人员而言,如何<strong>使用各种技术体系解决安全性问题</strong>是一大困惑。经验丰富的开发人员需要熟练使用 Spring Security 框架来应对业务发展的需求。例如,全面掌握 Spring Security 框架提供的认证、授权、方法级安全访问、OAuth2、JWT 等核心功能,构建自己对系统安全性设计的知识体系和解决方案。</p>
|
||||
|
||||
<p>而对于架构师而言,难点在于如何<strong>基于框架提供的功能并结合具体的业务场景,对框架进行扩展和定制化开发</strong>。这就需要他们对 Spring Security 中的用户认证和访问授权等核心功能的设计原理有充分的了解,能够从源码级别剖析框架的底层实现机制,进而满足更深层次的需求。</p>
|
||||
|
||||
<p>随着各种安全性问题的不断发生,可以说,<strong>安全性技术是构建个人技术体系不可缺少的一个环节,对于提升你的职业门槛也是一个重要的加分项</strong>。</p>
|
||||
|
||||
<h3>这门课程是如何设计的?</h3>
|
||||
|
||||
<p>任何技术体系的学习,都是一个边踩坑边前进的过程。我在从业生涯中,总结了一些使用 Spring Security 框架解决安全性问题时常见的、不可避免的痛点,大致可以分为以下几种。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>看上去简单,实则复杂</strong>:Spring Security 的一大特点是内置了很多基础功能,用起来很容易,让你觉得开发起来好像很快很简单,但实际上这些内置功能大多采用了默认实现机制,例如用户登录和登出、密码加解密等。它<strong>将系统开发的复杂度隐藏得很深</strong>,如果你不了解框架的核心内容,也就无法理解这些默认实现机制。因此,一旦在开发过程中出现问题,你会觉得一头雾水,定位问题和解决问题的难度也会加大。</li>
|
||||
|
||||
<li><strong>扩展性强大,但不易掌握</strong>:Spring Security 中提供的默认实现机制不一定能满足不同业务场景的需求,这就需要我们<strong>通过框架开发扩展功能</strong>。Spring Security 提供了面向认证、授权的开放式接口,也提供了过滤器等一系列扩展性功能。这些功能都很强大,但在使用过程中你会发现只有充分理解这些功能背后的设计原理,才能合理利用它们。盲目使用这些扩展性功能只会导致系统不稳定。</li>
|
||||
|
||||
<li><strong>技术体系和组件众多</strong>:Spring Security 提供了一大批功能组件,这些功能组件构成了庞大的技术体系。你会发现,好不容易学会了一个组件,<strong>碰到新的组件还是需要重新学习</strong>,导致学习效率很低,并且容易出错。同时,Spring Security 中的很多功能都是集成了市面上的一些开源组件和方案,如果你不了解这些组件和方案,在使用过程中很可能出现一些莫名其妙的问题,影响开发节奏。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>基于以上问题,我根据自己多年的架构经验,以及对安全性、Spring Security 框架的理解和实践,总结了<strong>一套由浅入深的学习路径,不仅可以带你掌握 Spring Security 框架的全局,还从实战角度出发,帮助你高效掌握基于 Spring Security 框架的系统安全性设计方法和开发技巧</strong>。</p>
|
||||
|
||||
<p>本专栏分为以下四大模块,能够帮助你解决前面提到的开发上的痛点。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><strong>模块一:基础功能篇</strong>。这部分我将介绍 Spring Security 的一些基础性功能,包括<strong>认证、授权和加密</strong>。这些功能是 Spring Security 框架的入口,可以说使用这个框架就离不开这些功能,而框架的其他功能也都依赖于这些基础特性,所以我不仅会介绍它们的使用方法,还将对原理进行展开。通过这一模块的学习,你可以在日常开发过程中完成对用户身份的认证、访问的授权以及集成加密机制,从而<strong>为 Web 应用程序添加基础的安全性功能</strong>。</li>
|
||||
|
||||
<li><strong>模块二:高级主题篇</strong>。这部分功能面向特定需求,用于<strong>构建比较复杂的应用场景</strong>,包括过滤器、跨站点请求伪造保护、跨域资源共享,以及针对非 Web 应用程序的全局方法安全机制。通过这一模块的学习,一方面,针对 Web 应用程序,你可以使用过滤器来定制化各种安全性策略,并集成跨站请求伪造保护和跨域访问等功能。另一方面,针对非 Web 类应用程序,你也可以使用 Spring Security 来完成方法级别的安全控制。</li>
|
||||
|
||||
<li><strong>模块三:OAuth2 与微服务篇</strong>。这部分内容关注微服务开发框架<strong>Spring Cloud 与 Spring Security 之间的整合</strong>,我们将对 OAuth2 协议和 JWT 全面展开讲解,并使用这些技术体系构建安全的微服务系统,以及单点登录系统。微服务是目前系统开发的主流技术体系,而微服务的安全性也是日常开发的一个重要主题。通过这一模块的学习,你可以掌握如何<strong>为目前主流的微服务架构添加安全性</strong>,并使用 OAuth2 协议和 JWT 实现对服务级别的授权访问。</li>
|
||||
|
||||
<li><strong>模块四:框架扩展篇</strong>。这部分内容是对 Spring Security 框架在应用上的一些扩展,包括在 Spring Security 中<strong>引入全新的响应式编程技术</strong>,以及如何对应用程序安全性进行测试的系统方法。通过这一模块的学习,你将掌握对 Spring Security 框架所提供的各项功能进行测试的系统方法。同时,随着 Spring 5 的发布也涌现出了响应式编程这种新型技术体系,新版本的 Spring Security 中也提供了对响应式编程的全面支持,本专栏对响应式 Spring Security 也做了系统介绍。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>从学习的角度来讲,Spring Security 中的一些功能看上去比较简单,但用起来并非容易全面掌握,经常会因为某一些细小的配置导致整个功能无法使用,能发现问题但不容易找到原因。因此,<strong>虽然 Spring Security 让你只花 20% 的时间就可以解决 80% 的问题,但是剩下 20% 的问题需要我们通过系统性的学习去弄懂</strong>,而学习 Spring Security 是有一定的方法和套路的。这就需要有系统化的讲解过程,也是本专栏价值所在。</p>
|
||||
|
||||
<p>此外,各个 Spring Security 核心组件以及使用方式,我都会按照完整的案例分析给出详细的代码实现方案,方便你进行学习和改造。课程配套代码,你可以在<a href="https://github.com/lagouEdAnna/SpringSecurity-jianxiang?fileGuid=xxQTRXtVcqtHK6j8">https://github.com/lagouEdAnna/SpringSecurity-jianxiang</a>进行下载。</p>
|
||||
|
||||
<h3>讲师寄语</h3>
|
||||
|
||||
<p>在现代互联网应用系统开发的过程中,安全性越来越受到重视。如何确保系统没有安全性漏洞是一大挑战,我们需要引入对应的技术体系和开发框架,以满足不断增长的安全性需求。面对这种困境,引入 Spring 家族生态中的重要组成部分——Spring Security 开源框架,可以称得上是一个绝佳选择。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">下一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997532ec493d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||||
|
||||
<script>
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
|
||||
|
||||
function gtag() {
|
||||
|
||||
dataLayer.push(arguments);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
|
||||
var path = window.location.pathname
|
||||
|
||||
var cookie = getCookie("lastPath");
|
||||
|
||||
console.log(path)
|
||||
|
||||
if (path.replace("/", "") === "") {
|
||||
|
||||
if (cookie.replace("/", "") !== "") {
|
||||
|
||||
console.log(cookie)
|
||||
|
||||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
setCookie("lastPath", path)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
|
||||
var d = new Date();
|
||||
|
||||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||||
|
||||
var expires = "expires=" + d.toGMTString();
|
||||
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
|
||||
var name = cname + "=";
|
||||
|
||||
var ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
|
||||
var c = ca[i].trim();
|
||||
|
||||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,742 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="icon" href="/static/favicon.png">
|
||||
|
||||
<title>01 顶级框架:Spring Security 是一款什么样的安全性框架?.md</title>
|
||||
|
||||
<!-- Spectre.css framework -->
|
||||
|
||||
<link rel="stylesheet" href="/static/index.css">
|
||||
|
||||
<!-- theme css & js -->
|
||||
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div class="book-container">
|
||||
|
||||
<div class="book-sidebar">
|
||||
|
||||
<div class="book-brand">
|
||||
|
||||
<a href="/">
|
||||
|
||||
<img src="/static/favicon.png">
|
||||
|
||||
<span>技术文章摘抄</span>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-menu uncollapsible">
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="../">上一级</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md">00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">01 顶级框架:Spring Security 是一款什么样的安全性框架?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/02 用户认证:如何使用 Spring Security 构建用户认证体系?.md">02 用户认证:如何使用 Spring Security 构建用户认证体系?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/03 认证体系:如何深入理解 Spring Security 用户认证机制?.md">03 认证体系:如何深入理解 Spring Security 用户认证机制?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/04 密码安全:Spring Security 中包含哪些加解密技术?.md">04 密码安全:Spring Security 中包含哪些加解密技术?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/05 访问授权:如何对请求的安全访问过程进行有效配置?.md">05 访问授权:如何对请求的安全访问过程进行有效配置?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/06 权限管理:如何剖析 Spring Security 的授权原理?.md">06 权限管理:如何剖析 Spring Security 的授权原理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md">07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md">08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md">09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/10 全局方法:如何确保方法级别的安全访问?.md">10 全局方法:如何确保方法级别的安全访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md">11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/12 开放协议:OAuth2 协议解决的是什么问题?.md">12 开放协议:OAuth2 协议解决的是什么问题?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md">13 授权体系:如何构建 OAuth2 授权服务器?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/14 资源保护:如何基于 OAuth2 协议配置授权过程?.md">14 资源保护:如何基于 OAuth2 协议配置授权过程?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/15 令牌扩展:如何使用 JWT 实现定制化 Token?.md">15 令牌扩展:如何使用 JWT 实现定制化 Token?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md">16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md">17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md">18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/19 测试驱动:如何基于 Spring Security 测试系统安全性?.md">19 测试驱动:如何基于 Spring Security 测试系统安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/20 结束语 以终为始,Spring Security 的学习总结.md">20 结束语 以终为始,Spring Security 的学习总结.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function add_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function remove_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sidebar_toggle() {
|
||||
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let content = document.querySelector('.off-canvas-content')
|
||||
|
||||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||||
|
||||
sidebar_toggle.classList.remove('extend')
|
||||
|
||||
sidebar.classList.remove('hide')
|
||||
|
||||
content.classList.remove('extend')
|
||||
|
||||
} else { // hide
|
||||
|
||||
sidebar_toggle.classList.add('extend')
|
||||
|
||||
sidebar.classList.add('hide')
|
||||
|
||||
content.classList.add('extend')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.add('show')
|
||||
|
||||
overlay.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
function hide_canvas() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.remove('show')
|
||||
|
||||
overlay.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="off-canvas-content">
|
||||
|
||||
<div class="columns">
|
||||
|
||||
<div class="column col-12 col-lg-12">
|
||||
|
||||
<div class="book-navbar">
|
||||
|
||||
<!-- For Responsive Layout -->
|
||||
|
||||
<header class="navbar">
|
||||
|
||||
<section class="navbar-section">
|
||||
|
||||
<a onclick="open_sidebar()">
|
||||
|
||||
<i class="icon icon-menu"></i>
|
||||
|
||||
</a>
|
||||
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
overflow-y: hidden;">
|
||||
|
||||
<div class="book-post">
|
||||
|
||||
<p id="tip" align="center"></p>
|
||||
|
||||
<div><h1>01 顶级框架:Spring Security 是一款什么样的安全性框架?</h1>
|
||||
|
||||
<p>在开篇词中,我们描述了 Web 应用程序的安全性需求,并引出了专门用来满足这些需求的 Spring Security 框架。Spring Security 是 Spring 家族中历史比较悠久的框架,具备完整而强大的功能体系。从今天开始,我们将正式学习 Spring Security,并围绕它的功能体系展开讨论。</p>
|
||||
|
||||
<h3>初识 Spring Security</h3>
|
||||
|
||||
<p>其实在 Spring Boot 出现之前,Spring Security 就已经诞生多年了。但 Spring Security 的发展一直都不是很顺利,主要问题在于应用程序中集成和配置 Spring Security 框架的过程比较复杂。但是随着 Spring Boot 的兴起,基于 Spring Boot 所提供的针对 Spring Security 的自动配置方案,开发人员可以零配置使用 Spring Security。如果想要在 Spring Boot 应用程序中使用 Spring Security,只需要在 Maven 工程的 pom 文件中添加如下依赖:</p>
|
||||
|
||||
<pre><code><dependency>
|
||||
|
||||
|
||||
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
|
||||
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
|
||||
|
||||
|
||||
</dependency>
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>接下来,让我们构建一个简单的 HTTP 端点,如下所示:</p>
|
||||
|
||||
<pre><code>@RestController
|
||||
|
||||
|
||||
|
||||
public class DemoController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@GetMapping("/hello")
|
||||
|
||||
|
||||
|
||||
public String hello() {
|
||||
|
||||
|
||||
|
||||
return "Hello World!";
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>现在,启动这个 Spring Boot 应用程序,然后通过浏览器访问"/hello"端点。你可能希望得到的是"Hello World!"这个输出结果,但事实上,浏览器会跳转到一个如下所示的登录界面:</p>
|
||||
|
||||
<p><img src="assets/Cgp9HWC5_NmAKJV2AABBm4b32Ns284.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>Spring Security 内置的登录界面</p>
|
||||
|
||||
<p>那么,为什么会弹出这个登录界面呢?原因就在于我们添加了 spring-boot-starter-security 依赖之后,Spring Security 为应用程序<strong>自动嵌入了用户认证机制</strong>。</p>
|
||||
|
||||
<p>接下来,我们围绕这个登录场景,分析如何获取登录所需的用户名和密码。我们注意到在 Spring Boot 的控制台启动日志中,出现了如下所示的一行日志:</p>
|
||||
|
||||
<pre><code>Using generated security password: 707d7469-631f-4d92-ab71-3809620fe0dc
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>这行日志就是 Spring Security 生成的一个密码,而用户名则是系统默认的“user”。通过输入正确的用户名和密码,浏览器就会输出"Hello World!"这个响应结果。你可以做一些尝试。</p>
|
||||
|
||||
<p>上述过程演示的就是 Spring Security 提供的认证功能,也是 Spring Security 众多功能中的一项基础功能。接下来,就让我们一同挖掘 Spring Security 中的完整功能体系。</p>
|
||||
|
||||
<h3>Spring Security 功能体系</h3>
|
||||
|
||||
<p>Spring Security 提供的是一整套完整的安全性解决方案。面向不同的业务需求和应用场景,Spring Security 分别提供了对应的安全性功能,在接下来的内容中,我们将从<strong>单体应用、微服务架构以及响应式系统</strong>这三个维度对这些功能展开讨论。</p>
|
||||
|
||||
<h4>Spring Security 与单体应用</h4>
|
||||
|
||||
<p>在软件系统中,我们可以把需要访问的内容定义为是一种资源(Resource),而安全性设计的核心目标就是对这些资源进行保护,确保对它们的访问是安全可控的。例如,在一个 Web 应用程序中,对外暴露的 HTTP 端点就可以被理解为是资源。对于资源的安全性访问,业界也存在一些常见的技术体系。在讲解这些技术体系之前,我们先来理解在安全领域中非常常见但又容易混淆的两个概念,即认证(Authentication)和授权(Authorization)。</p>
|
||||
|
||||
<p>首先我们需要明确,所谓认证,解决的是“你是谁”这一个问题,也就是说对于每一次访问请求,系统都能判断出访问者是否具有合法的身份标识。</p>
|
||||
|
||||
<p>一旦明确 “<strong>你是谁</strong>”,下一步就可以判断“<strong>你能做什么</strong>”,这个步骤就是授权。通用的授权模型大多基于权限管理体系,即对资源、权限、角色和用户的一种组合处理。</p>
|
||||
|
||||
<p>如果我们将认证和授权结合起来,就构成了对系统中的资源进行安全性管理的最常见解决方案,即先判断资源访问者的有效身份,再来确定其是否有对这个资源进行访问的合法权限,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_OuABmKhAABO7TPypr0449.png" alt="Drawing 2.png" /></p>
|
||||
|
||||
<p>基于认证和授权机制的资源访问安全性示意图</p>
|
||||
|
||||
<p>上图代表的是一种通用方案,而不同的应用场景和技术体系下可以衍生出很多具体的实现策略。Web 应用系统中的认证和授权模型与上图类似,但在具体设计和实现过程中也有其特殊性。</p>
|
||||
|
||||
<p>针对认证,这部分的需求相对比较明确。显然我们需要构建<strong>一套完整的存储体系</strong>来保存和维护用户信息,并且确保这些用户信息在处理请求的过程中能够得到合理的利用。</p>
|
||||
|
||||
<p>而针对授权,情况可能会比较复杂。对于某一个特定的 Web 应用程序而言,我们面临的第一个问题是如何判断一个 HTTP 请求是否具备访问自己的权限。更进一步,就算这个请求具备访问该应用程序的权限,但并不意味着该请求能够访问应用程序所有的 HTTP 端点。某些核心功能需要具备较高的权限才能访问,而有些则不需要。这就是我们需要解决的第二个问题,如何<strong>对访问的权限进行精细化管理</strong>?如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_POAP95KAABUwwJSnkM160.png" alt="Drawing 4.png" /></p>
|
||||
|
||||
<p>Web 应用程序访问授权效果示意图</p>
|
||||
|
||||
<p>在上图中,我们假设该请求具备对应用程序中端点 2、3、4 的访问权限,但不具备访问端点 1 的权限。想要达到这种效果,一般的做法是<strong>引入角色体系</strong>。我们对不同的用户设置不同等级的角色,角色等级不同对应的访问权限也不同。而每一个请求都可以绑定到某一个角色,也就具备了访问权限。</p>
|
||||
|
||||
<p>接下来,我们把认证和授权结合起来,梳理出 Web 应用程序访问场景下的安全性实现方案,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_QiAM0UXAAA8v_6Frio548.png" alt="Drawing 6.png" /></p>
|
||||
|
||||
<p>单体服务下的认证和授权整合示意图</p>
|
||||
|
||||
<p>结合示意图我们可以看到,通过请求传递用户凭证完成用户认证,然后根据该用户信息中具备的角色信息获取访问权限,并最终完成对 HTTP 端点的访问授权。</p>
|
||||
|
||||
<p>围绕认证和授权,我们还需要一系列的额外功能确保整个流程得以实现。这些功能包括用于密码保护的加解密机制、用于实现方法级的安全访问,以及支持跨域等,这些功能在我们专栏的后续内容中都会一一展开讨论。</p>
|
||||
|
||||
<h4>Spring Security 与微服务架构</h4>
|
||||
|
||||
<p>微服务架构的情况要比单体应用复杂很多,因为涉及了服务与服务之间的<strong>调用关系</strong>。我们继续沿用“资源”这个概念,对应到微服务系统中,服务提供者充当的角色就是资源的服务器,而服务消费者就是客户端。所以各个服务本身既可以是客户端,也可以作为资源服务器,或者两者兼之。</p>
|
||||
|
||||
<p>接下来,我们把认证和授权结合起来,梳理出微服务访问场景下的安全性实现方案,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_RGAOITBAACOS5oiq6U626.png" alt="Drawing 8.png" /></p>
|
||||
|
||||
<p>微服务架构下的认证和授权整合示意图</p>
|
||||
|
||||
<p>可以看到,与单体应用相比,在微服务架构中需要把认证和授权的过程进行集中化管理,所以在上图中出现了一个<strong>授权中心。</strong> 授权中心会获取客户端请求中所带有的身份凭证信息,然后基于凭证信息生成一个 Token,这个 Token 中就包含了<strong>权限范围和有效期</strong>。</p>
|
||||
|
||||
<p>客户端获取 Token 之后就可以基于这个 Token 发起对微服务的访问。这个时候,服务的提供者需要对这个 Token 进行认证,并根据 Token 的权限范围和有效期从授权中心获取该请求能够访问的特定资源。在微服务系统中,对外的资源表现形式同样可以理解为一个个 HTTP 端点。</p>
|
||||
|
||||
<p>上图中关键点就在于构建用于生成和验证 Token 的授权中心,为此我们需要引入<strong>OAuth2 协议</strong>。OAuth2 协议为我们在客户端程序和资源服务器之间设置了一个<strong>授权层</strong>,并确保 Token 能够在各个微服务中进行有效传递,如下图所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_R6AGH-TAAB7aKF2qcE086.png" alt="Drawing 10.png" /></p>
|
||||
|
||||
<p>OAuth2 协议在服务访问场景中的应用</p>
|
||||
|
||||
<p>OAuth2 是一个相对复杂的协议,综合应用摘要认证、签名认证、HTTPS 等安全性手段,需要提供 Token 生成和校验以及公私钥管理等功能,同时需要开发者入驻并进行权限粒度控制。一般我们应该避免自行实现这类复杂的协议,倾向于借助于特定工具以免重复造轮子。而 Spring Security 为我们提供了实现这一协议的完整解决方案,我们可以使用该框架完成适用于微服务系统中的认证授权机制。</p>
|
||||
|
||||
<h4>Spring Security 与响应式系统</h4>
|
||||
|
||||
<p>随着 Spring 5 的发布,我们迎来了响应式编程(Reactive Programming)的全新发展时期。响应式编程是 Spring 5 最核心的新功能,也是 Spring 家族目前重点推广的技术体系。Spring 5 的响应式编程模型以 Project Reactor 库为基础,后者则实现了响应式流规范。</p>
|
||||
|
||||
<p>事实上,Spring Boot 从 2.x 版本开始也全面依赖 Spring 5。同样,在 Spring Security 中,用户账户体系的建立、用户认证和授权、方法级别的安全访问、OAuth2 协议等传统开发模式下具备的安全性功能都具备对应的响应式版本。</p>
|
||||
|
||||
<h3>小结与预告</h3>
|
||||
|
||||
<p>本讲是整个专栏内容的第一讲,我们通过一个简单的示例引入了 Spring Security 框架,并基于日常开发的安全需求,全面剖析了 Spring Security 框架具备的功能体系。不同的功能对应不同的应用场景,在普通的单体应用、微服务架构、响应式系统中都可以使用 Spring Security 框架提供的各种功能确保系统的安全性。</p>
|
||||
|
||||
<p>本讲内容总结如下:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_SiAd8m0AABqq0x1Qls852.png" alt="Drawing 12.png" /></p>
|
||||
|
||||
<p>这里给你留一道思考题:针对单体应用和微服务架构,你能分别描述它们所需要的认证和授权机制吗?</p>
|
||||
|
||||
<p>接下来我们将正式进入到 Spring Security 框架各项功能的学习过程中,首先介绍的就是用户认证功能。下一讲,我们将讨论如何基于 Spring Security 对用户进行有效的认证。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/02 用户认证:如何使用 Spring Security 构建用户认证体系?.md">下一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099753558a93d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||||
|
||||
<script>
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
|
||||
|
||||
function gtag() {
|
||||
|
||||
dataLayer.push(arguments);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
|
||||
var path = window.location.pathname
|
||||
|
||||
var cookie = getCookie("lastPath");
|
||||
|
||||
console.log(path)
|
||||
|
||||
if (path.replace("/", "") === "") {
|
||||
|
||||
if (cookie.replace("/", "") !== "") {
|
||||
|
||||
console.log(cookie)
|
||||
|
||||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
setCookie("lastPath", path)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
|
||||
var d = new Date();
|
||||
|
||||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||||
|
||||
var expires = "expires=" + d.toGMTString();
|
||||
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
|
||||
var name = cname + "=";
|
||||
|
||||
var ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
|
||||
var c = ca[i].trim();
|
||||
|
||||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,990 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="icon" href="/static/favicon.png">
|
||||
|
||||
<title>02 用户认证:如何使用 Spring Security 构建用户认证体系?.md</title>
|
||||
|
||||
<!-- Spectre.css framework -->
|
||||
|
||||
<link rel="stylesheet" href="/static/index.css">
|
||||
|
||||
<!-- theme css & js -->
|
||||
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div class="book-container">
|
||||
|
||||
<div class="book-sidebar">
|
||||
|
||||
<div class="book-brand">
|
||||
|
||||
<a href="/">
|
||||
|
||||
<img src="/static/favicon.png">
|
||||
|
||||
<span>技术文章摘抄</span>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-menu uncollapsible">
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="../">上一级</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md">00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">01 顶级框架:Spring Security 是一款什么样的安全性框架?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/Spring Security 详解与实操/02 用户认证:如何使用 Spring Security 构建用户认证体系?.md">02 用户认证:如何使用 Spring Security 构建用户认证体系?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/03 认证体系:如何深入理解 Spring Security 用户认证机制?.md">03 认证体系:如何深入理解 Spring Security 用户认证机制?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/04 密码安全:Spring Security 中包含哪些加解密技术?.md">04 密码安全:Spring Security 中包含哪些加解密技术?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/05 访问授权:如何对请求的安全访问过程进行有效配置?.md">05 访问授权:如何对请求的安全访问过程进行有效配置?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/06 权限管理:如何剖析 Spring Security 的授权原理?.md">06 权限管理:如何剖析 Spring Security 的授权原理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md">07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md">08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md">09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/10 全局方法:如何确保方法级别的安全访问?.md">10 全局方法:如何确保方法级别的安全访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md">11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/12 开放协议:OAuth2 协议解决的是什么问题?.md">12 开放协议:OAuth2 协议解决的是什么问题?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md">13 授权体系:如何构建 OAuth2 授权服务器?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/14 资源保护:如何基于 OAuth2 协议配置授权过程?.md">14 资源保护:如何基于 OAuth2 协议配置授权过程?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/15 令牌扩展:如何使用 JWT 实现定制化 Token?.md">15 令牌扩展:如何使用 JWT 实现定制化 Token?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md">16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md">17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md">18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/19 测试驱动:如何基于 Spring Security 测试系统安全性?.md">19 测试驱动:如何基于 Spring Security 测试系统安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/20 结束语 以终为始,Spring Security 的学习总结.md">20 结束语 以终为始,Spring Security 的学习总结.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function add_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function remove_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sidebar_toggle() {
|
||||
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let content = document.querySelector('.off-canvas-content')
|
||||
|
||||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||||
|
||||
sidebar_toggle.classList.remove('extend')
|
||||
|
||||
sidebar.classList.remove('hide')
|
||||
|
||||
content.classList.remove('extend')
|
||||
|
||||
} else { // hide
|
||||
|
||||
sidebar_toggle.classList.add('extend')
|
||||
|
||||
sidebar.classList.add('hide')
|
||||
|
||||
content.classList.add('extend')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.add('show')
|
||||
|
||||
overlay.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
function hide_canvas() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.remove('show')
|
||||
|
||||
overlay.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="off-canvas-content">
|
||||
|
||||
<div class="columns">
|
||||
|
||||
<div class="column col-12 col-lg-12">
|
||||
|
||||
<div class="book-navbar">
|
||||
|
||||
<!-- For Responsive Layout -->
|
||||
|
||||
<header class="navbar">
|
||||
|
||||
<section class="navbar-section">
|
||||
|
||||
<a onclick="open_sidebar()">
|
||||
|
||||
<i class="icon icon-menu"></i>
|
||||
|
||||
</a>
|
||||
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
overflow-y: hidden;">
|
||||
|
||||
<div class="book-post">
|
||||
|
||||
<p id="tip" align="center"></p>
|
||||
|
||||
<div><h1>02 用户认证:如何使用 Spring Security 构建用户认证体系?</h1>
|
||||
|
||||
<p>上一讲中,我们引入了 Spring Security 框架,并梳理了它的各项核心功能。从今天开始,我们就对这些功能一一展开讲解,首先要讨论的就是<strong>用户认证功能</strong>。用户认证涉及用户账户体系的构建,也是<strong>实现授权管理的前提</strong>。在 Spring Security 中,实现用户认证的方式有很多,下面我们就结合框架提供的配置体系进行梳理。</p>
|
||||
|
||||
<h3>Spring Security 配置体系</h3>
|
||||
|
||||
<p>在 Spring Security 中,因为认证和授权等功能通常都不止有一种实现方法,所以框架开发了一套完整的配置体系来对这些功能进行灵活设置。开发人员在使用认证和授权等功能时就依赖于如何合理利用和扩展这套配置体系。</p>
|
||||
|
||||
<p>例如,针对用户账户存储这个切入点,就可以设计出多种不同的策略。我们可以把用户名和密码保存在内存中,作为一种轻量级的实现方式。更常见的,也可以把这些认证信息存储在关系型数据库中。当然,如果我们使用了 LDAP 协议,那么文件系统也是一种不错的存储媒介。</p>
|
||||
|
||||
<p>显然,针对这些可选择的实现方式,需要为开发人员提供一种机制以便他们能够根据自身的需求进行灵活的设置,这就是配置体系的作用。</p>
|
||||
|
||||
<p>同时,你应该也注意到了,在上一讲的示例中,我们没有进行任何的配置也能让 Spring Security 发挥作用,这就说明框架内部的功能采用了<strong>特定的默认配置</strong>。就用户认证这一场景而言,Spring Security 内部就初始化了一个默认的用户名“user”并且在应用程序启动时自动生成一个密码。当然,通过这种方式自动生成的密码在每次启动应用时都会发生变化,并不适合面向正式的应用。</p>
|
||||
|
||||
<p>我们可以通过翻阅框架的源代码(<a href="https://github.com/spring-projects/spring-security?fileGuid=xxQTRXtVcqtHK6j8">https://github.com/spring-projects/spring-security</a>)来进一步理解 Spring Security 中的一些默认配置。在 Spring Security 中,初始化用户信息依赖的配置类是 WebSecurityConfigurer 接口,该接口实际上是一个空接口,继承了更为基础的 SecurityConfigurer 接口。</p>
|
||||
|
||||
<p>在日常开发中,通常不需要我们自己实现这个接口,而是<strong>使用 WebSecurityConfigurerAdapter 类</strong>来简化该配置类的使用方式。而在 WebSecurityConfigurerAdapter 中我们发现了如下所示的 configure 方法:</p>
|
||||
|
||||
<pre><code>protected void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
http
|
||||
|
||||
|
||||
|
||||
.authorizeRequests()
|
||||
|
||||
|
||||
|
||||
.anyRequest().authenticated()
|
||||
|
||||
|
||||
|
||||
.and()
|
||||
|
||||
|
||||
|
||||
.formLogin().and()
|
||||
|
||||
|
||||
|
||||
.httpBasic();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>上述代码就是 Spring Security 中作用于用户认证和访问授权的默认实现,这里用到了多个常见的配置方法。再次回想上一讲中我们讲到的,一旦在代码类路径中引入 Spring Security 框架之后,访问任何端点时就会弹出一个登录界面用来完成用户认证。<strong>认证是授权的前置流程</strong>,认证结束之后就可以进入到授权环节。</p>
|
||||
|
||||
<p>结合这些配置方法,我们来简单分析一下这种默认效果是如何实现的:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>首先,通过 HttpSecurity 类的 authorizeRequests() 方法对所有访问 HTTP 端点的 HttpServletRequest 进行限制;</li>
|
||||
|
||||
<li>然后,anyRequest().authenticated() 语句指定了对于所有请求都需要执行认证,也就是说没有通过认证的用户就无法访问任何端点;</li>
|
||||
|
||||
<li>接着,formLogin() 语句用于指定使用表单登录作为认证方式,也就是会弹出一个登录界面;</li>
|
||||
|
||||
<li>最后,httpBasic() 语句表示可以使用 HTTP 基础认证(Basic Authentication)方法来完成认证。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>在日常开发过程中,我们可以继承 WebSecurityConfigurerAdapter 类并且覆写上述的 configure() 方法来完成配置工作。而在 Spring Security 中,存在一批类似于 WebSecurityConfigurerAdapter 的配置类。</p>
|
||||
|
||||
<p><strong>配置体系是开发人员使用 Spring Security 框架的主要手段之一</strong>,关于配置体系的讨论会贯穿我们整个专栏的始终。随着内容深度的演进,Spring Security 所提供的全面而灵活的配置功能也将一一展现在你的面前。</p>
|
||||
|
||||
<h3>实现 HTTP 基础认证和表单登录认证</h3>
|
||||
|
||||
<p>在上文中,我们提到了 httpBasic() 和 formLogin() 这两种用于控制用户认证的实现手段,分别代表了<strong>HTTP 基础认证和表单登录认证</strong>。在构建 Web 应用程序时,我们也可以在 Spring Security 提供的认证机制的基础上进行扩展,以满足日常开发需求。</p>
|
||||
|
||||
<h4>HTTP 基础认证</h4>
|
||||
|
||||
<p>HTTP 基础认证的原理比较简单,只需<strong>通过 HTTP 协议的消息头携带用户名和密码</strong>进行登录验证。在上一讲中,我们已经通过浏览器简单验证了用户登录操作。今天,我们将引入 Postman 这款可视化的 HTTP 请求工具来对登录的请求和响应过程做进一步分析。</p>
|
||||
|
||||
<p>在 Postman 中,我们直接访问<a href="http://localhost:8080/hello?fileGuid=xxQTRXtVcqtHK6j8">http://localhost:8080/hello</a>端点,会得到如下所示的响应:</p>
|
||||
|
||||
<pre><code>{
|
||||
|
||||
|
||||
|
||||
"timestamp": "2021-02-08T03:45:21.512+00:00",
|
||||
|
||||
|
||||
|
||||
"status": 401,
|
||||
|
||||
|
||||
|
||||
"error": "Unauthorized",
|
||||
|
||||
|
||||
|
||||
"message": "",
|
||||
|
||||
|
||||
|
||||
"path": "/hello"
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>显然,响应码 401 告诉我们没有访问该地址的权限。同时,在响应中出现了一个“WWW-Authenticate”消息头,其值为“Basic realm="Realm"”,这里的 Realm 表示 Web 服务器中受保护资源的安全域。</p>
|
||||
|
||||
<p>现在,让我们来执行 HTTP 基础认证,可以通过设置认证类型为“Basic Auth”并输入对应的用户名和密码来完成对 HTTP 端点的访问,设置界面如下所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_duASv7jAACBgx8x8WU605.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>使用 Postman 完成 HTTP 基础认证信息的设置</p>
|
||||
|
||||
<p>现在查看 HTTP 请求,可以看到 Request Header 中添加了 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Authorization?fileGuid=xxQTRXtVcqtHK6j8">Authorization</a> 标头,格式为:Authorization:<code> <type></code> <code><credentials</code>>。这里的 type 就是“Basic”,而 credentials 则是这样一个字符串:</p>
|
||||
|
||||
<pre><code>dXNlcjo5YjE5MWMwNC1lNWMzLTQ0YzctOGE3ZS0yNWNkMjY3MmVmMzk=
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>这个字符串就是<strong>将用户名和密码组合在一起,再经过 Base64 编码得到的结果</strong>。而我们知道 Base64 只是一种编码方式,并没有集成加密机制,所以本质上传输的还是<strong>明文形式</strong>。</p>
|
||||
|
||||
<p>当然,想要在应用程序中启用 HTTP 基础认证还是比较简单的,只需要在 WebSecurityConfigurerAdapter 的 configure 方法中添加如下配置即可:</p>
|
||||
|
||||
<pre><code>protected void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
http.httpBasic();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>HTTP 基础认证比较简单,没有定制的登录页面,所以单独使用的场景比较有限。在使用 Spring Security 时,我们<strong>一般会把 HTTP 基础认证和接下来要介绍的表单登录认证结合起来</strong>一起使用。</p>
|
||||
|
||||
<h4>表单登录认证</h4>
|
||||
|
||||
<p>在 WebSecurityConfigurerAdapter 的 configure() 方法中,一旦配置了 HttpSecurity 的 formLogin() 方法,就启动了表单登录认证,如下所示:</p>
|
||||
|
||||
<pre><code>protected void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
http.formLogin();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>formLogin() 方法的执行效果就是提供了一个默认的登录界面,如下所示:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_fmAOso9AAAqvMlklW8869.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<p>Spring Security 默认的登录界面</p>
|
||||
|
||||
<p>我们已经在上一讲中看到过这个登录界面。对于登录操作而言,这个登录界面通常都是定制化的,同时,我们也需要对登录的过程和结果进行细化控制。此时,我们就可以通过如下所示的配置内容来修改系统的默认配置:</p>
|
||||
|
||||
<pre><code>@Override
|
||||
|
||||
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
http
|
||||
|
||||
|
||||
|
||||
.formLogin()
|
||||
|
||||
|
||||
|
||||
.loginPage("/login.html")//自定义登录页面
|
||||
|
||||
|
||||
|
||||
.loginProcessingUrl("/action")//登录表单提交时的处理地址
|
||||
|
||||
|
||||
|
||||
.defaultSuccessUrl("/index");//登录认证成功后的跳转页面
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>可以看到,这里我们对登录界面、登录请求处理地址以及登录成功后的跳转界面进行了定制化。</p>
|
||||
|
||||
<h3>配置 Spring Security 用户认证体系</h3>
|
||||
|
||||
<p>讲完配置体系,现在让我们回到用户认证场景。因为 Spring Security 默认提供的用户名是固定的,而密码会随着每次应用程序的启动而变化,所以很不灵活。在 Spring Boot 中,我们可以通过在 application.yml 配置文件中添加如下所示的配置项来改变这种默认行为:</p>
|
||||
|
||||
<pre><code>spring:
|
||||
|
||||
|
||||
|
||||
security:
|
||||
|
||||
|
||||
|
||||
user:
|
||||
|
||||
|
||||
|
||||
name: spring
|
||||
|
||||
|
||||
|
||||
password: spring_password
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>现在让我们重启应用,就可以使用上述用户名和密码完成登录。基于配置文件的用户信息存储方案简单直接,但显然也<strong>缺乏灵活性</strong>,因为我们无法在系统运行时<strong>动态加载对应的用户名和密码</strong>。因此,在现实中,我们主要还是通过使用 WebSecurityConfigurerAdapter 配置类来改变默认的配置行为。</p>
|
||||
|
||||
<p>通过前面的内容中,我们已经知道可以通过 WebSecurityConfigurerAdapter 类的 configure(HttpSecurity http) 方法来完成认证。认证过程涉及 Spring Security 中用户信息的交互,我们可以通过继承 WebSecurityConfigurerAdapter 类并且覆写其中的 configure(AuthenticationManagerBuilder auth) 的方法来完成对用户信息的配置工作。请注意<strong>这是两个不同的 configure() 方法</strong>。</p>
|
||||
|
||||
<p>针对 WebSecurityConfigurer 配置类,我们首先需要明确配置的内容。实际上,初始化用户信息非常简单,只需要指定用户名(Username)、密码(Password)和角色(Role)这三项数据即可。在 Spring Security 中,基于 AuthenticationManagerBuilder 工具类为开发人员提供了<strong>基于内存、JDBC、LDAP 等多种验证方案</strong>。</p>
|
||||
|
||||
<p>接下来,我们就围绕 AuthenticationManagerBuilder 提供的功能来实现多种用户信息存储方案。</p>
|
||||
|
||||
<h4>使用基于内存的用户信息存储方案</h4>
|
||||
|
||||
<p>我们先来看如何使用 AuthenticationManagerBuilder 完成基于内存的用户信息存储方案。实现方法就是调用 AuthenticationManagerBuilder 的 inMemoryAuthentication 方法,示例代码如下:</p>
|
||||
|
||||
<pre><code>@Override
|
||||
|
||||
|
||||
|
||||
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
builder.inMemoryAuthentication()
|
||||
|
||||
|
||||
|
||||
.withUser("spring_user").password("password1").roles("USER")
|
||||
|
||||
|
||||
|
||||
.and()
|
||||
|
||||
|
||||
|
||||
.withUser("spring_admin").password("password2").roles("USER", "ADMIN");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>从上面的代码中,我们可以看到系统中存在“spring_user”和“spring_admin”这两个用户,其密码分别是"password1"和"password2",在角色上也分别代表着普通用户 USER 以及管理员 ADMIN。</p>
|
||||
|
||||
<p>请注意,这里的 roles() 方法背后使用的还是<strong>authorities() 方法</strong>。通过 roles() 方法,Spring Security 会在每个角色名称前自动添加“ROLE_”前缀,所以我们也可以通过如下所示的代码实现同样的功能:</p>
|
||||
|
||||
<pre><code>@Override
|
||||
|
||||
|
||||
|
||||
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
builder.inMemoryAuthentication()
|
||||
|
||||
|
||||
|
||||
.withUser("spring_user").password("password1").authorities("ROLE_USER")
|
||||
|
||||
|
||||
|
||||
.and()
|
||||
|
||||
|
||||
|
||||
.withUser("spring_admin").password("password2").authorities("ROLE_USER", "ROLE_ADMIN");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>可以看到,基于内存的用户信息存储方案实现也比较简单,但同样缺乏灵活性,因为用户信息是写死在代码里的。所以,我们接下来就要引出另一种更为常见的用户信息存储方案——数据库存储。</p>
|
||||
|
||||
<h4>使用基于数据库的用户信息存储方案</h4>
|
||||
|
||||
<p>既然是将用户信息存储在数据库中,势必需要<strong>创建表结构</strong>。我们可以在 Spring Security 的源文件(org/springframework/security/core/userdetails/jdbc/users.ddl)中找到对应的 SQL 语句,如下所示:</p>
|
||||
|
||||
<pre><code>create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
create unique index ix_auth_username on authorities (username,authority);
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>一旦我们在自己的数据库中创建了这两张表,并添加了相应的数据,就可以直接通过注入一个 DataSource 对象进行用户数据的查询,如下所示:</p>
|
||||
|
||||
<pre><code>@Autowired
|
||||
|
||||
|
||||
|
||||
DataSource dataSource;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
|
||||
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
auth.jdbcAuthentication().dataSource(dataSource)
|
||||
|
||||
|
||||
|
||||
.usersByUsernameQuery("select username, password, enabled from Users " + "where username=?")
|
||||
|
||||
|
||||
|
||||
.authoritiesByUsernameQuery("select username, authority from UserAuthorities " + "where username=?")
|
||||
|
||||
|
||||
|
||||
.passwordEncoder(new BCryptPasswordEncoder());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>这里使用了 AuthenticationManagerBuilder 的 jdbcAuthentication 方法来配置数据库认证方式,内部则使用了 JdbcUserDetailsManager 这个工具类。在该类中,就定义了各种用于数据库查询的 SQL 语句,以及使用 JdbcTemplate 完成数据库访问的具体实现方法。</p>
|
||||
|
||||
<p>请你注意,这里我们用到了一个<strong>passwordEncoder() 方法</strong>,这是 Spring Security 中提供的一个<strong>密码加解密器</strong>,我们会在“密码安全:Spring Security 中包含哪些加解密技术?”一讲中进行详细的讨论。</p>
|
||||
|
||||
<h3>小结与预告</h3>
|
||||
|
||||
<p>这一讲我们详细介绍了如何使用 Spring Security 构建用户认证体系的系统方法。在 Spring Security 中,认证相关的功能都是可以通过配置体系进行定制化开发和管理的。通过简单的配置方法,我们可以组合使用 HTTP 基础认证和表单登录认证,也可以分别基于内存以及基于数据库方案来存储用户信息,这些功能都是 Spring Security 内置的。</p>
|
||||
|
||||
<p>本讲内容总结如下:</p>
|
||||
|
||||
<p><img src="assets/CioPOWC5_g-AP68cAACSqF1r51g526.png" alt="Drawing 2.png" /></p>
|
||||
|
||||
<p>最后我想给你留一道思考题:你知道在 Spring Security 中有哪几种存储用户信息的实现方案吗?欢迎在留言区和我分享你的想法。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/03 认证体系:如何深入理解 Spring Security 用户认证机制?.md">下一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997537be703d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||||
|
||||
<script>
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
|
||||
|
||||
function gtag() {
|
||||
|
||||
dataLayer.push(arguments);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
|
||||
var path = window.location.pathname
|
||||
|
||||
var cookie = getCookie("lastPath");
|
||||
|
||||
console.log(path)
|
||||
|
||||
if (path.replace("/", "") === "") {
|
||||
|
||||
if (cookie.replace("/", "") !== "") {
|
||||
|
||||
console.log(cookie)
|
||||
|
||||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
setCookie("lastPath", path)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
|
||||
var d = new Date();
|
||||
|
||||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||||
|
||||
var expires = "expires=" + d.toGMTString();
|
||||
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
|
||||
var name = cname + "=";
|
||||
|
||||
var ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
|
||||
var c = ca[i].trim();
|
||||
|
||||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
1366
专栏/Spring Security 详解与实操/04 密码安全:Spring Security 中包含哪些加解密技术?.md.html
Normal file
1222
专栏/Spring Security 详解与实操/05 访问授权:如何对请求的安全访问过程进行有效配置?.md.html
Normal file
1340
专栏/Spring Security 详解与实操/06 权限管理:如何剖析 Spring Security 的授权原理?.md.html
Normal file
1472
专栏/Spring Security 详解与实操/09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md.html
Normal file
1054
专栏/Spring Security 详解与实操/10 全局方法:如何确保方法级别的安全访问?.md.html
Normal file
760
专栏/Spring Security 详解与实操/12 开放协议:OAuth2 协议解决的是什么问题?.md.html
Normal file
@@ -0,0 +1,760 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="icon" href="/static/favicon.png">
|
||||
|
||||
<title>12 开放协议:OAuth2 协议解决的是什么问题?.md</title>
|
||||
|
||||
<!-- Spectre.css framework -->
|
||||
|
||||
<link rel="stylesheet" href="/static/index.css">
|
||||
|
||||
<!-- theme css & js -->
|
||||
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div class="book-container">
|
||||
|
||||
<div class="book-sidebar">
|
||||
|
||||
<div class="book-brand">
|
||||
|
||||
<a href="/">
|
||||
|
||||
<img src="/static/favicon.png">
|
||||
|
||||
<span>技术文章摘抄</span>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-menu uncollapsible">
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="../">上一级</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md">00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">01 顶级框架:Spring Security 是一款什么样的安全性框架?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/02 用户认证:如何使用 Spring Security 构建用户认证体系?.md">02 用户认证:如何使用 Spring Security 构建用户认证体系?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/03 认证体系:如何深入理解 Spring Security 用户认证机制?.md">03 认证体系:如何深入理解 Spring Security 用户认证机制?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/04 密码安全:Spring Security 中包含哪些加解密技术?.md">04 密码安全:Spring Security 中包含哪些加解密技术?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/05 访问授权:如何对请求的安全访问过程进行有效配置?.md">05 访问授权:如何对请求的安全访问过程进行有效配置?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/06 权限管理:如何剖析 Spring Security 的授权原理?.md">06 权限管理:如何剖析 Spring Security 的授权原理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md">07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md">08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md">09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/10 全局方法:如何确保方法级别的安全访问?.md">10 全局方法:如何确保方法级别的安全访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md">11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/Spring Security 详解与实操/12 开放协议:OAuth2 协议解决的是什么问题?.md">12 开放协议:OAuth2 协议解决的是什么问题?.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md">13 授权体系:如何构建 OAuth2 授权服务器?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/14 资源保护:如何基于 OAuth2 协议配置授权过程?.md">14 资源保护:如何基于 OAuth2 协议配置授权过程?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/15 令牌扩展:如何使用 JWT 实现定制化 Token?.md">15 令牌扩展:如何使用 JWT 实现定制化 Token?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md">16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md">17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md">18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/19 测试驱动:如何基于 Spring Security 测试系统安全性?.md">19 测试驱动:如何基于 Spring Security 测试系统安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/20 结束语 以终为始,Spring Security 的学习总结.md">20 结束语 以终为始,Spring Security 的学习总结.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function add_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function remove_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sidebar_toggle() {
|
||||
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let content = document.querySelector('.off-canvas-content')
|
||||
|
||||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||||
|
||||
sidebar_toggle.classList.remove('extend')
|
||||
|
||||
sidebar.classList.remove('hide')
|
||||
|
||||
content.classList.remove('extend')
|
||||
|
||||
} else { // hide
|
||||
|
||||
sidebar_toggle.classList.add('extend')
|
||||
|
||||
sidebar.classList.add('hide')
|
||||
|
||||
content.classList.add('extend')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.add('show')
|
||||
|
||||
overlay.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
function hide_canvas() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.remove('show')
|
||||
|
||||
overlay.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="off-canvas-content">
|
||||
|
||||
<div class="columns">
|
||||
|
||||
<div class="column col-12 col-lg-12">
|
||||
|
||||
<div class="book-navbar">
|
||||
|
||||
<!-- For Responsive Layout -->
|
||||
|
||||
<header class="navbar">
|
||||
|
||||
<section class="navbar-section">
|
||||
|
||||
<a onclick="open_sidebar()">
|
||||
|
||||
<i class="icon icon-menu"></i>
|
||||
|
||||
</a>
|
||||
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
overflow-y: hidden;">
|
||||
|
||||
<div class="book-post">
|
||||
|
||||
<p id="tip" align="center"></p>
|
||||
|
||||
<div><h1>12 开放协议:OAuth2 协议解决的是什么问题?</h1>
|
||||
|
||||
<p>从今天开始,我们针对安全性的讨论将从单体服务上升到微服务架构。对于微服务架构而言,安全性设计的最核心考虑点还是<strong>认证和授权</strong>。由于一个微服务系统中各服务之间存在相互调用的关系,因此针对每一个服务,我们既需要考虑来自客户端的请求,同时也要考虑可能来自另一个服务的请求。安全访问控制就面临着从客户端请求到服务、从服务到服务的多种授权场景。因此,我们需要引入专门用于处理分布式环境下的授权体系,OAuth2 协议就是应对这种应用场景的有效解决方案。</p>
|
||||
|
||||
<h3>OAuth2 协议详解</h3>
|
||||
|
||||
<p>OAuth 是 Open Authorization 的简称,<strong>该协议解决的是授权问题</strong>而不是认证问题,目前普遍被采用的是 OAuth 2.0 版协议。OAuth2 是一个相对复杂的协议,对涉及的角色和授权模式给出了明确的定义,我们继续往下看。</p>
|
||||
|
||||
<h4>OAuth2 协议的应用场景</h4>
|
||||
|
||||
<p>在常见的电商系统中,通常会存在类似工单处理的系统,而工单的生成在使用用户基本信息的同时,势必也依赖于用户的订单记录等数据。为了降低开发成本,假设我们的整个商品订单模块并不是自己研发的,而是集成了外部的订单管理平台,此时为了生成工单记录,就必须让工单系统读取用户在订单管理平台上的订单记录。</p>
|
||||
|
||||
<p>在这个场景中,难点在于<strong>只有得到用户的授权</strong>,才能同意工单系统读取用户在订单管理平台上的订单记录。那么问题就来了,工单系统如何获得用户的授权呢?一般我们想到的方法是用户将自己在订单管理平台上的用户名和密码告诉工单系统,然后工单系统通过用户名和密码登录到订单管理平台并读取用户的订单记录,整个过程如下图所示:</p>
|
||||
|
||||
<p><img src="assets/Cgp9HWDewCGALXXGAABc1HLmoQs176.png" alt="Drawing 0.png" /></p>
|
||||
|
||||
<p>案例系统中用户认证和授权交互示意图</p>
|
||||
|
||||
<p>上图中的方案虽然可行,但显然存在几个严重的缺点:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>工单系统为了开展后续的服务,会保存用户在订单管理平台上的密码,这样很不安全;如果用户密码不小心被泄露了,就会导致订单管理平台上的用户数据发生泄露;</li>
|
||||
|
||||
<li>工单系统拥有了获取用户存储在订单管理平台上所有资料的权限,用户无法限制工单系统获得授权的范围和有效期;</li>
|
||||
|
||||
<li>如果用户修改了订单管理平台的密码,那么工单系统就无法正常访问订单管理平台了,这会导致业务中断,但我们又不能限制用户修改密码。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>既然这个方案存在如此多的问题,那么有没有更好的办法呢?答案是肯定的,<strong>OAuth2 协议</strong>的诞生就是为了解决这些问题。</p>
|
||||
|
||||
<p>首先,针对密码的安全性,在 OAuth2 协议中,密码还是由用户自己保管,避免了敏感信息的泄露;其次,OAuth2 协议中提供的授权具有明确的应用范围和有效期,用户可以根据需要限制工单系统所获取授权信息的作用效果;最后,如果用户对自己的密码等身份凭证信息进行了修改,只需通过 OAuth2 协议重新进行一次授权即可,不会影响到相关联的其他第三方应用程序。</p>
|
||||
|
||||
<p><img src="assets/CioPOWDewCqACykjAABqSG9fZc8154.png" alt="Drawing 1.png" /></p>
|
||||
|
||||
<p>传统认证授权机制与 OAuth2 协议的对比图</p>
|
||||
|
||||
<h4>OAuth2 协议的角色</h4>
|
||||
|
||||
<p>OAuth2 协议之所有能够具备这些优势,一个主要的原因在于它把整个系统涉及的各个角色及其职责做了很好地划分。OAuth2 协议中定义了四个核心的角色:<strong>资源、客户端、授权服务器和资源服务器</strong>。</p>
|
||||
|
||||
<p><img src="assets/CioPOWDewDCAIiUOAAB24Bx2cBU249.png" alt="Drawing 2.png" /></p>
|
||||
|
||||
<p>OAuth2 协议中的角色定义</p>
|
||||
|
||||
<p>我们可以把 OAuth2 中的角色与现实中的应用场景对应起来。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>OAuth2 协议中把需要访问的接口或服务统称为资源(Resource),每个资源都有一个拥有者(Resource Owner),也就是案例中的用户。</li>
|
||||
|
||||
<li>案例的工单系统代表的是一种第三方应用程序(Third-party Application),通常被称为客户端(Client)。</li>
|
||||
|
||||
<li>与客户端相对应的,OAuth2 协议中还存在一个服务提供商,案例中的订单管理平台就扮演了这个角色。服务提供商拥有一个<strong>资源服务器</strong>(Resource Server)和一个<strong>授权服务器</strong>(Authorization Server),其中资源服务器存放着用户资源,案例中的订单记录就是一种用户资源;而授权服务器的作用就是完成针对用户的授权流程,并最终颁发一个令牌,也就是我们所说的 Token。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>OAuth2 协议的 Token</h4>
|
||||
|
||||
<p>看到这里,你可能会提问,所谓的访问令牌是什么?令牌是 OAuth2 协议中非常重要的一个概念,本质上也是一种<strong>代表用户身份的授权凭证</strong>,但与普通的用户名和密码信息不同,<strong>令牌具有针对资源的访问权限范围和有效期</strong>。如下所示就是一种常见的令牌信息:</p>
|
||||
|
||||
<pre><code>{
|
||||
|
||||
|
||||
|
||||
"access_token": "0efa61be-32ab-4351-9dga-8ab668ababae",
|
||||
|
||||
|
||||
|
||||
"token_type": "bearer",
|
||||
|
||||
|
||||
|
||||
"refresh_token": "738c42f6-79a6-457d-8d5a-f9eab0c7cc5e",
|
||||
|
||||
|
||||
|
||||
"expires_in": 43199,
|
||||
|
||||
|
||||
|
||||
"scope": "webclient"
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
</code></pre>
|
||||
|
||||
<p>上述令牌信息中的各个字段都很重要,我们展开分析。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>access_token:代表 OAuth2 的令牌,当访问每个受保护的资源时,用户都需要携带这个令牌以便进行验证。</li>
|
||||
|
||||
<li>token_type:代表令牌类型,OAuth2 协议中有多种可选的令牌类型,包括 Bearer 类型、MAC 类型等,这里指定的 Bearer 类型是最常见的一种类型。</li>
|
||||
|
||||
<li>expires_in:用于指定 access_token 的有效时间,当超过这个有效时间,access_token 将会自动失效。</li>
|
||||
|
||||
<li>refresh_token:其作用在于当 access_token 过期后,重新下发一个新的 access_token。</li>
|
||||
|
||||
<li>scope:指定了可访问的权限范围,这里指定的是访问 Web 资源的“webclient”。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>现在我们已经介绍完令牌,你可能会好奇这样一个令牌究竟有什么用?接下来,我们就来看如何使用令牌完成基于 OAuth2 协议的授权工作流程。整个流程如下图所示:
|
||||
|
||||
<img src="assets/CioPOWDewDyAFnrhAABs_ltusFE367.png" alt="Drawing 3.png" /></p>
|
||||
|
||||
<p>基于 OAuth2 协议的授权工作流程图</p>
|
||||
|
||||
<p>我们可以把上述流程进一步展开梳理。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>首先,客户端向用户请求授权,请求中一般包含资源的访问路径、对资源的操作类型等信息。如果用户同意授权,就会将这个授权返回给客户端。</li>
|
||||
|
||||
<li>现在,客户端已经获取了用户的授权信息,可以向授权服务器请求访问令牌。</li>
|
||||
|
||||
<li>接下来,授权服务器向客户端发放访问令牌,这样客户端就能携带访问令牌访问资源服务器上的资源。</li>
|
||||
|
||||
<li>最后,资源服务器获取访问令牌后会验证令牌的有效性和过期时间,并向客户端开放其需要访问的资源。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>OAuth2 协议的授权模式</h4>
|
||||
|
||||
<p>在整个工作流程中,最为关键的是第二步,即获取用户的有效授权。那么如何获取用户授权呢?在 OAuth 2.0 中,定义了四种授权方式,即<strong>授权码模式(Authorization Code)、简化模式(Implicit)、密码模式(Password Credentials)和客户端模式(Client Credentials)</strong>。</p>
|
||||
|
||||
<p>我们先来看最具代表性的授权码模式。<strong>当用户同意授权后,授权服务器返回的只是一个授权码,而不是最终的访问令牌</strong>。在这种授权模式下,需要客户端携带授权码去换令牌,这就需要客户端自身具备与授权服务器进行直接交互的后台服务。</p>
|
||||
|
||||
<p><img src="assets/CioPOWDewEWARSNPAACJjFnruAQ327.png" alt="Drawing 4.png" /></p>
|
||||
|
||||
<p>授权码模式工作流程图</p>
|
||||
|
||||
<p>我们简单梳理一下授权码模式下的执行流程。</p>
|
||||
|
||||
<p>首先,用户在访问客户端时会被客户端导向授权服务器,此时用户可以选择是否给予客户端授权。一旦用户同意授权,授权服务器会调用客户端的后台服务提供的一个回调地址,并在调用过程中将一个授权码返回给客户端。客户端收到授权码后进一步向授权服务器申请令牌。最后,授权服务器核对授权码并向客户端发送访问令牌。</p>
|
||||
|
||||
<p>这里要注意的是,<strong>通过授权码向授权服务器申请令牌的过程是系统自动完成的,不需要用户的参与,用户需要做的就是在流程启动阶段同意授权</strong>。</p>
|
||||
|
||||
<p>接下来,我们再来看另一种比较常用的密码模式,其授权流程如下图所示:</p>
|
||||
|
||||
<p><img src="assets/Cgp9HWDewE2Ab2vhAABPDH7Zxjo001.png" alt="Drawing 5.png" /></p>
|
||||
|
||||
<p>密码模式工作流程图</p>
|
||||
|
||||
<p>可以看到,密码模式比较简单,也更加容易理解。用户要做的就是提供自己的用户名和密码,然后客户端会基于这些用户信息向授权服务器请求令牌。授权服务器成功执行用户认证操作后将会发放令牌。</p>
|
||||
|
||||
<p>OAuth2 中的客户端模式和简化模式因为在日常开发过程中应用得不是很多,这里就不详细介绍了。</p>
|
||||
|
||||
<p>你可能注意到了,虽然 OAuth2 协议解决的是授权问题,但它也应用到了认证的概念,这是因为只有验证了用户的身份凭证,我们才能完成对他的授权。所以说,<strong>OAuth2 实际上是一款技术体系比较复杂的协议,综合应用了信息摘要、签名认证等安全性手段,并需要提供令牌以及背后的公私钥管理等功能</strong>。</p>
|
||||
|
||||
<h3>OAuth2 协议与微服务架构</h3>
|
||||
|
||||
<p>对应到微服务系统中,<strong>服务提供者充当的角色就是资源服务器,而服务消费者就是客户端</strong>。所以每个服务本身既可以是客户端,也可以作为资源服务器,或者两者兼之。当客户端拿到 Token 之后,该 Token 就能在各个服务之间进行传递。如下图所示:
|
||||
|
||||
<img src="assets/CioPOWDewFaAKXrYAABQvvLUy-Q531.png" alt="Drawing 6.png" /></p>
|
||||
|
||||
<p>OAuth2 协议在服务访问场景中的应用</p>
|
||||
|
||||
<p>在整个 OAuth2 协议中,最关键的问题就是如何获取客户端授权。就目前主流的微服架构来说,当我们发起 HTTP 请求时,关注的是如何通过 HTTP 协议透明而高效地传递令牌,此时授权码模式下通过回调地址进行授权管理的方式就不是很实用,密码模式反而更加简洁高效。因此,在本专栏中,我们将使用<strong>密码模式</strong>作为 OAuth2 协议授权模式的默认实现方式。</p>
|
||||
|
||||
<h3>小结与预告</h3>
|
||||
|
||||
<p>今天我们进入微服务安全性领域展开了探讨,在这个领域中,认证和授权仍然是最基本的安全性控制手段。通过系统分析微服务架构中的认证和授权解决方案,我们引入了 OAuth2 协议,这也是微服务架构体系下主流的授权协议。我们对 OAuth2 协议具备的角色、授权模式以及与微服务架构之间的集成关系做了详细展开。</p>
|
||||
|
||||
<p>本讲内容总结如下:</p>
|
||||
|
||||
<p><img src="assets/CioPOWDewF2AG8SOAAGkQKcZcsU075.png" alt="Drawing 7.png" /></p>
|
||||
|
||||
<p>最后给你留一道思考题:你能描述 OAuth2 协议中所具备的四大角色以及四种授权模式吗?欢迎在留言区和我分享你的收获。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md">下一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709975501cb43d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||||
|
||||
<script>
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
|
||||
|
||||
function gtag() {
|
||||
|
||||
dataLayer.push(arguments);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
|
||||
var path = window.location.pathname
|
||||
|
||||
var cookie = getCookie("lastPath");
|
||||
|
||||
console.log(path)
|
||||
|
||||
if (path.replace("/", "") === "") {
|
||||
|
||||
if (cookie.replace("/", "") !== "") {
|
||||
|
||||
console.log(cookie)
|
||||
|
||||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
setCookie("lastPath", path)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
|
||||
var d = new Date();
|
||||
|
||||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||||
|
||||
var expires = "expires=" + d.toGMTString();
|
||||
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
|
||||
var name = cname + "=";
|
||||
|
||||
var ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
|
||||
var c = ca[i].trim();
|
||||
|
||||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
1020
专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md.html
Normal file
1054
专栏/Spring Security 详解与实操/14 资源保护:如何基于 OAuth2 协议配置授权过程?.md.html
Normal file
1326
专栏/Spring Security 详解与实操/15 令牌扩展:如何使用 JWT 实现定制化 Token?.md.html
Normal file
@@ -0,0 +1,648 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<link rel="icon" href="/static/favicon.png">
|
||||
|
||||
<title>20 结束语 以终为始,Spring Security 的学习总结.md</title>
|
||||
|
||||
<!-- Spectre.css framework -->
|
||||
|
||||
<link rel="stylesheet" href="/static/index.css">
|
||||
|
||||
<!-- theme css & js -->
|
||||
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div class="book-container">
|
||||
|
||||
<div class="book-sidebar">
|
||||
|
||||
<div class="book-brand">
|
||||
|
||||
<a href="/">
|
||||
|
||||
<img src="/static/favicon.png">
|
||||
|
||||
<span>技术文章摘抄</span>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-menu uncollapsible">
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li><a href="../">上一级</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<ul class="uncollapsible">
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md">00 开篇词 Spring Security,为你的应用安全与职业之路保驾护航.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/01 顶级框架:Spring Security 是一款什么样的安全性框架?.md">01 顶级框架:Spring Security 是一款什么样的安全性框架?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/02 用户认证:如何使用 Spring Security 构建用户认证体系?.md">02 用户认证:如何使用 Spring Security 构建用户认证体系?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/03 认证体系:如何深入理解 Spring Security 用户认证机制?.md">03 认证体系:如何深入理解 Spring Security 用户认证机制?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/04 密码安全:Spring Security 中包含哪些加解密技术?.md">04 密码安全:Spring Security 中包含哪些加解密技术?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/05 访问授权:如何对请求的安全访问过程进行有效配置?.md">05 访问授权:如何对请求的安全访问过程进行有效配置?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/06 权限管理:如何剖析 Spring Security 的授权原理?.md">06 权限管理:如何剖析 Spring Security 的授权原理?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md">07 案例实战:使用 Spring Security 基础功能保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md">08 管道过滤:如何基于 Spring Security 过滤器扩展安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md">09 攻击应对:如何实现 CSRF 保护和跨域 CORS?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/10 全局方法:如何确保方法级别的安全访问?.md">10 全局方法:如何确保方法级别的安全访问?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md">11 案例实战:使用 Spring Security 高级主题保护 Web 应用.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/12 开放协议:OAuth2 协议解决的是什么问题?.md">12 开放协议:OAuth2 协议解决的是什么问题?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/13 授权体系:如何构建 OAuth2 授权服务器?.md">13 授权体系:如何构建 OAuth2 授权服务器?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/14 资源保护:如何基于 OAuth2 协议配置授权过程?.md">14 资源保护:如何基于 OAuth2 协议配置授权过程?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/15 令牌扩展:如何使用 JWT 实现定制化 Token?.md">15 令牌扩展:如何使用 JWT 实现定制化 Token?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md">16 案例实战:基于 Spring Security 和 Spring Cloud 构建微服务安全架构.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md">17 案例实战:基于 Spring Security 和 OAuth2 实现单点登录.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md">18 技术趋势:如何为 Spring Security 添加响应式编程特性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/19 测试驱动:如何基于 Spring Security 测试系统安全性?.md">19 测试驱动:如何基于 Spring Security 测试系统安全性?.md.html</a>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
|
||||
|
||||
<a class="current-tab" href="/专栏/Spring Security 详解与实操/20 结束语 以终为始,Spring Security 的学习总结.md">20 结束语 以终为始,Spring Security 的学习总结.md.html</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function add_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function remove_inner() {
|
||||
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
|
||||
inner.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sidebar_toggle() {
|
||||
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let content = document.querySelector('.off-canvas-content')
|
||||
|
||||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||||
|
||||
sidebar_toggle.classList.remove('extend')
|
||||
|
||||
sidebar.classList.remove('hide')
|
||||
|
||||
content.classList.remove('extend')
|
||||
|
||||
} else { // hide
|
||||
|
||||
sidebar_toggle.classList.add('extend')
|
||||
|
||||
sidebar.classList.add('hide')
|
||||
|
||||
content.classList.add('extend')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.add('show')
|
||||
|
||||
overlay.classList.add('show')
|
||||
|
||||
}
|
||||
|
||||
function hide_canvas() {
|
||||
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
|
||||
sidebar.classList.remove('show')
|
||||
|
||||
overlay.classList.remove('show')
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="off-canvas-content">
|
||||
|
||||
<div class="columns">
|
||||
|
||||
<div class="column col-12 col-lg-12">
|
||||
|
||||
<div class="book-navbar">
|
||||
|
||||
<!-- For Responsive Layout -->
|
||||
|
||||
<header class="navbar">
|
||||
|
||||
<section class="navbar-section">
|
||||
|
||||
<a onclick="open_sidebar()">
|
||||
|
||||
<i class="icon icon-menu"></i>
|
||||
|
||||
</a>
|
||||
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
overflow-y: hidden;">
|
||||
|
||||
<div class="book-post">
|
||||
|
||||
<p id="tip" align="center"></p>
|
||||
|
||||
<div><h1>20 结束语 以终为始,Spring Security 的学习总结</h1>
|
||||
|
||||
<p>如今,虽然越来越多的开发人员开始意识到安全问题,但不幸的是,从应用程序设计和开发之初就充分考虑安全性问题并不是一种常见的做法。这种态度应该改变,每个参与软件系统开发的团队或个人都必须从一开始就学会考虑安全性。</p>
|
||||
|
||||
<p>Spring Security 是 Spring 家族的重要组成成员,也是业界领先的一款应用程序开发框架,提供了多项核心功能,帮忙我们构建完整的安全解决方案。本专栏作为针对 Spring Security 的一门系统化课程,在课程的最后,我想和你一起回顾和总结一下 Spring Security 核心功能,分享一些我在写作过程中的一些思考和心得。</p>
|
||||
|
||||
<h2>学习 Spring Security 的意义</h2>
|
||||
|
||||
<p>在 Java 领域中,Spring Security 是应用非常广泛的一个开发框架,也是 Spring 家族中历史比较悠久的一个框架。Spring Security 同样是 Spring Cloud 等综合性开发框架的底层基础框架之一,功能完备且强大,因此在 Spring 家族的其他框架中应用十分广泛。</p>
|
||||
|
||||
<p>另一方面,随着 Spring Boot 的大规模流行,Spring Security 也迎来了全新的发展时期。基于 Spring Boot 的自动配置原理,以往在传统 Spring 应用程序中集成和配置 Spring Security 框架的复杂过程将成为历史。在日常开发过程中,Spring Security 与 Spring Boot 等框架可以无缝集成,为构建完整的安全性解决方案提供保障。</p>
|
||||
|
||||
<p>Spring Security 内置了很多强大的功能,针对整个框架,我们可以从以下几个方面对其进行分析和学习:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Spring Security 的体系结构和基本组件,以及如何使用它来保护应用程序;</li>
|
||||
|
||||
<li>使用 Spring Security 进行身份验证和授权,以及如何将它们应用于面向生产的应用程序;</li>
|
||||
|
||||
<li>如何在应用程序的不同层中使用 Spring Security;</li>
|
||||
|
||||
<li>在应用程序中使用不同的安全配置方式和最佳实践;</li>
|
||||
|
||||
<li>将 Spring Security 用于响应式应用程序;</li>
|
||||
|
||||
<li>对安全性解决方案进行测试。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>但是,基于我自己的学习过程,以及从周围开发人员接收到的信息,我们在学习如何正确地使用 Spring Security 来保护应用程序免受常见漏洞的攻击时,或多或少都会遇到困难。当然,我们可以在网上找到有关 Spring Security 的所有细节。但是如果你希望在使用框架时花费最少的精力,那么就需要将相关知识按正确、合理的顺序放在一起进行学习,这通常需要大量的时间和经验。因此我设计这门课程的初衷,就是为了帮助你节省学习时间,提高学习效率。</p>
|
||||
|
||||
<p>此外,不完整的知识可能导致你设计并实现了难以维护的解决方案,甚至可能暴露安全漏洞。很多时候,当我们去 Review 这些问题时,会发现 Spring Security 的使用方式本身可能就是不合理的。而且,在许多情况下,主要原因还是开发人员对“如何使用 Spring Security”缺乏必要的了解。Spring Security 中的一些功能看上去比较简单,但用起来却经常会因为一些细小的配置,导致整个功能无法使用。即使发现了问题,也不太不容易找到原因。</p>
|
||||
|
||||
<p>因此,我决定设计一个系统化的专栏,帮助所有使用 Spring 框架的开发人员理解“如何正确使用 Spring Security”。这门课程应该是一个资源,以帮助开发人员逐步了解 Spring Security 框架,希望能为你带来价值,避免在应用程序中引入所有可能的安全漏洞。</p>
|
||||
|
||||
<p>下面我们再来回顾一下这门课程具体讲了哪些内容,这些内容又具备哪些特色呢?</p>
|
||||
|
||||
<h2>Spring Security 这门课有什么特色?</h2>
|
||||
|
||||
<p>在设计这门课程时,我关注的是将框架提供的各项功能进行合理的组织,并详细介绍它们的应用方式。<strong>课程组织上按照“基础功能篇→高级主题篇→OAuth2 与微服务篇 → 框架扩展篇”的主线来展开内容,呈递进关系。这是本课程的一大特色</strong>。我们将 Spring Security 的各项功能按照基础和高级等不同维度进行划分,由浅入深进行讲解。</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>基础功能篇中,我们介绍 Spring Security 的一些基础性功能,包括认证、授权和加密;</li>
|
||||
|
||||
<li>高级主题篇的功能面向特定需求,可以用于构建比较复杂的应用场景,包括过滤器、CSRF 保护、跨域 CORS,以及针对非 Web 应用程序的全局方法安全机制;</li>
|
||||
|
||||
<li>而 OAuth2 与微服务篇的内容关注微服务开发框架 Spring Cloud 与 Spring Security 之间的整合,我们对 OAuth2 协议和 JWT 进行了全面的展开,并使用这些技术体系构建了安全的微服务系统,以及单点登录系统。</li>
|
||||
|
||||
<li>最后,在框架扩展篇中,我们对 Spring Security 框架在应用上的一些扩展进行讨论,包括在 Spring Security 中引入全新的响应式编程技术,以及如何对应用程序安全性进行测试的系统方法。</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p><strong>课程的第二大特色在于案例驱动</strong>。整个专栏分别在基础功能篇、高级主题篇和微服务安全篇中结合本篇内容提供一个完整的案例,分别介绍 Spring Security 的基础认证授权功能、过滤器功能、基于 OAuth2 协议的单点登录和微服务访问授权体系的实战技巧。我们在案例中使用到的很多示例代码都可以直接使用在面向生产的应用程序中。</p>
|
||||
|
||||
<p><strong>第三个特色是技术创新</strong>。随着 Spring 5 的发布涌现出了响应式编程这种新型技术体系,新版本的 Spring Security 中也提供了对响应式编程的全面支持。本课程对响应式 Spring Security 也做了介绍,这部分内容应该在目前 Spring Security 相关资料中还是首创。</p>
|
||||
|
||||
<p><strong>第四个特色是深度和广度并重</strong>。从内容广度上,我们对 Spring Security 框架可以说是面面俱到,相关知识点娓娓道来。而在内容深度上,我们对框架最核心的认证和授权机制进行底层原理的分析,让你知其然更值其所以然。Spring Security 内置了很多可扩展性组件,通过对框架底层实现机制的理解和把握,可以帮助我们更好的实现扩展性。</p>
|
||||
|
||||
<h3>写在最后</h3>
|
||||
|
||||
<p>整个课程从平时的积累,到酝酿的启动再到上线经历了小半年的时间,伴随着这个过程,我把 Spring Security 的部分源代码系统地梳理了一遍,并对内部的设计思想和实现原理也做了一些提炼和总结。</p>
|
||||
|
||||
<p>总体而言,Spring Security 是一款代码质量非常高的开源框架,其中关于对用户认证和访问授权模型的抽象、内置的过滤器机制、全局方法安全机制、OAuth2 协议,以及响应式编程支持等诸多功能都给我留下了深刻的印象,使我受益良多。相信对于坚持学习到今天的你而言也是一样。</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div style="float: left">
|
||||
|
||||
<a href="/专栏/Spring Security 详解与实操/19 测试驱动:如何基于 Spring Security 测试系统安全性?.md">上一页</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709975821bc13d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||||
|
||||
<script>
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
|
||||
|
||||
function gtag() {
|
||||
|
||||
dataLayer.push(arguments);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
|
||||
var path = window.location.pathname
|
||||
|
||||
var cookie = getCookie("lastPath");
|
||||
|
||||
console.log(path)
|
||||
|
||||
if (path.replace("/", "") === "") {
|
||||
|
||||
if (cookie.replace("/", "") !== "") {
|
||||
|
||||
console.log(cookie)
|
||||
|
||||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
setCookie("lastPath", path)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
|
||||
var d = new Date();
|
||||
|
||||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||||
|
||||
var expires = "expires=" + d.toGMTString();
|
||||
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
|
||||
var name = cname + "=";
|
||||
|
||||
var ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
|
||||
var c = ca[i].trim();
|
||||
|
||||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 259 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 100 KiB |