前后端交互使用Javascript。目前底层原生工具有fetchXMLHttpRequest。开发常用的axios是对fetch的封装。这两种通信方式都是HTTP/HTTPS协议,即无状态协议。

跨域问题:

Fetch 如果后端不支持CORS,前端是无法实现跨域访问的。

1
2
3
4
5
6
7
8
9
10
11
12
const headers = new Headers();
headers.set("Content-Type", "application/json");
const request = new Request(process.env.NEXT_PUBLIC_LOGIN_API!, {
method: "POST",
mode: "cors",
headers: headers,
});
const response = await fetch(request);
if (!response.ok) {
throw new Error(`Response status ${response.status}`);
}
const json = await response.json();

Git 常用命令

Git分为工作区,版本库(暂存区和提交历史)

工作区即项目目录,版本库为其中的.git隐藏文件夹,其中index即为暂存区。

1
2
3
git add <filename>
git commit -m "message"
git commit -a -m "message"
1
2
3
4
git switch <branchname>
git swtich -c <branchname>
git checkout <branchname>
git checkout -b <branchname>
1
2
3
git branch
git branch -d <branchname>
git branch -D <branchname> #force delete
1
2
3
git stash
git stash pop
git stash supply <stashid>
1
2
3
4
git cherry-pick <commithash>
git cherry-pick <startcomit> <endcommit> #(startcommit, endcommit]
git cherry-pick --continue #deal with conflict
git cherry-pick --abort #abort cherry pick
1
git log --graph --pretty=oneline --abbrev-commit

Switch

场景 命令 说明
切换已有本地分支 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会阻止分支切换。

解决冲突的方案:

  • 在切换分支之前进行Commit操作,将所有工作区,暂存区的数据都提交,放入到版本库中
  • 在切换分支之前使用Stash将工作区,暂存区的数据都保留镜像,等到切换回该分支后再执行pop将工作区,暂存区恢复。
  • 直接放弃在工作区,暂存区的修改。

注意:一般来说,在切换分支前需要将工作区、暂存区清空,防止将一些不必要的修改带入到其他分支。

工作区中包含了未被追踪的数据加入暂存区后又被修改的数据

暂存区中包含了上一次add时数据的状态

Restore

命令 作用
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>

Stash

功能 命令
保存修改 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}

Merge / Rebase

image-20250629210400266

Reset / Revert

操作 修改历史 生成新提交 是否安全 用途
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,直接丢弃修改,彻底干净

Push

添加远程分支

功能 命令
添加远程 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 即可

Fetch / Pull

git fetch + git merge = git pull

Spring 持久化框架

Spring提供了JDBC(Java Database Connectivity)JPA (Java Persistence API)MyBatis

JDBC(Java Database Connectivity) 是Java的标准数据库访问技术。它直接与数据库交互,允许Java应用程序执行SQL语句并与数据库进行通信。

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.4.2</version>
</dependency>

JPA 是一个Java的持久化API,用于管理Java对象与数据库之间的映射。它提供了对象关系映射(ORM)的功能,允许开发者以面向对象的方式处理数据。

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.4.2</version>
</dependency>

MyBatis 是一个支持普通SQL查询、存储过程和高级映射的持久层框架。它避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatus是半自动ORM:MyBatis不像JPA那样完全抽象掉SQL,它允许开发者写SQL语句,同时提供了映射文件来定义SQL语句与Java对象的映射关系。

就目前版本的mybatis中包含了spring-boot-starter-jdbc,直接使用JDBC的Datasource和数据库连接池。

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>

SpringBoot 数据库连接池

Spring IOC 循环依赖

Spring Session

Spring Session 是 Spring 框架的一个项目,旨在提供会话管理的解决方案。它可以与各种后端存储(如内存、数据库、Redis 等)集成。

  • SecurityContextHolderFilter中的SecurityContextRepository,存储用户信息上下文,其中一种实现方式就是HttpSessionSecurityContextRepository,而且该方式是默认的实现方式。
  • AbstractAuthenticationProcessingFilter中,当用户登录成功,也是会调用this.securityContextRepository.saveContext(context, request, response);存储上下文到session中。

目标是实现前后端分离(BFF)的OAuth2登陆流程。需要详细分析Spring OAuth2 Client认证流程,才能对后端的认证进行修改满足前后端分离的要求。

Spring OAuth 配置

1. 依赖(Dependency)

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. OAuth2 Server Configuration (application.yml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spring:
security:
oauth2:
client:
registration:
github:
client-id: Ov23lisFyLM8ev2mgeX1
client-secret: da386df22b42d1374216995f47c3d05d441b3da6
google:
client-id: google-client-id
client-secret: google-client-secret
facebook:
client-id: okta-client-id
client-secret: okta-client-secret

#为了更好的了解后端认证流程,将TRACE log打印出来
logging:
level:
org:
springframework:
security: TRACE

3. Config SecurityFilterChain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated())
.oauth2Login(Customizer.withDefaults());

return http.build();
}

}

到目前为止,默认的Spring security OAuth2已经配置完成了。启动项目并访问localhost:8080 会自动跳转到登陆窗口 localhost:8080/login

login_window

我们发现登陆的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页面。

1. 访问localhost:8080/oauth2/authorization/github

当浏览器访问该地址后会自动跳转到Github的Login页面,此时后端日志停在了OAuth2AuthorizationRequestRedirectFilter (7/16),也就是请求被该类处理了并发起了Redirecting to https://github.com/login/oauth/authorize?的页面跳转。

image-20250103202726604

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

image-20250103203153119

访问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中的设置:

image-20250219171534862

即后续跳转到https://localhost:8443/login/oauth2/code/github?code=3fd7543e7cf92aab5d53&state=1NiT_GBSj2pOolkHnTHpSft8V9x6uOMTsvYveTHM6GE%3D

此时后端日志中可以看到过滤链停在了OAuth2LoginAuthenticationFilter (8/16),该类会接受code和state参数并进行处理。

image-20250104112126452

在该类结束的时候会重定向到/页面。

1
2
2025-01-03T23:55:11.589+08:00 DEBUG 46026 --- [nio-8080-exec-4] o.s.s.web.DefaultRedirectStrategy        : Redirecting to /
2025-01-03T23:55:11.590+08:00 TRACE 46026 --- [nio-8080-exec-4] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]

进入到类OAuth2LoginAuthenticationFilter,发现请求会进入到attemptAuthentication()方法中。该类主要完成以下步骤:

  • redirect_uri的请求会被过滤器OAuth2LoginAuthenticationFilter拦截处理
  • 向Github发送{token-uri}/login/oauth/access_token请求并获取token
  • 将获取的token封装到OAuth2AuthenticationToken中并返回该值
  • 设置Session
  • SecurityContextHolder 存储 OAuth2AuthenticationToken
  • 从Session拿出之前访问的受限地址并重定向到该地址。

SpringBoot OAuth2 Client前后端分离

经过上述分析,前端只需要访问localhost:8080/oauth2/authorization/github即可开始认证过程。只要设置了http.oauth2Login(),SpringSecurity 即会保证/oauth2/authorization/{client}认证端点对外开放。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated())
.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint();
return http.build();
}
}

但前端调用该接口是需要添加参数,告诉后端处理完成之后需要跳转回哪里。所以目前需要在Spring Security OAuth中完成两项工作:

  • 修改拦截localhost:8080/oauth2/authorization/github?origin_url=xxx的拦截器,将其中的参数origin_url取出存到Session中。
  • 登陆完成后,从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的参数

本文通过重写AuthorizationRequestRepositoryorigin_url写入Session。

也可以通过重写AuthorizationRequestResolver来解析origin_url,在解析选择哪种数据源的同时将origin_url封装到OAuth2AuthorizationRequest类里,后续AuthorizationRequestRepository会将该类直接放到Session中,最后需要通过获取Session中的OAuth2AuthorizationRequest对象,再从里面获取origin_url。读者可以自行尝试。

经过调试OAuth2AuthorizationRequestRedirectFilter,发现只要是authorization_code认证方式的OAuth请求,都会经过HttpSessionOAuth2AuthorizationRequestRepository的处理。

1
2
3
4
5
6
7
8
9
// OAuth2AuthorizationRequestRedirectFilter.java
private void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,
OAuth2AuthorizationRequest authorizationRequest) throws IOException {
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
}
this.authorizationRedirectStrategy.sendRedirect(request, response,
authorizationRequest.getAuthorizationRequestUri());
}

其中的this.authorizationRequestRepository默认是HttpSessionOAuth2AuthorizationRequestRepository。它的源码显示其专门在Session中存储请求信息:

1
2
3
4
5
6
7
// HttpSessionOAuth2AuthorizationRequestRepository.java
/**
* An implementation of an {@link AuthorizationRequestRepository} that stores
* {@link OAuth2AuthorizationRequest} in the {@code HttpSession}.
*/
public final class HttpSessionOAuth2AuthorizationRequestRepository
implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {}

所以,可以自定义该类来解析Request请求中的参数origin_url。查看Spring Security OAuth2 Client文档可以配置自定义的AuthorizationRequestRepository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(endpoint -> endpoint
.authorizationRequestRepository(new ParseUrlHttpSessionOAuth2AuthorizationRequestRepository())
// ...
)
);
return http.build();
}
}

目的只是为了增加一个解析URL参数,所以核心代码仍然使用它默认的HttpSessionOAuth2AuthorizationRequestRepository,只在saveAuthorizationRequest()方法中加入对origin_url的解析。

定义类ParseUrlHttpSessionOAuth2AuthorizationRequestRepository,将HttpSessionOAuth2AuthorizationRequestRepository的内容完全复制过来,将Class名字更改,并在saveAuthorizationRequest()中加入三行代码:

1
2
3
4
// set url params to session
String originUrl = request.getParameter("origin_url");
Assert.hasText(originUrl, "authorizationRequest.origin_url cannot be empty");
request.getSession().setAttribute("origin_url", originUrl);

解析URL获取origin_url,并将URL存入到Session中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// ParseUrlHttpSessionOAuth2AuthorizationRequestRepository.java
public class ParseUrlHttpSessionOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private static final String DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME = HttpSessionOAuth2AuthorizationRequestRepository.class
.getName() + ".AUTHORIZATION_REQUEST";

private final String sessionAttributeName = DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME;

@Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
...
}

@Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
HttpServletResponse response) {
Assert.notNull(request, "request cannot be null");
Assert.notNull(response, "response cannot be null");
if (authorizationRequest == null) {
removeAuthorizationRequest(request, response);
return;
}
String state = authorizationRequest.getState();
Assert.hasText(state, "authorizationRequest.state cannot be empty");

// set url params to session
String originUrl = request.getParameter("origin_url");
Assert.hasText(originUrl, "authorizationRequest.origin_url cannot be empty");
request.getSession().setAttribute("origin_url", originUrl);

request.getSession().setAttribute(this.sessionAttributeName, authorizationRequest);
}

@Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request,
HttpServletResponse response) {
...
}


private String getStateParameter(HttpServletRequest request) {
return request.getParameter(OAuth2ParameterNames.STATE);
}

private OAuth2AuthorizationRequest getAuthorizationRequest(HttpServletRequest request) {
...
}
}

设置登陆成功后跳转回制定URL(origin_url)

当OAuth2处理成功后,需要从Session中拿出origin_url并重定向。

Spring Security OAuth2文档显示可以自定义authenticationSuccessHandler,即http.oauth2Login.successHandler()来设置自定义成功后处理逻辑。

1
2
3
4
5
6
7
http.oauth2Login(oauth2 -> oauth2
.successHandler(
(request, response, authentication) -> {
String originUrl = (String)request.getSession().getAttribute("origin_url");
response.sendRedirect(originUrl);
})
)

最终OAuth2的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(authorization -> authorization
.authorizationRequestRepository(new ParseUrlHttpSessionOAuth2AuthorizationRequestRepository()))
.successHandler(
(request, response, authentication) -> {
String originUrl = (String) request.getSession().getAttribute("origin_url");
response.sendRedirect(originUrl);
})
)
);
return http.build();
}

}

全流程日志打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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)
2025-02-21T23:46:00.313+08:00 TRACE 156775 --- [nio-8443-exec-2] o.s.s.authentication.ProviderManager : Authenticating request with OidcAuthorizationCodeAuthenticationProvider (2/3)
2025-02-21T23:46:00.313+08:00 DEBUG 156775 --- [nio-8443-exec-2] .s.a.DefaultAuthenticationEventPublisher : No event was found for the exception org.springframework.security.oauth2.core.OAuth2AuthenticationException
2025-02-21T23:46:00.313+08:00 TRACE 156775 --- [nio-8443-exec-2] .s.o.c.w.OAuth2LoginAuthenticationFilter : Failed to process authentication request

org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: I/O error on POST request for "https://github.com/login/oauth/access_token": Unexpected end of file from server
at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:115) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-6.4.2.jar:6.4.2]
at org.springframework.security.authentication.ObservationAuthenticationManager.lambda$authenticate$1(ObservationAuthenticationManager.java:54) ~[spring-security-core-6.4.2.jar:6.4.2]
at io.micrometer.observation.Observation.observe(Observation.java:564) ~[micrometer-observation-1.14.2.jar:1.14.2]
at org.springframework.security.authentication.ObservationAuthenticationManager.authenticate(ObservationAuthenticationManager.java:53) ~[spring-security-core-6.4.2.jar:6.4.2]
at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:196) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:231) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:198) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:133) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.4.2.jar:6.4.2]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:243) ~[spring-webmvc-6.2.1.jar:6.2.1]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:238) ~[spring-security-config-6.4.2.jar:6.4.2]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) ~[spring-web-6.2.1.jar:6.2.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142) ~[spring-session-core-3.4.1.jar:3.4.1]
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82) ~[spring-session-core-3.4.1.jar:3.4.1]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278) ~[spring-web-6.2.1.jar:6.2.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.1.jar:6.2.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:598) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.34.jar:10.1.34]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: org.springframework.security.oauth2.core.OAuth2AuthorizationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: I/O error on POST request for "https://github.com/login/oauth/access_token": Unexpected end of file from server
at org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient.getResponse(DefaultAuthorizationCodeTokenResponseClient.java:101) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient.getTokenResponse(DefaultAuthorizationCodeTokenResponseClient.java:80) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient.getTokenResponse(DefaultAuthorizationCodeTokenResponseClient.java:57) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider.authenticate(OAuth2AuthorizationCodeAuthenticationProvider.java:85) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:109) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
... 100 common frames omitted
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://github.com/login/oauth/access_token": Unexpected end of file from server
at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:926) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:906) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:741) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient.getResponse(DefaultAuthorizationCodeTokenResponseClient.java:94) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
... 104 common frames omitted
Caused by: java.net.SocketException: Unexpected end of file from server
at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:955) ~[na:na]
at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:759) ~[na:na]
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1690) ~[na:na]
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1599) ~[na:na]
at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:531) ~[na:na]
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:307) ~[na:na]
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:85) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler.hasError(OAuth2ErrorResponseErrorHandler.java:52) ~[spring-security-oauth2-client-6.4.2.jar:6.4.2]
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:942) ~[spring-web-6.2.1.jar:6.2.1]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:902) ~[spring-web-6.2.1.jar:6.2.1]
... 106 common frames omitted

