Spring Security part13—使用OAuth2实现第三方登录
如何实践OAuth2
前言
从授权服务器的搭建、客户端的配置,到令牌的生成与验证,Spring Security
OAuth2
等组件已将协议细节封装为可复用的接口与注解,让开发者无需深入钻研协议底层,即可快速实现
“用微信登录自研系统”“用 GitHub 账号接入企业平台” 等场景。
正好,我的项目需要引入第三方登录,我们以 github
第三方登录为例子,来在 Spring Security 中使用 OAuth2
至于为什么不用微信,QQ,钉钉等这种国内更常用的,你可以去看看,申请这些东西有多复杂,不仅需要整体的备案,还需要上线服务,显然,对于第三方登录的实践,国内平台是不太合适的
有空我也想使下 OSU!的三方登录))
先给大家看一下基本的项目结构
image-20251115145142724
OAuth2的第三方登录的业务应该如何设计
那么,最重要的部分就是 OAuth2 业务如何设计
在老式的设计中,开发人员可能会在 User 表里加 google_id,...
Spring Security part12—了解OAuth2及其相关原理
OAuth2 协议
为什么需要 OAuth 协议
第三方应用需要用户在其他平台的资源,但用户不愿、也不应暴露核心账号密码,怎么办?
OAuth2.0
协议的核心价值是解决「第三方应用访问用户在另一平台的资源」时的安全与信任问题,避免账号密码泄露和权限滥用。
我们假设这样的一个场景
在 A 需访问用户在 B
的相片资源的场景中,直接用账号密码验证的方案存在致命问题:
用户信任危机:用户需向 A 暴露 B
的账号密码,无法确认 A 是否会窃取、滥用这些核心凭证。
平台信任危机:B 无法保证 A
仅将账号密码用于即时访问,可能被 A
存储、批量收集,导致用户信息泄露风险。
权限无边界:账号密码是最高权限凭证,一旦泄露,攻击者或恶意
A 可对用户在 B 的资源执行任意操作(如删改相片、篡改信息)。
凭证无法管控:账号密码一旦提供给
A,用户无法单方面收回权限,只能通过修改密码,操作繁琐且影响自身使用。
这种情况就是,直接提供凭证的方案的不可解决的安全与信任漏洞
image-20251115134720814
OAuth2.0...
Spring Security part11—同源策略及其CORS配置
安全与便利的天平往往难以把握——过于宽松的配置可能导致敏感数据泄露,过于严格的策略又会影响正常业务交互
什么是跨域?为什么会有跨域?
同源策略
其实这是一个很简单的东西,但是配置不好确实很容易让人头疼
CORS
的出现是为了解决浏览器的同源策略限制,我们先从同源策略说起
浏览器这样定义的同源:两个 URL
的协议、域名(主机名)、端口三者完全相同,才被视为
同源。
所以说,在开发前后端分离的项目的时候,由于前端项目和后端项目运行在不同的端口上,天然跨域产生了,配置不好的话经常会出现这种导致的500的问题,浏览器发起请求时,请求会发送出去,服务器也会处理并返回,但是浏览器会在门口把响应拦截下来并报错,不让前端
JS 代码拿到数据。
同源策略其实是浏览器的一种安全机制,防止恶意网站通过 JavaScript
获取另一个网站的敏感数据(比如 cookie、localStorage、API
返回的用户信息)。如果没有同源策略,黑客可以轻易伪造用户请求,窃取数据。
浏览器默认遵循同源策略。它规定:A 网站的 JavaScript
(如...
Spring Security part10—CSRF及其防御
认识CSRF攻击
小明的悲惨遭遇
例子来自美团的https://tech.meituan.com/2018/10/11/fe-security-csrf.html
这一天,小明同学百无聊赖地刷着Gmail邮件。大部分都是没营养的通知、验证码、聊天记录之类。但有一封邮件引起了小明的注意:
甩卖比特币,一个只要998!!
聪明的小明当然知道这种肯定是骗子,但还是抱着好奇的态度点了进去(请勿模仿)。果然,这只是一个什么都没有的空白页面,小明失望的关闭了页面。一切似乎什么都没有发生……
在这平静的外表之下,黑客的攻击已然得手。小明的Gmail中,被偷偷设置了一个过滤规则,这个规则使得所有的邮件都会被自动转发到hacker@hackermail.com。小明还在继续刷着邮件,殊不知他的邮件正在一封封地,如脱缰的野马一般地,持续不断地向着黑客的邮箱转发而去。
不久之后的一天,小明发现自己的域名已经被转让了。懵懂的小明以为是域名到期自己忘了续费,直到有一天,对方开出了
$650...
Spring Security part9—持久化Token实现RemberMe
Token持久化
Token 持久化实际上用到的最多的场景就是登录时候的记住我
这是 Spring Security 最经典、最常用的 Token
持久化场景。它允许用户在关闭浏览器后(会话失效),下次访问时无需重新登录,依然允许用户自动登录。
它的核心机制有两种形式,现在基本上用的最多的就是持久化令牌方案,因为它更安全,它通过在数据库中存储一个随机生成的“系列号(Series)”和“令牌(Token)”来验证身份。
工作原理:
用户登录时勾选“记住我”。
服务器生成一对随机数:Series(固定,代表该设备登录会话)和
Token(动态,每次自动登录都会更新)。
这两者与用户名、过期时间一起存入数据库,并以 Cookie
形式发给浏览器。
下次访问时,Spring Security 检查 Cookie,检查 Cookie 中的 Token
用于请求自动登录,如果 Series 匹配但 Token 不匹配,说明 Token
可能被盗用(重放攻击),系统会立即失效该用户的所有持久化令牌,则用户需要重新认证。
img
Spring...
Spring Security part8—基于request的授权下
继续接着上
授权管理实例
RBAC
在之前的设计中,我们严格按照了 RBAC 模型设计了三层关系
用户 User:多对多关联角色,也就是用户可以被赋予一个或多个角色
12345678910@Entity@Table(name = "users")...@ManyToMany(fetch = FetchType.LAZY)@JoinTable( name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))private Set<Role> roles = new HashSet<>();
角色 Role:承上启下,连接用户和权限
12345678910111213@Entity@Table(name =...
Vue Router part3—视图,重定向,路由传参和两种路由模式
两种路由模式
Vue Router 使用两种主要的方式来监听 URL 的变化:一种是基于浏览器的
history API,另一种是基于 hash
变化。这两种方式分别对应 history 模式和 hash
模式。
不管是 hash 模式还是 history
模式,核心目的都是在不刷新整个页面的前提下,实现前端路由的切换(单页应用
SPA 的核心需求),只是监听 URL 变化的方式和 URL 表现形式不同。
history 模式
在 history 模式下,Vue Router 利用 HTML5 的
history API
来实现路由的切换。(pushState、replaceState 和
popstate 事件)
这些 API
允许开发者在不刷新页面的情况下,修改浏览器的历史记录栈,并且 URL
中不再包含 # 符号
Vue Router 会监听浏览器的 popstate
事件(当用户点击浏览器的前进 / 后退按钮时触发),同时通过
pushState/replaceState 方法修改...
Vue3 part11—使用Axios库与后端进行对接
Axios 是什么
简介
Axios 是一个基于 Promise 的 HTTP
客户端,可以在浏览器和 Node.js 环境中使用。简化了前端发送 HTTP
请求的流程,提供了拦截器、自动解析 JSON 等强大功能。
它就是一个专门用来发送 HTTP 请求的工具库,相比浏览器原生的
XMLHttpRequest 或者 Fetch
API,它有更简洁的语法、更强大的功能(比如拦截器、请求取消、自动转换 JSON
数据等),因此成为了前后端分离项目中对接后端接口的首选工具。
它的核心特点:
支持 Promise API,便于异步操作的处理(async/await 语法)。
拦截请求和响应(比如请求前添加 token、响应后统一处理错误)。
自动转换请求和响应数据(比如自动将 JSON 字符串转为 JavaScript
对象)。
取消请求。
客户端支持防御 XSRF(跨站请求伪造)。
在前后端分离架构中,前端(如 Vue/React/Angular)通过 Axios...
Vue3 part10—新一代Vue状态管理Pinia
状态管理
什么是状态管理
在前端开发中,状态(State)
指的是应用中可变化的数据,比如用户信息、购物车列表、页面切换的标签页、表单输入值、全局主题配置等。
状态管理
则是对这些状态进行统一的创建、读取、修改、监听和共享
的一套规则和工具,核心目标是解决状态分散、流转混乱、难以维护的问题。
Vue 项目中,组件间共享数据的原生方式有哪些?
父子组件:props 向下传、emit
向上触发事件;
跨层级 / 兄弟组件:依赖 EventBus 或父组件中转;
全局数据:挂载到 Vue.prototype 或 window
上。
这些在项目规模扩大后会暴露致命问题
数据在多个组件间层层传递(“prop”),溯源和调试困难状态流转混乱:
状态修改不可控:任何组件都能随意修改全局数据,出现...
Vue Router part2—动态路由与路由匹配
嵌套路由
嵌套路由如何诞生
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL
的片段通常对应于特定的嵌套组件结构,例如:
image-20251206130056766
父路由组件中通过 <router-view>
承载子路由组件,路由路径按层级嵌套,最终实现 URL
与页面结构的一一对应。
在单页应用(SPA)中,很多页面并非 “一次性渲染”,而是由父容器
+ 子内容组成
电商后台:/dashboard(父)→
/dashboard/order(子:订单页)、/dashboard/user(子:用户页);
社交应用:/user/123(父:用户主页)→
/user/123/profile(子:个人资料)、/user/123/posts(子:用户动态)。
使用嵌套路由
在 routes 数组中,父路由通过...
动态规划之多重背包及其两种优化
问题描述
多重背包问题是经典的动态规划问题之一,是 01
背包和完全背包的泛化:
给定 n 种物品和一个容量为 m 的背包。第
i 种物品的重量为
wei[i],价值为
val[i],并且数量有限,最多只能取
s[i] 件。
目标是在不超过背包容量的前提下,选择物品装入背包,使得总价值最大。
与 01 背包的区别:01 背包中每种物品只有 1
件(s[i] = 1)。
与完全背包的区别:完全背包中每种物品有无限件(s[i] = ∞)。
image-20251130195244607
问题分析
在学习了前两种背包的基础上,多重背包的问题还是很清晰的
可以看出,多重背包是介于 01
背包和完全背包之间的一种更通用的模型:
当 s[i] = 1 时,退化为 01 背包
当 s[i] 足够大(大于
m/wei[i])时,退化为完全背包
那么,01 背包的状态转移 是这样的
12// 01背包:对于第i件物品,选或不选dp[i][j] = max(dp[i-1][j], dp[i-1][j-wei[i]] +...
Vue Router part1—认识Vue Router路由管理和其基本使用
SPA架构
什么是 SPA 架构
我们知道 SPA
是单页面的前端架构,但是,单页面应用(SPA)不是简单的只有一个HTML文件的形式,而是一种应用架构模式:
SPA = Single Page Application(单页应用)
整个应用只加载一次 HTML
文件,后续所有页面切换、数据交互都在这一个页面内完成,不重新加载整个页面。
SPA 就像一个「容器」,首次加载时把所有必需的 JS、CSS
都加载完成(或按需加载),之后用户操作时,只动态更新页面内的「部分内容」,URL
变化但浏览器不刷新。
SPA 架构做的前端有这样的核心特征
应用仅包含 1 个核心 HTML(比如
index.html),所有页面都是这个 HTML 内的「组件片段」;
无刷新切换,页面切换靠 JS
动态渲染组件(销毁旧组件、挂载新组件),浏览器地址栏 URL
可变化,但不会触发页面刷新(区别于传统网站的「跳转 = 刷新」);
数据驱动视图,页面内容更新依赖「数据变化」(比如
Vue 的响应式),而非重新加载 DOM;交互时只通过...
动态规划之完全背包
问题
给定 n 种物品和一个容量为 C 的背包。第
i 种物品的重量是 w[i],价值是
v[i]。
每种物品可以无限次使用(即可以选 0 个、1 个、2 个…
直到背包容量限制)。
问:将哪些物品装入背包,可使这些物品的总重量不超过背包容量,且总价值最大?
image-20251125192750189
问题分析
该问题与 0-1 背包非常像,但是确实是两者问题
0-1 背包:每种物品只能选0 个或 1 个
完全背包:每种物品可以选任意多个
所以,我们依旧可以这样定义状态变量
定义 f[i][j] 表示:考虑前 i
种物品,在背包容量为 j 时的最大价值。
那么接下来就要确定递推关系,那么对当前背包容量
j,依旧考虑的是第i件物品能否放入?或者说是否放入?
所以对于第 i 种物品,我们可以参考 0-1
背包的思路,有多种选择:
选 0 个:dp[i][j] = dp[i-1][j]
也就是当前背包容量 j < w[i],这个物品放不下
或者能放入,但是不合适,所以不放入
选 1...
















