😄Spring Authorization Server (4) 客户端、授权服务器认证授权流程的深入解析

demo-client(客户端)

从提供的demo中可以理解为 demo-client就是客户端,或者也理解为:引入了 spring-boot-starter-oauth2-client依赖的也是客户端

demo-client中、demo-authorizationserver中都引入 spring-boot-starter-oauth2-client依赖,他们的作用肯定也是一致,我们来一起看看

访问 http://127.0.0.1:8080/index,看看demo-client的日志,进行解析。

java 复制代码
2023-09-04T16:34:56.211+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.security.web.FilterChainProxy        : Securing GET /index
2023-09-04T16:34:56.212+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-09-04T16:34:56.213+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://127.0.0.1:8080/index?continue to session
2023-09-04T16:34:56.214+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://127.0.0.1:8080/oauth2/authorization/messaging-client-oidc
2023-09-04T16:34:56.225+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.security.web.FilterChainProxy        : Securing GET /oauth2/authorization/messaging-client-oidc
2023-09-04T16:34:56.226+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://192.168.56.1:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=_c0sC-fsLjR6ZTi37U65_J5Z5JCnpoSyfoFa5A2llo4%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=t02L8qUOiibfrKdZuukArlhoymJBFRFDual0kSTKI7w
2023-09-04T16:34:57.103+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.security.web.FilterChainProxy        : Securing GET /index
2023-09-04T16:34:57.104+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-09-04T16:34:57.106+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://127.0.0.1:8080/index?continue to session
2023-09-04T16:34:57.109+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://127.0.0.1:8080/oauth2/authorization/messaging-client-oidc
2023-09-04T16:34:57.120+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.security.web.FilterChainProxy        : Securing GET /oauth2/authorization/messaging-client-oidc
2023-09-04T16:34:57.120+08:00 DEBUG 6224 --- [  XNIO-1 task-2] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://192.168.56.1:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=dFUSOiG3BXavKSPFCHfK-ER7LbcAyOnHkzWcsNG9w_4%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=XMptCr0NC1yRO1yNJ6v6VdtuNTC09Di_wPm_LWb7Ph0
  • Redirecting to http://127.0.0.1:8080/oauth2/authorization/messaging-client-oidc 这个/oauth2/authorization肯定又是spring security 内置的,对应的 OAuth2AuthorizationRequestRedirectFilter

    java 复制代码
     public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
        //The default base {@code URI} used for authorization requests.
        public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";  
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
               OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request); //
               if (authorizationRequest != null) {
                   this.sendRedirectForAuthorization(request, response, authorizationRequest);
                   return;
               }
           }
           catch (Exception ex) {
               this.unsuccessfulRedirectForAuthorization(request, response, ex);
               return;
           }
    
    
       }
         ...   
     }
  • OAuth2AuthorizationRequestRedirectFilter 中进去看看做了,这个 Redirecting to http://192.168.56.1:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=_c0sC-fsLjR6ZTi37U65_J5Z5JCnpoSyfoFa5A2llo4%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=t02L8qUOiibfrKdZuukArlhoymJBFRFDual0kSTKI7w是怎么样来的呢?

    java 复制代码
    //authorizationRequestResolver 的默认实现类   DefaultOAuth2AuthorizationRequestResolver 组装了 OAuth2AuthorizationRequest 对象返回,这个对象里面是对应客户端的一些信息
    OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request); 
    //有了客户端的一些信息 组装一个获取授权码的请求 进行重定向 
    this.sendRedirectForAuthorization(request, response, authorizationRequest); 

    OAuth2AuthorizationRequestRedirectFilter干了以下几个事:

    这不就是我们之前使用postman 去请求 授权服务器的 oauth2/authorize?client_id=messaging-client&response_type=code&scope={scope}&redirect_uri={redirect_uri}

demo-authorizationserver(授权服务器)

从提供的demo中可以理解为demo-authorizationserver就是授权服务端,或者也理解为:引入了 spring-security-oauth2-authorization-server依赖的也是授权服务端

看看demo-authorizationserver的日志,进行解析。

