目标是实现前后端分离(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接受请求使用的是Java Servlet的过滤器(Filter)。

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%