Nextjs前后端交互
前后端交互使用Javascript。目前底层原生工具有
fetch和XMLHttpRequest。开发常用的axios是对fetch的封装。这两种通信方式都是HTTP/HTTPS协议,即无状态协议。
跨域问题:
Fetch 如果后端不支持CORS,前端是无法实现跨域访问的。
1 | const headers = new Headers(); |
前后端交互使用Javascript。目前底层原生工具有
fetch和XMLHttpRequest。开发常用的axios是对fetch的封装。这两种通信方式都是HTTP/HTTPS协议,即无状态协议。
跨域问题:
Fetch 如果后端不支持CORS,前端是无法实现跨域访问的。
1 | const headers = new Headers(); |
Git分为工作区,版本库(暂存区和提交历史)
工作区即项目目录,版本库为其中的
.git隐藏文件夹,其中index即为暂存区。
1 | git add <filename> |
1 | git switch <branchname> |
1 | git branch |
1 | git stash |
1 | git cherry-pick <commithash> |
1 | git log --graph --pretty=oneline --abbrev-commit |
| 场景 | 命令 | 说明 |
|---|---|---|
| 切换已有本地分支 | git switch dev |
快速切换 |
| 创建并切换新分支 | git switch -c dev |
不关联远程 |
| 基于远程创建并跟踪 | git switch --track -c dev origin/dev |
推荐用法 |
| 基于 tag 创建新分支 | git switch -c dev v1.0 |
基于 tag |
| 切换到 tag | git switch v1.0 |
游离 HEAD |
| 切换到 commit | git switch a1b2c3d |
游离 HEAD |
| 强制切换丢弃修改 | git switch -f dev |
小心使用 |
| 游离 HEAD(只查看) | git switch --detach origin/dev |
只看不提交 |
Git在切换分支时,如果工作区或暂存区存在数据并没有提交,Git会尝试将这些数据(未追踪的数据,修改但没加入暂存区的数据,加入暂存区的数据)转移到新分支的工作区和暂存区,如果合并过程中出现冲突,那么Git会阻止分支切换。
解决冲突的方案:
注意:一般来说,在切换分支前需要将工作区、暂存区清空,防止将一些不必要的修改带入到其他分支。
工作区中包含了未被追踪的数据和加入暂存区后又被修改的数据
暂存区中包含了上一次add时数据的状态
| 命令 | 作用 |
|---|---|
git restore <file> |
恢复指定文件到最新提交版本,覆盖工作区的改动(未提交的更改会丢失)。 |
git restore . |
恢复所有文件,丢弃全部工作区未提交的更改。 |
git restore --staged <file> |
取消暂存区的指定文件,保留工作区改动。 |
git restore --staged . |
取消所有已暂存的文件,保留工作区改动。 |
git restore --staged --worktree <file> |
同时恢复暂存区和工作区,彻底还原文件到最近一次提交版本。 |
git restore --source=<branch> --staged --worktree <file> |
从指定提交恢复文件,同时覆盖工作区和暂存区。 |
| 需求 | 命令 |
|---|---|
| 恢复工作区到仓库版本 | git restore <file> |
| 恢复工作区到暂存区版本 | git restore --source=: <file> |
| 恢复暂存区到仓库版本 | git restore --staged <file> |
| 功能 | 命令 |
|---|---|
| 保存修改 | git stash |
| 保存修改(含未跟踪) | git stash -u |
| 保存所有 | git stash -a |
| 查看列表 | git stash list |
| 查看修改 | git stash show -p stash@{0} |
| 应用最新 stash (但不删除) | git stash apply |
| 应用并删除 | git stash pop |
| 删除指定 stash | git stash drop stash@{0} |
| 清空 stash | git stash clear |
| 交互式 stash | git stash push -p |
| 新建分支恢复 stash | git stash branch new-branch stash@{0} |

| 操作 | 修改历史 | 生成新提交 | 是否安全 | 用途 |
|---|---|---|---|---|
git reset --soft <commit> |
✅ 是 | ❌ 否 | ❌ 否 | 回退到目标 commit,将当前commit保留到暂存区 |
git reset --mixed <commit> |
✅ 是 | ❌ 否 | ❌ 否 | 回退到目标 commit,将当前commit保留到工作区 |
git reset --hard <commit> |
✅ 是 | ❌ 否 | ❌ 否 | 回退到目标 commit,丢弃所有修改 |
git revert |
❌ 否 | ✅ 是 | ✅ 是 | 撤销当前 commit,生成一个新的节点,协作开发,不改历史 |
| 命令 | HEAD 指针移动 | 暂存区变化 | 工作区变化 | 场景总结 |
|---|---|---|---|---|
git reset --soft HEAD^ |
✅ 回退 | 保持回退前commit的内容 | ❌ 保留(工作区内容不变,保持回退前commit的内容) | 回退 commit,修改回到暂存区,适合继续提交 |
git reset --mixed HEAD^ |
✅ 回退 | 更改至目标commit的内容 | ❌ 保留(工作区内容不变,保持回退前commit的内容) | 回退 commit,修改回到工作区,适合继续编辑 |
git reset --hard HEAD^ |
✅ 回退 | 更改至目标commit的内容 | ✅ 恢复为目标commit的状态 | 回退 commit,直接丢弃修改,彻底干净 |
添加远程分支
| 功能 | 命令 |
|---|---|
| 添加远程 | git remote add origin <url> |
| 查看远程 | git remote -v |
| 修改远程地址 | git remote set-url origin <new-url> |
| 删除远程 | git remote remove origin |
push本地分支到远程分支并关联在一起
| 场景 | 是否需要手动指定 |
|---|---|
| 第一次推送主分支 | 需要执行 git push -u origin main |
| 后续推送主分支 | 直接 git push 即可 |
| 新建其他分支第一次推送 | 需要执行 git push -u origin 分支名 |
| 新建其他分支后续推送 | 直接 git push 即可 |
git fetch + git merge = git pull
Spring提供了JDBC(Java Database Connectivity),JPA (Java Persistence API),MyBatis。
JDBC(Java Database Connectivity) 是Java的标准数据库访问技术。它直接与数据库交互,允许Java应用程序执行SQL语句并与数据库进行通信。
1 | <dependency> |
JPA 是一个Java的持久化API,用于管理Java对象与数据库之间的映射。它提供了对象关系映射(ORM)的功能,允许开发者以面向对象的方式处理数据。
1 | <dependency> |
MyBatis 是一个支持普通SQL查询、存储过程和高级映射的持久层框架。它避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatus是半自动ORM:MyBatis不像JPA那样完全抽象掉SQL,它允许开发者写SQL语句,同时提供了映射文件来定义SQL语句与Java对象的映射关系。
就目前版本的mybatis中包含了spring-boot-starter-jdbc,直接使用JDBC的Datasource和数据库连接池。
1 | <dependency> |
Spring Session 是 Spring 框架的一个项目,旨在提供会话管理的解决方案。它可以与各种后端存储(如内存、数据库、Redis 等)集成。
目标是实现前后端分离(BFF)的OAuth2登陆流程。需要详细分析Spring OAuth2 Client认证流程,才能对后端的认证进行修改满足前后端分离的要求。
1 | <dependency> |
1 | spring: |
SecurityFilterChain1 | @Configuration |
到目前为止,默认的Spring security OAuth2已经配置完成了。启动项目并访问localhost:8080 会自动跳转到登陆窗口 localhost:8080/login

我们发现登陆的API为localhost:8080/oauth2/authorization/github,这也是我们前端需要跳转的页面API地址。
当访问
localhost:8080跳转到localhost:8080/login是由于在 SecurityFilterChain 中所有的页面都需要认证,当访问\时,被AuthorizationFilter抛出异常,并被ExceptionTranslationFilter中的LoginUrlAuthenticationEntryPoint处理 并重定向到localhost:8080/login。 访问localhost:8080/login的时候,会被DefaultLoginPageGeneratingFilter拦截并返回Login页面。该过程在前端的核心认证过程中用不到,就不展开了。但在规范API的时候可以自定义AuthenticationEntryPoint,将错误API统一返回为JSON格式,而不是返回
/login页面。
localhost:8080/oauth2/authorization/github当浏览器访问该地址后会自动跳转到Github的Login页面,此时后端日志停在了OAuth2AuthorizationRequestRedirectFilter (7/16),也就是请求被该类处理了并发起了Redirecting to https://github.com/login/oauth/authorize?的页面跳转。

通过前端调试Network可以看到,当访问localhost:8080/oauth2/authorization/github后端会发送重定向请求,将Response的Location设置为https://github.com/login/oauth/authorize?并进行跳转。

访问https://github.com/login/oauth/authorize?后再次跳转到https://github.com/login?

登陆完成后,Github OAuth Server会重定向到https://github.com/login/oauth/authorize? 然后Github会根据之前在Spring OAuth中设置的Authorization callback URL并跳转到该地址。下图是本文在Github中的设置:
即后续跳转到https://localhost:8443/login/oauth2/code/github?code=3fd7543e7cf92aab5d53&state=1NiT_GBSj2pOolkHnTHpSft8V9x6uOMTsvYveTHM6GE%3D
此时后端日志中可以看到过滤链停在了OAuth2LoginAuthenticationFilter (8/16),该类会接受code和state参数并进行处理。
在该类结束的时候会重定向到/页面。
1 | 2025-01-03T23:55:11.589+08:00 DEBUG 46026 --- [nio-8080-exec-4] o.s.s.web.DefaultRedirectStrategy : Redirecting to / |
进入到类OAuth2LoginAuthenticationFilter,发现请求会进入到attemptAuthentication()方法中。该类主要完成以下步骤:
redirect_uri的请求会被过滤器OAuth2LoginAuthenticationFilter拦截处理{token-uri}/login/oauth/access_token请求并获取tokenOAuth2AuthenticationToken中并返回该值SecurityContextHolder 存储 OAuth2AuthenticationToken经过上述分析,前端只需要访问localhost:8080/oauth2/authorization/github即可开始认证过程。只要设置了http.oauth2Login(),SpringSecurity 即会保证/oauth2/authorization/{client}认证端点对外开放。
1 | @Configuration |
但前端调用该接口是需要添加参数,告诉后端处理完成之后需要跳转回哪里。所以目前需要在Spring Security OAuth中完成两项工作:
localhost:8080/oauth2/authorization/github?origin_url=xxx的拦截器,将其中的参数origin_url取出存到Session中。origin_url并将用户从Github拿到的基本信息添加到该URL中进行跳转,返回到原来的页面。通过上一章节的页面调试,可以看到针对localhost:8080/oauth2/authorization/github?origin_url=xxx的处理是过滤连中OAuth2AuthorizationRequestRedirectFilter处理的。
当Github根据提前设置的Authorization callback URL将code返回时,是过滤链中OAuth2LoginAuthenticationFilter处理的,处理过程包括1.接收Github传回的code。2.使用该code向Github发起POST请求来换取Token,最后接收到Token。 3.根据Token来获取Github用户基本信息。
处理完成后跳转回最初的访问地址(此处需要更改为 从Session中拿出origin_url并跳转)。
/oauth2/authorization/github?origin_url=xxx的参数本文通过重写
AuthorizationRequestRepository将origin_url写入Session。也可以通过重写
AuthorizationRequestResolver来解析origin_url,在解析选择哪种数据源的同时将origin_url封装到OAuth2AuthorizationRequest类里,后续AuthorizationRequestRepository会将该类直接放到Session中,最后需要通过获取Session中的OAuth2AuthorizationRequest对象,再从里面获取origin_url。读者可以自行尝试。
经过调试OAuth2AuthorizationRequestRedirectFilter,发现只要是authorization_code认证方式的OAuth请求,都会经过HttpSessionOAuth2AuthorizationRequestRepository的处理。
1 | // OAuth2AuthorizationRequestRedirectFilter.java |
其中的this.authorizationRequestRepository默认是HttpSessionOAuth2AuthorizationRequestRepository。它的源码显示其专门在Session中存储请求信息:
1 | // HttpSessionOAuth2AuthorizationRequestRepository.java |
所以,可以自定义该类来解析Request请求中的参数origin_url。查看Spring Security OAuth2 Client文档可以配置自定义的AuthorizationRequestRepository。
1 | @Configuration |
目的只是为了增加一个解析URL参数,所以核心代码仍然使用它默认的HttpSessionOAuth2AuthorizationRequestRepository,只在saveAuthorizationRequest()方法中加入对origin_url的解析。
定义类ParseUrlHttpSessionOAuth2AuthorizationRequestRepository,将HttpSessionOAuth2AuthorizationRequestRepository的内容完全复制过来,将Class名字更改,并在saveAuthorizationRequest()中加入三行代码:
1 | // set url params to session |
解析URL获取origin_url,并将URL存入到Session中。
1 | // ParseUrlHttpSessionOAuth2AuthorizationRequestRepository.java |
origin_url)当OAuth2处理成功后,需要从Session中拿出origin_url并重定向。
Spring Security OAuth2文档显示可以自定义authenticationSuccessHandler,即http.oauth2Login.successHandler()来设置自定义成功后处理逻辑。
1 | http.oauth2Login(oauth2 -> oauth2 |
最终OAuth2的配置如下:
1 | @Configuration |
1 | 2025-02-21T23:45:30.306+08:00 TRACE 156775 --- [nio-8443-exec-2] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2LoginAuthenticationProvider (1/3) |
1 | 2025-02-19T13:38:16.582+08:00 INFO 28513 --- [on(4)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' |
Spring Security提供两种方式,一种是基于Servlet模型,一种是基于Reactive模型
1 | flowchart LR |
向Servlet中加入Filter有两种方式:
web.xml里面WebFilter()添加1 | @WebFilter("/*") |
SpringBoot会将DelegatingFilterProxy注册到FilterChain里,请求在进入Servlet之前会通过DelegatingFilterProxy这个Filter,DelegatingFilterProxy在初始化的时候会通过WebApplicationContext加载名称为springSecurityFilterChain的bean,该bean类中doFilterDelegate中包含了FilterChainProxy。
通过将DelegatingFilterProxy加入到Servlet的Filter中,并且DelegatingFilterProxy中通过调用WebApplicationContext创建FilterChainProxy,二者将Servlet和Spring框架结合起来。
1 | const generateSecureRandomString = (length: number): string => { |