java 复制代码
2023-09-05T10:00:43.140+08:00 DEBUG 18232 --- [  XNIO-1 task-4] o.s.security.web.FilterChainProxy        : Securing GET /oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=u2Gws4H0Sk34mm8gQGRvx3Ulx6ukazugneZEbaCeBj8%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=XG92BigeimIMXkDZnQwKmJY1E2_pGGzeAeeqwwq87mU
2023-09-05T10:00:43.194+08:00 DEBUG 18232 --- [  XNIO-1 task-4] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-09-05T10:00:43.202+08:00 DEBUG 18232 --- [  XNIO-1 task-4] o.s.s.w.s.HttpSessionEventPublisher      : Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=io.undertow.servlet.spec.HttpSessionImpl@6670b8f6]
2023-09-05T10:00:43.203+08:00 DEBUG 18232 --- [  XNIO-1 task-4] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://192.168.56.1:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=u2Gws4H0Sk34mm8gQGRvx3Ulx6ukazugneZEbaCeBj8%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=XG92BigeimIMXkDZnQwKmJY1E2_pGGzeAeeqwwq87mU&continue to session
2023-09-05T10:00:43.204+08:00 DEBUG 18232 --- [  XNIO-1 task-4] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.HeaderContentNegotiationStrategy@2c929396, matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[]]
2023-09-05T10:00:43.206+08:00 DEBUG 18232 --- [  XNIO-1 task-4] s.w.a.DelegatingAuthenticationEntryPoint : Match found! Executing org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint@1441274e
2023-09-05T10:00:43.207+08:00 DEBUG 18232 --- [  XNIO-1 task-4] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://192.168.56.1:9000/login
java 复制代码
 Securing GET 
/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=u2Gws4H0Sk34mm8gQGRvx3Ulx6ukazugneZEbaCeBj8%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=XG92BigeimIMXkDZnQwKmJY1E2_pGGzeAeeqwwq87mU

这个就是客户端重定向到授权服务器的授权请求

/oauth2/authorize对应是 OAuth2AuthorizationEndpointFilter(在spring-authorization-server源码中能搜索到这个过滤器)

java 复制代码
public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {
 //The default endpoint {@code URI} for authorization requests.
 private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize";
 ...省略
 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
         throws ServletException, IOException {

     if (!this.authorizationEndpointMatcher.matches(request)) {
         filterChain.doFilter(request, response);
         return;
     }

     try {
         Authentication authentication = this.authenticationConverter.convert(request);
         if (authentication instanceof AbstractAuthenticationToken) {
             ((AbstractAuthenticationToken) authentication)
                     .setDetails(this.authenticationDetailsSource.buildDetails(request));
         }
         Authentication authenticationResult = this.authenticationManager.authenticate(authentication);

         if (!authenticationResult.isAuthenticated()) {
             // If the Principal (Resource Owner) is not authenticated then
             // pass through the chain with the expectation that the authentication process
             // will commence via AuthenticationEntryPoint
             filterChain.doFilter(request, response);
             return;
         }

         if (authenticationResult instanceof OAuth2AuthorizationConsentAuthenticationToken) {
             if (this.logger.isTraceEnabled()) {
                 this.logger.trace("Authorization consent is required");
             }
             sendAuthorizationConsent(request, response,
                     (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication,
                     (OAuth2AuthorizationConsentAuthenticationToken) authenticationResult);
             return;
         }

         this.sessionAuthenticationStrategy.onAuthentication(
                 authenticationResult, request, response);

         this.authenticationSuccessHandler.onAuthenticationSuccess(
                 request, response, authenticationResult);

     } catch (OAuth2AuthenticationException ex) {
         if (this.logger.isTraceEnabled()) {
             this.logger.trace(LogMessage.format("Authorization request failed: %s", ex.getError()), ex);
         }
         this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);
     }
 }
 ...省略
}

授权服务器也保存了授权请求到session中

java 复制代码
Saved request http://192.168.56.1:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid%20profile&state=u2Gws4H0Sk34mm8gQGRvx3Ulx6ukazugneZEbaCeBj8%3D&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&nonce=XG92BigeimIMXkDZnQwKmJY1E2_pGGzeAeeqwwq87mU&continue to session

以上的这些流程和之前用postman是不是一样的,我们登录再继续看看,选择gitee登录

html 复制代码
<a class="w-100 btn btn-light btn-block bg-white" href="/oauth2/authorization/gitee" role="link" style="margin-top: 10px">
     <img src="/assets/img/gitee.png" th:src="@{/assets/img/gitee.png}" width="20" style="margin-right: 5px;" alt="Sign in with Gitee">
     Sign in with Gitee