2025-02-21T23:46:00.317+08:00 TRACE 156775 --- [nio-8443-exec-2] .s.o.c.w.OAuth2LoginAuthenticationFilter : Cleared SecurityContextHolder
2025-02-21T23:46:00.317+08:00 TRACE 156775 --- [nio-8443-exec-2] .s.o.c.w.OAuth2LoginAuthenticationFilter : Handling authentication failure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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'
2025-02-19T13:38:16.583+08:00 INFO 28513 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2025-02-19T13:38:16.586+08:00 INFO 28513 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
2025-02-19T13:43:35.633+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain defined as 'securityFilterChain' in [class path resource [top/chc/usermanager/config/SecurityConfig.class]] matching [any request] and having filters [DisableEncodeUrl, ChannelProcessing, WebAsyncManagerIntegration, SecurityContextHolder, HeaderWriter, Cors, Logout, OAuth2AuthorizationRequestRedirect, OAuth2LoginAuthentication, BasicAuthentication, SecurityContextHolderAwareRequest, AnonymousAuthentication, ExceptionTranslation, Authorization] (1/1)
2025-02-19T13:43:35.634+08:00 DEBUG 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Securing GET /oauth2/authorization/github?origin_url=localhost:8443
2025-02-19T13:43:35.635+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/14)
2025-02-19T13:43:35.638+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking ChannelProcessingFilter (2/14)
2025-02-19T13:43:35.638+08:00 DEBUG 28513 --- [nio-8443-exec-2] o.s.s.w.a.c.ChannelProcessingFilter : Request: filter invocation [GET /oauth2/authorization/github?origin_url=localhost:8443]; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
2025-02-19T13:43:35.638+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (3/14)
2025-02-19T13:43:35.640+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (4/14)
2025-02-19T13:43:35.641+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (5/14)
2025-02-19T13:43:35.642+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking CorsFilter (6/14)
2025-02-19T13:43:35.643+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (7/14)
2025-02-19T13:43:35.643+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.s.w.a.logout.LogoutFilter : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
2025-02-19T13:43:35.644+08:00 TRACE 28513 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationRequestRedirectFilter (8/14)
2025-02-19T13:43:35.687+08:00 DEBUG 28513 --- [nio-8443-exec-2] o.s.s.web.DefaultRedirectStrategy : Redirecting to https://github.com/login/oauth/authorize?response_type=code&client_id=Ov23lisFyLM8ev2mgeX1&scope=read:user&state=1NiT_GBSj2pOolkHnTHpSft8V9x6uOMTsvYveTHM6GE%3D&redirect_uri=https://localhost:8443/login/oauth2/code/github
2025-02-19T13:44:50.058+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain defined as 'securityFilterChain' in [class path resource [top/chc/usermanager/config/SecurityConfig.class]] matching [any request] and having filters [DisableEncodeUrl, ChannelProcessing, WebAsyncManagerIntegration, SecurityContextHolder, HeaderWriter, Cors, Logout, OAuth2AuthorizationRequestRedirect, OAuth2LoginAuthentication, BasicAuthentication, SecurityContextHolderAwareRequest, AnonymousAuthentication, ExceptionTranslation, Authorization] (1/1)
2025-02-19T13:44:50.058+08:00 DEBUG 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Securing GET /login/oauth2/code/github?code=3fd7543e7cf92aab5d53&state=1NiT_GBSj2pOolkHnTHpSft8V9x6uOMTsvYveTHM6GE%3D
2025-02-19T13:44:50.058+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/14)
2025-02-19T13:44:50.059+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking ChannelProcessingFilter (2/14)
2025-02-19T13:44:50.059+08:00 DEBUG 28513 --- [io-8443-exec-10] o.s.s.w.a.c.ChannelProcessingFilter : Request: filter invocation [GET /login/oauth2/code/github?code=3fd7543e7cf92aab5d53&state=1NiT_GBSj2pOolkHnTHpSft8V9x6uOMTsvYveTHM6GE%3D]; ConfigAttributes: [REQUIRES_SECURE_CHANNEL]
2025-02-19T13:44:50.059+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (3/14)
2025-02-19T13:44:50.059+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (4/14)
2025-02-19T13:44:50.059+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (5/14)
2025-02-19T13:44:50.060+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking CorsFilter (6/14)
2025-02-19T13:44:50.060+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (7/14)
2025-02-19T13:44:50.061+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.s.w.a.logout.LogoutFilter : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
2025-02-19T13:44:50.061+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationRequestRedirectFilter (8/14)
2025-02-19T13:44:50.062+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2LoginAuthenticationFilter (9/14)
2025-02-19T13:44:50.102+08:00 TRACE 28513 --- [io-8443-exec-10] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2LoginAuthenticationProvider (1/3)
2025-02-19T13:44:51.534+08:00 TRACE 28513 --- [io-8443-exec-10] s.CompositeSessionAuthenticationStrategy : Preparing session with ChangeSessionIdAuthenticationStrategy (1/1)
2025-02-19T13:44:51.539+08:00 DEBUG 28513 --- [io-8443-exec-10] .s.ChangeSessionIdAuthenticationStrategy : Changed session id from 2cb1fec3-dba6-4a8f-8376-40934da49d8d
2025-02-19T13:44:51.541+08:00 DEBUG 28513 --- [io-8443-exec-10] w.c.HttpSessionSecurityContextRepository : Stored SecurityContextImpl [Authentication=OAuth2AuthenticationToken [Principal=Name: [42151586], Granted Authorities: [[OAUTH2_USER, SCOPE_read:user]], User Attributes: [{login=caohongchuan, id=42151586, node_id=MDQ6VXNlcjQyMTUxNTg2, avatar_url=https://avatars.githubusercontent.com/u/42151586?v=4, gravatar_id=, url=https://api.github.com/users/caohongchuan, html_url=https://github.com/caohongchuan, followers_url=https://api.github.com/users/caohongchuan/followers, following_url=https://api.github.com/users/caohongchuan/following{/other_user}, gists_url=https://api.github.com/users/caohongchuan/gists{/gist_id}, starred_url=https://api.github.com/users/caohongchuan/starred{/owner}{/repo}, subscriptions_url=https://api.github.com/users/caohongchuan/subscriptions, organizations_url=https://api.github.com/users/caohongchuan/orgs, repos_url=https://api.github.com/users/caohongchuan/repos, events_url=https://api.github.com/users/caohongchuan/events{/privacy}, received_events_url=https://api.github.com/users/caohongchuan/received_events, type=User, user_view_type=private, site_admin=false, name=Zheng Ying, company=null, blog=blog.caohongchuan.com, location=null, email=null, hireable=null, bio=Java/nlp, twitter_username=null, notification_email=null, public_repos=13, public_gists=0, followers=0, following=0, created_at=2018-08-06T17:32:16Z, updated_at=2025-02-19T05:44:49Z, private_gists=0, total_private_repos=2, owned_private_repos=2, disk_usage=28310, collaborators=0, two_factor_authentication=true, plan={name=free, space=976562499, collaborators=0, private_repos=10000}}], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=2cb1fec3-dba6-4a8f-8376-40934da49d8d], Granted Authorities=[OAUTH2_USER, SCOPE_read:user]]] to HttpSession [org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@66bfb1a7]
2025-02-19T13:44:51.542+08:00 DEBUG 28513 --- [io-8443-exec-10] .s.o.c.w.OAuth2LoginAuthenticationFilter : Set SecurityContextHolder to OAuth2AuthenticationToken [Principal=Name: [42151586], Granted Authorities: [[OAUTH2_USER, SCOPE_read:user]], User Attributes: [{login=caohongchuan, id=42151586, node_id=MDQ6VXNlcjQyMTUxNTg2, avatar_url=https://avatars.githubusercontent.com/u/42151586?v=4, gravatar_id=, url=https://api.github.com/users/caohongchuan, html_url=https://github.com/caohongchuan, followers_url=https://api.github.com/users/caohongchuan/followers, following_url=https://api.github.com/users/caohongchuan/following{/other_user}, gists_url=https://api.github.com/users/caohongchuan/gists{/gist_id}, starred_url=https://api.github.com/users/caohongchuan/starred{/owner}{/repo}, subscriptions_url=https://api.github.com/users/caohongchuan/subscriptions, organizations_url=https://api.github.com/users/caohongchuan/orgs, repos_url=https://api.github.com/users/caohongchuan/repos, events_url=https://api.github.com/users/caohongchuan/events{/privacy}, received_events_url=https://api.github.com/users/caohongchuan/received_events, type=User, user_view_type=private, site_admin=false, name=Zheng Ying, company=null, blog=blog.caohongchuan.com, location=null, email=null, hireable=null, bio=Java/nlp, twitter_username=null, notification_email=null, public_repos=13, public_gists=0, followers=0, following=0, created_at=2018-08-06T17:32:16Z, updated_at=2025-02-19T05:44:49Z, private_gists=0, total_private_repos=2, owned_private_repos=2, disk_usage=28310, collaborators=0, two_factor_authentication=true, plan={name=free, space=976562499, collaborators=0, private_repos=10000}}], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=2cb1fec3-dba6-4a8f-8376-40934da49d8d], Granted Authorities=[OAUTH2_USER, SCOPE_read:user]]

Java Servlet

Spring Security提供两种方式,一种是基于Servlet模型,一种是基于Reactive模型

1
2
3
4
5
flowchart LR
A[Client] -->|http request| B(Filter1)
B(Filter1) --> C(Filter2)
C(Filter2) --> D(Filter...)
D(Filter...) --> E(Sevlet)

向Servlet中加入Filter有两种方式:

  • 直接添加在web.xml里面
  • 使用Java注解WebFilter()添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebFilter("/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----初始化---");
System.out.println(filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//设置字符编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");

System.out.println("--------放行前--------");
chain.doFilter(request,response); //放行
System.out.println("--------放行后--------");

}
}

Spring Security

spring security

SpringBoot会将DelegatingFilterProxy注册到FilterChain里,请求在进入Servlet之前会通过DelegatingFilterProxy这个Filter,DelegatingFilterProxy在初始化的时候会通过WebApplicationContext加载名称为springSecurityFilterChain的bean,该bean类中doFilterDelegate中包含了FilterChainProxy

image-20241230133737429

通过将DelegatingFilterProxy加入到Servlet的Filter中,并且DelegatingFilterProxy中通过调用WebApplicationContext创建FilterChainProxy,二者将Servlet和Spring框架结合起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const generateSecureRandomString = (length: number): string => {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const crypto = window.crypto
const randomValues = new Uint32Array(length);
crypto.getRandomValues(randomValues);
let randomString = "";
for (let i = 0; i < length; i++) {
const randomIndex = randomValues[i] % charset.length;
randomString += charset.charAt(randomIndex);
}
return randomString;
}

const login = () => {
const CLIENT_ID = 'Ov23lisFyLM8ev2mgeX1'

const authorize_uri = 'https://github.com/login/oauth/authorize';
const redirect_uri = 'http://localhost:8080/login/oauth2/code/github';
const state: string = generateSecureRandomString(10);
window.location.href = `${authorize_uri}?client_id=${CLIENT_ID}&scope=user:email&redirect_uri=${redirect_uri}&state=${state}`;
}

23种设计模式(Java实现)

1. 工厂模式

工厂模式是SpringBoot最常见的模式。

2. 模板模式

3. 单例模式

0%