</a>

href="/oauth2/authorization/gitee 这个是客户端中的模板url,因为授权服务端 引入了spring-boot-starter-oauth2-client依赖。

为什么我们要引入spring-boot-starter-oauth2-client依赖呢?

因为demo-authorizationserver 使用 gitee 登录的时候 ,是不是demo-authorizationserver 服务 相对 gitee的授权服 就是客户端了,然后 demo-authorizationserver 引入 spring-boot-starter-oauth2-client依赖 配置/oauth2/authorization/gitee 也是构建了一个 向gitee的授权请求。

看看日志

java 复制代码
2023-09-05T10:58:31.465+08:00 DEBUG 18232 --- [  XNIO-1 task-5] o.s.s.web.DefaultRedirectStrategy        : Redirecting to https://gitee.com/oauth/authorize?response_type=code&client_id=29b85c97ed682910eaa4276d84a0c4532f00b962e1b9fe8552520129e65ae432&scope=emails%20user_info&state=jUPPNYGLGSX4wdUCUIpAxCo22xBnTUDoVL_5eyyhuGI%3D&redirect_uri=http://192.168.56.1:9000/login/oauth2/code/gitee

giee登录授权成功后,回调demo-authorizationserver的 http://192.168.56.1:9000/login/oauth2/code/gitee

java 复制代码
2023-09-05T11:04:42.741+08:00 DEBUG 18232 --- [  XNIO-1 task-5] o.s.security.web.FilterChainProxy        : Securing GET /login/oauth2/code/gitee?code=878b7b045bacc8ce85f3c5437dfb9669d5a4584a53237ba9db79b33439a10a01&state=jUPPNYGLGSX4wdUCUIpAxCo22xBnTUDoVL_5eyyhuGI%3D

返回的授权码,demo-authorizationserver拿着授权码到gitee服务中去换取token,这个过程是不是很熟悉😄

跟进看看回调的处理,/login/oauth2/code/*对应的是OAuth2LoginAuthenticationFilter

java 复制代码
public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {   
 //The default {@code URI} where this {@code Filter} processes authentication
 public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";

 // 核心方法
 @Override
 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
         throws AuthenticationException {

     //这个里面就是那找授权码到gitee服务去换取token ,跟进去看看 OAuth2AuthorizationCodeAuthenticationProvider、DefaultAuthorizationCodeTokenResponseClient
     OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this
             .getAuthenticationManager().authenticate(authenticationRequest);

     //保存gitee登录的
     this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
 	return oauth2Authentication;
 }

}

DefaultAuthorizationCodeTokenResponseClient 帮我们做了拿code去获取token这个流程,gitee 就成功授权给 demo-authorizationserver 服务了

java 复制代码
Redirecting to http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc?code=FDiywGjUuXB5fYJhpth1sNcj_yQ-goUPMwslNdOwjCrAmAxKdp53pUV3ufr49owGOao49GFfR_zlUQ_k2L3QMoVwwbVAH88lvfKte0XMhvdCMbUf9WhrAJbZDhuGYe3z&state=jni3l4Hzr99RjaK4LbEokSld6buwR8lbj8cmJu0FCKo%3D

回调到 demo-client的授权请求的回调地址

后面流程也就是与 demo-authorizationserver 到 gitee 换取流程一致了

demo-client 拿着 code 到 demo-authorizationserver换取token,然后 demo-authorizationserver 成功授权给 demo-client

总结

spring-boot-starter-oauth2-client

  • 帮客户端构建向授权服务器获取的授权请求(支持oauth2.0协议都支持(也就是授权流程是一致的),避免程序员重复造轮子)
  • 帮客户端构建向授权服务器拿code换取token的请求

spring-security-oauth2-authorization-server 从以上来看还没有体现出来它做了什么,实际上它做认证的方式,授权客户端,后面在扩展的时候具体讲解

  • 认证方式(授权码模式、密码模式、客户端模式、设备码模式)
  • 授权客户端

oauth客户端认证、授权的流程图

相关推荐
用户8307196840824 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解5 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解5 小时前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记9 小时前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者1 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840821 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解1 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者2 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺2 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart2 天前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot