OAuth2.0 detailed introduction and practice (easy to understand)

1. Introduction to OAuth2.0

1.1 Overview

  • The OAUTH protocol provides a safe, open and simple standard for authorization of user resources. The difference from the previous authorization methods is that the OAUTH authorization will not allow the third party to touch the user's account information (such as user name and password) , that is, the third party can apply for the user's resources without using the user's user name and password. Authorization , so OAUTH is secure. oAuth is short for Open Authorization.
  • OAuth (Open Authorization) is an open standard that allows users to authorize third-party applications to access their information stored on another service provider without giving the third-party application a username and password or sharing all content of their data . OAuth2.0 is a continuation of the OAuth protocol, but it is not backward compatible with OAuth1.0, that is, OAuth1.0 is completely abolished.
  • OAuth is an open protocol that provides a simple, standard way for desktop, mobile or web applications to access API services that require user authorization

1.2 Features

  • Simple: Whether it is an OAUTH service provider or an application developer, it is easy to understand and use;
  • Security: No information such as user keys is involved, which is safer and more flexible;
  • Open: Any service provider can implement OAUTH, and any software developer can use OAUTH;

1.2 Example of Oauth2 authentication

This example is the process of a website using WeChat authentication

  1. If a user logs in to a website with the help of WeChat authentication, the user does not need to register as a user on the website alone. How is the authentication successful?
  2. If a website needs to successfully obtain the user's identity information from WeChat, it considers that the user authentication is successful, so how to obtain the user's identity information from WeChat?
  3. The owner of the user information is the user himself. WeChat needs the user's consent to generate a token for a website, and a website can obtain the user's information from WeChat with this token.

Step 1 : The client (browser) requests third-party authorization

The user enters the login page of the website, clicks the icon of WeChat to log in to the system with the WeChat account, and the user is the resource owner of the information in WeChat. Click on "WeChat" and a QR code will appear. At this time, the user scans the QR code and starts to authorize the client.

insert image description here

Step 2: The resource owner agrees to authorize the client

The resource owner scans the QR code to indicate that the resource owner agrees to authorize the client. WeChat will verify the identity of the resource owner. After the verification is passed, WeChat will ask the user whether to authorize the client to access their WeChat data. The user clicks " "Confirm Login" indicates that you agree to the authorization, and the WeChat authentication server will issue an authorization code and redirect you to the client's website.

insert image description here

Step 3: The client obtains the authorization code and requests the authentication server to apply for a token

This process cannot be seen by the user, and the client application requests the authentication server to carry the authorization code.

Step 4: The authentication server responds with a token to the client

The WeChat authentication server verifies the authorization code requested by the client, and issues a token to the client if it is legal, and the token is the passport for the client to access resources. The user cannot see this interaction process. When the client gets the token, the user can see that the login has been successful on the website.

Step 5: The client requests resources from the resource server

The client carries the token to access the resources of the resource server.

The client website carries the token and requests to access the WeChat server to obtain the basic information of the user.

Step 6: The resource server returns the protected resource

The resource server verifies the legitimacy of the token, and if it is legal, responds to the resource information content to the user.

The detailed execution process of the above authentication and authorization is as follows:

insert image description here

1.4 OAuth2.0 authentication process on the official website

insert image description here

OAauth2.0 includes the following roles:

  • client

    It does not store resources itself, and needs to request resources from the resource server through the authorization of the resource owner, such as: Android client, Web client (browser end), WeChat client, etc.

  • resource owner

    Usually a user, but also an application, that is, the owner of the resource.

  • Authorization server (also known as authentication server)

    It is used by the service provider to authenticate the identity owned by the resource and authorize access to the resource. After successful authentication, the client will issue a token (access_token) as the credential for the client to access the resource server. This example is the authentication server of WeChat.

  • resource server

    A server that stores resources, in this example, user information stored by WeChat.

Now there is another question, can the service provider allow any client to access its authorization server? The answer is no, the service provider will give the admitted access party an identity for the credentials when accessing: client_id: client identification

client_secret: client secret key

Therefore, to be precise, the authorization server performs authentication and authorization on two roles in OAuth2.0, which are the resource owner and the client .

1.5 Authorization Process

three URLs

  • Request Token URL: Get unauthorized Request Token service address;

  • User Authorization URL: Get the Request Token service address authorized by the user;

  • Access Token URL: Exchange the authorized Request Token for the service address of the Access Token;

OAUTH authentication and authorization are three steps, which can be summarized in three sentences:

  1. Get an unauthorized Request Token
  2. Obtain the Request Token authorized by the user
  3. Exchange authorized Request Token for Access Token

After the application obtains the Access Token, it has the right to access the resources authorized by the user . You may have noticed that these three steps are the three URL service addresses corresponding to OAUTH. In the above three steps, each step requests a URL and receives relevant information, and obtains the relevant information of the previous step to request the next URL until the Access Token is obtained.

The specific execution information of each step is as follows:

  1. The user (third-party software) requests an unauthorized Request Token from the OAUTH service provider. Initiate a request to the Request Token URL, and the parameters that need to be included in the request.
  2. The OAUTH service provider agrees to the user's request, issues an unauthorized oauth_token and corresponding oauth_token_secret to the user, and returns it to the user.
  3. The user requests the Request Token authorized by the user from the OAUTH service provider. Initiate a request to the User Authorization URL, requesting to bring the unauthorized token and key obtained in the previous step.
  4. The OAUTH service provider will guide the user to authorize. The process may prompt the user which protected resources you want to grant to the app. This step may or may not return an authorized Request Token. For example, Yahoo OAUTH will not return any information to the user.
  5. After the Request Token is authorized, the user will initiate a request to the Access Token URL to exchange the Request Token authorized in the previous step for an Access Token. This one more parameter than the first step is Request Token.
  6. The OAUTH service provider agrees to the user's request, issues an Access Token and the corresponding key to the user, and returns it to the user.
  7. The user can then use the Access Token returned in the previous step to access the resources authorized by the user.

It can be seen from the above steps that the user has never provided information such as his user name and password to the user, which is more secure.

Two, OAuth2 practice

  • Spring-Security-OAuth2 is an implementation of OAuth2, and it complements the Spring Security we learned before, and the integration with the Spring Cloud system is also very convenient
  • The service provider of OAuth2.0 covers two services, namely Authorization Server (also called Authentication Service) and Resource Server (Resource Server). When using Spring Security OAuth2, you can choose to implement them in the same application , you can also choose to create multiple resource services using the same authorization service.

**Authorization Server** should include functions such as verifying the legitimacy of the access terminal and the login user and issuing tokens. The request endpoint for the token is implemented by the Spring MVC controller. The following is to configure an authentication service Endpoints that must be implemented:

  • AuthorizationEndpoint serves authentication requests. Default URL: /oauth/authorize .
  • TokenEndpoint serves requests for access tokens. Default URL: /oauth/token.

Resource Server (Resource Server) should include resource protection functions, intercept illegal requests, parse and authenticate tokens in requests, etc. The following filters are used to implement OAuth 2.0 resource services:

  • OAuth2AuthenticationProcessingFilter is used to parse and authenticate the identity token given by the request.

2.1 The simplest OAuth2 for distributed authority management

project one

Authentication and authorization demo flow chart

insert image description here

1. The client requests the authorization service for authentication.

2. After passing the authentication, the token will be issued.

3. The client carries the token Token to request resource services.

4. The resource service verifies the legitimacy of the token, and returns the resource information if it is legal.

demo illustration

insert image description here

2.1.1 Authorization service construction

  • Create a spring boot project

  • import dependencies

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>cn.cvzhanshi</groupId>
        <artifactId>authorization_server</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>authorization_server</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    
  • configuration file

    # 应用名称
    spring:
      application:
        name: authorization_server
    server:
      port: 3001
    
  • security configuration class

    /**
     * @author cVzhanshi
     * @create 2022-10-25 17:55
     */
    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
          
          
    
    
    
        @Bean
        public PasswordEncoder passwordEncoder(){
          
          
            return new BCryptPasswordEncoder();
        }
    
        //密码模式才需要配置,认证管理器
        @Bean
        @Override
        protected AuthenticationManager authenticationManager() throws Exception {
          
          
            return super.authenticationManager();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
          
          
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().permitAll()
                    .and()
                    .formLogin()
                    .and()
                    .logout();
        }
    
        // 模拟账号密码验证  就两个账号   user/user  admin/admin
        @Bean
        public UserDetailsService userDetailsService() {
          
          
            UserDetailsService userDetailsService = new UserDetailsService() {
          
          
                @Override
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          
          
                    if (username.equals("user")){
          
          
                        MyUserDetails user = new MyUserDetails();
                        user.setUsername("user");
                        user.setPassword(passwordEncoder().encode("user"));
                        user.setPerms("user");
                        return user;
                    }
                    if (username.equals("admin")){
          
          
                        MyUserDetails admin = new MyUserDetails();
                        admin.setUsername("admin");
                        admin.setPassword(passwordEncoder().encode("admin"));
                        admin.setPerms("admin");
                        return admin;
                    }
                    return null;
                }
            };
            return userDetailsService;
        }
    }
    
  • Configure OAuth2.0 authorization service configuration class

    • You can use @EnableAuthorizationServer annotation and inherit AuthorizationServerConfigurerAdapter to configure OAuth2.0 authorization server.

    • AuthorizationServerConfigurerAdapter requires the configuration of the following classes, which are independent configuration objects created by Spring, which will be passed into AuthorizationServerConfigurer by Spring for configuration.

      public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
              
              
          public AuthorizationServerConfigurerAdapter() {
              
              }
          public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
              
              }
          public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
              
              }
          public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
              
              }
      }
      
      • ClientDetailsServiceConfigurer : Used to configure the client details service (ClientDetailsService). The client details information is initialized here. You can write the client details information here or store and call the details information through the database.
      • AuthorizationServerEndpointsConfigurer : Used to configure the access endpoints and token services of tokens.
      • AuthorizationServerSecurityConfigurer : Used to configure the security constraints of the token endpoint
    • Configure client details

      • ClientDetailsServiceConfigurer can use memory or JDBC to implement client details service (ClientDetailsService), ClientDetailsService is responsible for finding ClientDetails, and ClientDetails has several important properties as listed below:

        • clientId: (required) the Id used to identify the client.
        • secret: (requires trusted client) client security code (password), if any
        • scope: used to limit the access scope of the client, if it is empty (default), then the client has all the access scope.
        • authorizedGrantTypes: Grant types that this client can use, default is empty.
        • authorities: The authorities this client can use (based on Spring Security authorities).
        // 配置客户端详细信息,可以配置多个
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                  
                  
            // 存储在缓存中,后期可以选择存在数据库中
            clients.inMemory()
                // 设置客户端的id和密码
                .withClient(("client1"))
                .secret(passwordEncoder.encode("123456"))
                // 给client一个id,这个在client的配置里要用的, 能使用的资源id
                .resourceIds("resource1")
        
                //允许的申请token的方式
                //authorization_code授权码模式,这个是标准模式
                //implicit简单模式,这个主要是给无后台的纯前端项目用的
                //password密码模式,直接拿用户的账号密码授权,不安全
                //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
                //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                // 授权的范围,每个resource会设置自己的范围.
                .scopes("scope1","scope2")
                .autoApprove(false)
                .redirectUris("http://www.baidu.com");
        
            /*配置更多客户端
                        .and()
        
                        .withClient("client2")
                        .secret(passwordEncoder.encode("123123"))
                        .resourceIds("resource1")
                        .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                        .scopes("all")
                        .autoApprove(false)
                        .redirectUris("http://www.qq.com");*/
        
        
        }
        
    • management token

      • The AuthorizationServerTokenServices interface defines some operations so that you can perform some necessary management on the token. The token can be used to load identity information, which contains the relevant permissions of the token. This class does almost everything for you, except that the persistent token is delegated to a TokenStore interface. And the TokenStore interface has a default implementation, which is InMemoryTokenStore. As it is named, all tokens are stored in memory. In addition to InMemoryTokenStore, there are some other versions JdbcTokenStore: This is a JDBC-based implementation version, and tokens will be stored in a relational database. JwtTokenStore.

      • Define TokenConfig¶

        /**
         * @author cVzhanshi
         * @create 2022-10-25 18:18
         */
        @Configuration
        public class TokenConfig {
                  
                  
        	// 令牌的存储策略
            @Bean
            public TokenStore tokenStore(){
                  
                  
                // 在内存中生成一个普通的令牌
                return new InMemoryTokenStore();
            }
        }
        
      • Define AuthorizationServerTokenServices Define AuthorizationServerTokenServices in AuthorizationServer

        @Autowired
        private TokenStore tokenStore;
        @Autowired
        private ClientDetailsService clientDetailsService;
        
        
        //配置token管理服务
        @Bean
        public AuthorizationServerTokenServices tokenServices() {
                  
                  
            DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            // 客户端信息服务
            defaultTokenServices.setClientDetailsService(clientDetailsService);
            // 是否产生刷新令牌
            defaultTokenServices.setSupportRefreshToken(true);
        
            //配置token的存储策略
            defaultTokenServices.setTokenStore(tokenStore);
            // 令牌的有效期
            defaultTokenServices.setAccessTokenValiditySeconds(300);
            // 刷新令牌的时间
            defaultTokenServices.setRefreshTokenValiditySeconds(1500);
            return defaultTokenServices;
        }
        
    • Token Access Endpoint Configuration

      AuthorizationServerEndpointsConfigurer instance of this object can complete token service and token endpoint configuration.

      • Configure Grant Types (Grant Types) AuthorizationServerEndpointsConfigurer determines the supported grant types (Grant Types) by setting the following properties:

        • authenticationManager : authentication manager, when you choose resource owner password (password) authorization type, please set this property to inject an AuthenticationManager object.
        • userDetailsService : If you set this property, it means that you have an implementation of your own UserDetailsService interface, or you can set this thing to the global domain. When you set this, then "refresh_token" is the refresh token The process of the authorization type mode will include a check to ensure that the account is still valid. If you disable the account, the authorization will fail.
        • authorizationCodeServices : This attribute is used to set the authorization code service (that is, the instance object of AuthorizationCodeServices), which is mainly used in the "authorization_code" authorization code type mode.
        • implicitGrantService : This attribute is used to set the implicit grant mode, which is used to manage the state of the implicit grant mode.
        • tokenGranter : When you set this thing (that is, TokenGranter interface implementation), then the authorization will be fully controlled by you, and the above attributes will be ignored. This attribute is generally used for extension purposes, that is, the standard Only when the four authorization modes can no longer meet your needs will you consider using this.
      • Configure the URL of the authorization endpoint (Endpoint URLs): AuthorizationServerEndpointsConfigurer This configuration object has a method called pathMapping() to configure the endpoint URL link, which has two parameters: The first parameter: String type, the default link of this endpoint URL . The second parameter: String type, you want to replace the URL link.

        以上的参数都将以 "/" 字符为开始的字符串,框架的默认URL链接如下列表,可以作为这个 pathMapping() 方法的第一个参数:
        /oauth/authorize:授权端点。
        /oauth/token:令牌端点。
        /oauth/confirm_access:用户确认授权提交端点。
        /oauth/error:授权服务错误信息端点。
        /oauth/check_token:用于资源服务访问的令牌解析端点。
        /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。
        需要注意的是授权端点这个URL应该被Spring Security保护起来只供授权用户访问.
        
      //密码模式才需要配置,认证管理器
      @Autowired
      private AuthenticationManager authenticationManager;
      @Bean
      public AuthorizationCodeServices authorizationCodeServices() {
              
               //设置授权码模式的授权码如何存取,暂时采用内存方式
          return new InMemoryAuthorizationCodeServices();
      }
      @Autowired
      private AuthorizationCodeServices authorizationCodeServices;
      
      // 把上面的各个组件组合在一起
      @Override
      public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
              
              
          endpoints.authenticationManager(authenticationManager)//认证管理器
              .authorizationCodeServices(authorizationCodeServices)//授权码管理
              .tokenServices(tokenServices())//token管理
              .allowedTokenEndpointRequestMethods(HttpMethod.POST);
      }
      
    • Security Constraints for Token Endpoints

      // 令牌端点的安全约束
      //配置哪些接口可以被访问
      @Override
      public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
              
              
          security.tokenKeyAccess("permitAll()")//   /oauth/token_key公开
              .checkTokenAccess("permitAll()")//   /oauth/check_token公开
              .allowFormAuthenticationForClients();//允许表单认证
      }
      
    • The complete code of the authorization configuration class

      /**
       * @author cVzhanshi
       * @create 2022-10-25 18:05
       */
      @Configuration
      //开启oauth2,auth server模式
      @EnableAuthorizationServer
      public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
              
              
      
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          // 配置客户端详细信息,可以配置多个
          @Override
          public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
              
              
              // 存储在缓存中,后期可以选择存在数据库中
              clients.inMemory()
                  // 设置客户端的id和密码
                  .withClient(("client1"))
                  .secret(passwordEncoder.encode("123456"))
                  // 给client一个id,这个在client的配置里要用的, 能使用的资源id
                  .resourceIds("resource1")
      
                  //允许的申请token的方式
                  //authorization_code授权码模式,这个是标准模式
                  //implicit简单模式,这个主要是给无后台的纯前端项目用的
                  //password密码模式,直接拿用户的账号密码授权,不安全
                  //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
                  //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
                  .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                  // 授权的范围,每个resource会设置自己的范围.
                  .scopes("scope1","scope2")
                  .autoApprove(false)
                  .redirectUris("http://www.baidu.com");
      
              /*配置更多客户端
                      .and()
      
                      .withClient("client2")
                      .secret(passwordEncoder.encode("123123"))
                      .resourceIds("resource1")
                      .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                      .scopes("all")
                      .autoApprove(false)
                      .redirectUris("http://www.qq.com");*/
      
      
          }
      
          @Autowired
          private TokenStore tokenStore;
          @Autowired
          private ClientDetailsService clientDetailsService;
      
      
          //配置token管理服务
          @Bean
          public AuthorizationServerTokenServices tokenServices() {
              
              
              DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
              // 客户端信息服务
              defaultTokenServices.setClientDetailsService(clientDetailsService);
              // 是否产生刷新令牌
              defaultTokenServices.setSupportRefreshToken(true);
      
              //配置token的存储策略
              defaultTokenServices.setTokenStore(tokenStore);
              // 令牌的有效期
              defaultTokenServices.setAccessTokenValiditySeconds(300);
              // 刷新令牌的时间
              defaultTokenServices.setRefreshTokenValiditySeconds(1500);
              return defaultTokenServices;
          }
      
          //密码模式才需要配置,认证管理器
          @Autowired
          private AuthenticationManager authenticationManager;
          @Bean
          public AuthorizationCodeServices authorizationCodeServices() {
              
               //设置授权码模式的授权码如何存取,暂时采用内存方式
              return new InMemoryAuthorizationCodeServices();
          }
          @Autowired
          private AuthorizationCodeServices authorizationCodeServices;
      
          // 把上面的各个组件组合在一起
          @Override
          public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
              
              
              endpoints.authenticationManager(authenticationManager)//认证管理器
                  .authorizationCodeServices(authorizationCodeServices)//授权码管理
                  .tokenServices(tokenServices())//token管理
                  .allowedTokenEndpointRequestMethods(HttpMethod.POST);
          }
      
          // 令牌端点的安全约束
          //配置哪些接口可以被访问
          @Override
          public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
              
              
              security.tokenKeyAccess("permitAll()")//   /oauth/token_key公开
                  .checkTokenAccess("permitAll()")//   /oauth/check_token公开
                  .allowFormAuthenticationForClients();//允许表单认证
          }
      }
      
      

2.1.2 Authorization service test (five modes)

Authorization code mode

insert image description here

  1. The resource owner opens the client, and the client asks the resource owner for authorization, and it redirects the browser to the authorization server, and the client's identity information is attached to the redirection.

    访问链接
    http://127.0.0.1:3001/oauth/authorize?client_id=client1&response_type=code&scope=scope1&redirect_uri=http://www.baidu.com
    
    参数列表如下:
    client_id:客户端准入标识。
    response_type:授权码模式固定为code。
    scope:客户端权限。
    redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。
    
  2. The browser appears to authorize the authorization server page, and then the user agrees to authorize

  3. The authorization server sends the authorization code (AuthorizationCode) to the client via the browser (via redirect_uri).

    insert image description here

  4. The client asks the authorization server for an access_token with the authorization code, and the request is as follows:

    http://127.0.0.1:3001/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://www.baidu.com
    
    参数列表如下
    client_id:客户端准入标识。
    client_secret:客户端秘钥。
    grant_type:授权类型,填写authorization_code,表示授权码模式
    code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
    redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。
    
  5. The authorization server returns a token (access_token)

    insert image description here

This mode is the safest of the four modes. It is generally used when the client is a web server application or a third-party native app to call resource services. Because in this mode, the access_token will not pass through the browser or mobile app, but will be exchanged directly from the server, which minimizes the risk of token leakage.

Simplified mode

insert image description here

  1. The resource owner opens the client, and the client asks the resource owner for authorization, and it redirects the browser to the authorization server, and the client's identity information is attached to the redirection. like:

    http://127.0.0.1:3001/oauth/authorize?client_id=client1&response_type=token&scope=scope1&redirect_uri=http://www.baidu.com
    
  2. The browser appears to authorize the authorization server page, and then the user agrees to authorize

  3. The authorization server stores the authorization code and the token (access_token) in the fargment of the redirection uri in the form of Hash and sends it to the browser.

    insert image description here

Simplified mode is used for third-party single-page applications without a server side, because without a server side, authorization codes cannot be received

This is generally used for pure front-end projects, without background

password mode

insert image description here

  1. The resource owner sends the username and password to the client
  2. The client requests a token (access_token) from the authorization server with the user name and password of the resource owner

insert image description here

List of request parameters

  • client_id: Client access ID.
  • client_secret: client secret key.
  • grant_type: authorization type, fill in password to indicate the password mode
  • username: Username of the resource owner.
  • password: Resource owner password.

This mode is very simple, but it means that the user's sensitive information is directly leaked to the client, so this means that this mode can only be used when the client is developed by ourselves.

client mode

insert image description here

  1. The client sends its identity information to the authorization server and requests a token (access_token)

  2. After confirming that the client's identity is correct, send the token (access_token) to the client

    insert image description here

parameter list

  • client_id: Client access ID.
  • client_secret: client secret key.
  • grant_type: authorization type, fill in client_credentials to indicate the client mode

This mode is the most convenient but least secure mode. So this requires us to completely trust the client, and the client itself is also safe. Therefore, this mode is generally used to provide us with fully trusted server-side services. For example, the partner system is connected to pull a set of user information.

Token Refresh Test

  1. First use the authorization code mode to obtain the token and reflash

    insert image description here

  2. Then use refresh_token to request the interface to refresh access_token

    insert image description here

2.1.3 Construction of resource services

  • Create a project and depend on the same as authorizing the service

  • SecurityConfig configuration class

    /**
     * @author cVzhanshi
     * @create 2022-10-25 19:18
     */
    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
          
          
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
          
          
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().permitAll();
        }
    }
    
  • @EnableResourceServer is annotated to a @Configuration configuration class, and the configuration object ResourceServerConfigurer must be used for configuration (you can choose to inherit from ResourceServerConfigurerAdapter and override the method, the parameter is an instance of this object), the following are some configurable properties:

    ResourceServerSecurityConfigurer mainly includes:

    • tokenServices: An instance of the ResourceServerTokenServices class, used to implement token services.
    • tokenStore: An instance of the TokenStore class, specifying how to access the token, optional with tokenServices configuration
    • resourceId: The ID of this resource service, this attribute is optional, but it is recommended to set it and verify it in the authorization service.
    • Other extended attributes such as tokenExtractor token extractor is used to extract the token in the request.

    HttpSecurity configuration is similar to Spring Security:

    • The request matcher is used to set the resource path that needs to be protected. By default, it is all paths of the protected resource service.
    • Set access rules for protected resources through http.authorizeRequests()
    • Other custom permission protection rules are configured through HttpSecurity.
  • The @EnableResourceServer annotation automatically adds a filter chain of type OAuth2AuthenticationProcessingFilter

/**
 * @author cVzhanshi
 * @create 2022-10-25 19:18
 */
@Configuration
//开启oauth2,reousrce server模式
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    
    
        //远程token验证, 普通token必须远程校验
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        //配置去哪里验证token
        tokenServices.setCheckTokenEndpointUrl("http://127.0.0.1:3001/oauth/check_token");
        // 配置组件的clientid和密码,这个也是在auth中配置好的  资源服务相对于授权服务也是一个客户端所以也需要clientid
        tokenServices.setClientId("client2");
        tokenServices.setClientSecret("123456");
        resources
            //设置我这个resource的id, 这个在auth中配置
            .resourceId("resource1")
            .tokenServices(tokenServices)

            //这个貌似是配置要不要把token信息记录在session中
            .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
    
    
        http.csrf().disable()
            .authorizeRequests()

            //本项目所需要的授权范围  可以多个,这个scope是和auth服务那里的配置的scope做对比
            .antMatchers("/**").access("#oauth2.hasScope('scope1')")

            .and()

            //这个貌似是配置要不要把token信息记录在session中
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}
  • Write resource interface

    /**
     * @author cVzhanshi
     * @create 2022-10-25 19:17
     */
    @RestController
    public class TestController {
          
          
        @RequestMapping("user")
        public String user() {
          
          
            return "user";
        }
    
        //测试接口
        @RequestMapping("admin")
        // 需要权限
        @PreAuthorize("hasAnyAuthority('admin')")
        public String admin() {
          
          
            return "admin";
        }
    
        @RequestMapping("me")
        public Principal me(Principal principal) {
          
          
            return principal;
        }
    }
    

2.1.4 Resource service test

  • First use the password mode to get the token

    insert image description here

  • Then use token to access resources

    insert image description here

  • Resources with Insufficient Access Rights

    insert image description here

  • Use admin to log in to get token and then access permission resources

    insert image description here

2.2 Transform token into jwt_token

Project 2: Copy Project 1 for transformation

demo flow chart

insert image description here

2.2.1 Retrofit authorization service

  1. Modify TokenConfig

    /**
     * @author cVzhanshi
     * @create 2022-10-25 18:18
     */
    @Configuration
    public class TokenConfig {
          
          
    
        // 配置jwt的token转换器
        @Bean
        public JwtAccessTokenConverter tokenConverter(){
          
          
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            // 配置密钥
            converter.setSigningKey("jwt_token");
            return converter;
        }
    
        // 令牌的存储策略
        @Bean
        public TokenStore tokenStore(){
          
          
            // 把策略换成jwt_token Store
            return new JwtTokenStore(tokenConverter());
        }
    }
    
  2. Configure a token enhancement chain in AuthorizationServerConfig, and enhance the token to jwttoken

    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private JwtAccessTokenConverter tokenConverter;
    
    
    //配置token管理服务
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
          
          
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        // 客户端信息服务
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        // 是否产生刷新令牌
        defaultTokenServices.setSupportRefreshToken(true);
    
        //配置token的存储策略
        defaultTokenServices.setTokenStore(tokenStore);
        // 令牌的有效期
        defaultTokenServices.setAccessTokenValiditySeconds(300);
        // 刷新令牌的时间
        defaultTokenServices.setRefreshTokenValiditySeconds(1500);
    
        /**
        * 新增的
        **/
        // 配置一个token增强链  配置完之后token就增强为jwttoken
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenConverter));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
    
        return defaultTokenServices;
    }
    
  3. configuration complete

2.2.2 Test authorization service

  • Test password mode authentication and get token

    insert image description here

  • check token

    insert image description here

2.2.3 Transform resource service

First use jwttoken, then the resource service does not need to go to the authorization service to verify, and you can verify the jwttoken yourself

  • First add a TokenConfig configuration class to configure the jwt converter and strategy

    /**
         * @author cVzhanshi
         * @create 2022-10-25 18:18
         */
    @Configuration
    public class TokenConfig {
          
          
    
        // 配置jwt的token转换器
        @Bean
        public JwtAccessTokenConverter tokenConverter(){
          
          
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            // 配置密钥
            converter.setSigningKey("jwt_token");
            return converter;
        }
    
        // 令牌的存储策略
        @Bean
        public TokenStore tokenStore(){
          
          
            // 把策略换成jwt_token Store
            return new JwtTokenStore(tokenConverter());
        }
    }
    
  • Modify the configuration of ResourceServerConfig without remote verification

    @Autowired
    private TokenStore tokenStore;
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
          
          
        resources
            //设置我这个resource的id, 这个在auth中配置
            .resourceId("resource1")
            .tokenStore(tokenStore)
    
            //这个貌似是配置要不要把token信息记录在session中
            .stateless(true);
    }
    

2.2.4 Test resource service

  • First use the password mode to get the jwt_token (use the user user, you cannot access /admin without admin permission)

    insert image description here

  • Access authorized resources with token

    insert image description here

  • Access to resources without permission

    insert image description here

  • Use admin to get token to access admin resource

    insert image description here

2.3 Persist the token to the database

Project 3: Copy Project 1 for transformation

demo process

insert image description here

  • Token persistence only needs to modify the token storage strategy of the authorization service on a simple example

  • Import database dependencies and create database tables

    create table oauth_client_details (
        client_id VARCHAR(128) PRIMARY KEY,
        resource_ids VARCHAR(128),
        client_secret VARCHAR(128),
        scope VARCHAR(128),
        authorized_grant_types VARCHAR(128),
        web_server_redirect_uri VARCHAR(128),
        authorities VARCHAR(128),
        access_token_validity INTEGER,
        refresh_token_validity INTEGER,
        additional_information VARCHAR(4096),
        autoapprove VARCHAR(128)
    );
        create table oauth_client_token (
            token_id VARCHAR(128),
            token BLOB,
            authentication_id VARCHAR(128) PRIMARY KEY,
            user_name VARCHAR(128),
            client_id VARCHAR(128)
        );
        create table oauth_access_token (
            token_id VARCHAR(128),
            token BLOB,
            authentication_id VARCHAR(128) PRIMARY KEY,
            user_name VARCHAR(128),
            client_id VARCHAR(128),
            authentication BLOB,
            refresh_token VARCHAR(128)
        );
        create table oauth_refresh_token (
            token_id VARCHAR(128),
            token BLOB,
            authentication BLOB
        );
        create table oauth_code (
            code VARCHAR(128), authentication BLOB
        );
        create table oauth_approvals (
            userId VARCHAR(128),
            clientId VARCHAR(128),
            scope VARCHAR(128),
            status VARCHAR(10),
            expiresAt TIMESTAMP,
            lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        );
        -- customized oauth_client_details table
        create table ClientDetails (
            appId VARCHAR(128) PRIMARY KEY,
            resourceIds VARCHAR(128),
            appSecret VARCHAR(128),
            scope VARCHAR(128),
            grantTypes VARCHAR(128),
            redirectUrl VARCHAR(128),
            authorities VARCHAR(128),
            access_token_validity INTEGER,
            refresh_token_validity INTEGER,
            additionalInformation VARCHAR(4096),
            autoApproveScopes VARCHAR(128)
        );
        
        
         INSERT INTO `oauth_client_details` VALUES ('client1', 'resource1', '$2a$10$YEpRG0cFXz5yfC/lKoCHJ.83r/K3vaXLas5zCeLc.EJsQ/gL5Jvum', 'scope1,scope2', 'authorization_code,password,client_credentials,implicit,refresh_token', 'http://www.baidu.com', null, '300', '1500', null, 'false');
    
  • Modify the storage policy for configuration class tokens

    /**
     * @author cVzhanshi
     * @create 2022-10-25 18:18
     */
    @Configuration
    public class TokenConfig {
          
          
    
        @Autowired
        private DataSource dataSource;
    
        // 令牌的存储策略
        @Bean
        public TokenStore tokenStore(){
          
          
            return new JdbcTokenStore(dataSource);
        }
    }
    
    @Autowired
    private DataSource dataSource;
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
          
          
        JdbcClientDetailsService detailsService = new JdbcClientDetailsService(dataSource);
        detailsService.setPasswordEncoder(passwordEncoder);
        clients.withClientDetails(detailsService);
    }
    
    //设置授权码模式的授权码如何存取,暂时采用内存方式
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
          
           
        return new JdbcAuthorizationCodeServices(dataSource);
    }
    
  • configuration complete

test

  • Start authorization service, resource service

  • certified

    insert image description here

  • view database

    insert image description here

    already exists in the database

  • test resources

    insert image description here

2.4 Persist jwt_token to the database

Project Four: Copy Project Three for Transformation

Because jwttoken is not verified with authorization, jwt_token is not stored to the database

insert image description here

Modify Authorization Service

  • Modify TokenConfig

    /**
     * @author cVzhanshi
     * @create 2022-10-25 18:18
     */
    @Configuration
    public class TokenConfig {
          
          
    
        @Bean
        public JwtAccessTokenConverter tokenConverter(){
          
          
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey("jwt_token");
            return converter;
        }
    
        // 令牌的存储策略
        @Bean
        public TokenStore tokenStore(){
          
          
            return new JwtTokenStore(tokenConverter());
        }
    }
    
  • Modify the oauth configuration

    @Autowired
    private JwtAccessTokenConverter tokenConverter;
    
    
    //配置token管理服务
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
          
          
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        // 客户端信息服务
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        // 是否产生刷新令牌
        defaultTokenServices.setSupportRefreshToken(true);
    
        //配置token的存储策略
        defaultTokenServices.setTokenStore(tokenStore);
        // 令牌的有效期
        defaultTokenServices.setAccessTokenValiditySeconds(300);
        // 刷新令牌的时间
        defaultTokenServices.setRefreshTokenValiditySeconds(1500);
    
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenConverter));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
    
        return defaultTokenServices;
    }
    
  • Test Authorization Service

    insert image description here

  • view database

    insert image description here

Modify resource service

  • Copy the TokenConfig of the authorization service

  • Modify configuration class

    @Autowired
    private TokenStore tokenStore;
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
          
          
    
        resources
            //设置我这个resource的id, 这个在auth中配置
            .resourceId("resource1")
            .tokenStore(tokenStore)
    
            //这个貌似是配置要不要把token信息记录在session中
            .stateless(true);
    }
    
  • start test

    insert image description here

2.5 Using SpringOAuth2 in the SpringCloud microservice architecture

demo flow chart

insert image description here

or

insert image description here

2.5.1 Build eureka and zuul

Build eureka

  • Create a Spring Boot project

  • import dependencies

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
  • Modify the configuration file

    server:
      port: 2001
    spring:
      application:
        name: eureka
      cloud:
        client:
          ipAddress: 127.0.0.1
    eureka:
      instance:
        #基于ip配置
        prefer-ip-address: false
        #自定义id
        instance-id: ${
          
          spring.cloud.client.ipAddress}:${
          
          server.port}
        hostname: ${
          
          spring.cloud.client.ipAddress}
      client:
        serviceUrl:
          defaultZone: http://${
          
          eureka.instance.hostname}:${
          
          server.port}/eureka/
        #注册到其他eureka
        registerWithEureka: false
        #从其他eureka拉取信息
        fetchRegistry: false
      server:
        #自我保护
        enable-self-preservation: false
    
  • Add annotations to the main startup class@EnableEurekaServer

  • Start the project and visit localhost:2001, there is a page indicating success

Build Zuul

  • Create a Spring Boot project

  • import dependencies

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
  • Modify the configuration file

    server:
      port: 5001
    spring:
      application:
        name: zuul
      cloud:
        client:
          ipAddress: 127.0.0.1
    eureka:
      instance:
        prefer-ip-address: false
        instance-id: ${
          
          spring.cloud.client.ipAddress}:${
          
          server.port}
        hostname: ${
          
          spring.cloud.client.ipAddress}
      client:
        serviceUrl:
          #eurekaServers
          defaultZone: http://127.0.0.1:2001/eureka
    
  • Add another configuration file application.properties

    #zuul不传递cookie和head信息
    #方法1:这个设置是开启全局的cookie和head传递
    zuul.sensitive-headers=
    
  • Modifying the startup class

    @SpringCloudApplication
    @EnableZuulProxy
    public class ZuulApplication {
          
          
    
        public static void main(String[] args) {
          
          
            SpringApplication.run(ZuulApplication.class, args);
        }
    
    }
    
  • Start, check if zuul is registered in eureka

2.5.2 Build an authorization center

Authorization center copyproject twocarry out renovation

  • Modify the client details configuration and configure two clients

    // 配置客户端详细信息,可以配置多个
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
          
          
        // 存储在缓存中,后期可以选择存在数据库中
        clients.inMemory()
            // 设置客户端的id和密码
            .withClient(("client1"))
            .secret(passwordEncoder.encode(" "))
            // 给client一个id,这个在client的配置里要用的, 能使用的资源id
            .resourceIds("resource1")
    
            //允许的申请token的方式
            //authorization_code授权码模式,这个是标准模式
            //implicit简单模式,这个主要是给无后台的纯前端项目用的
            //password密码模式,直接拿用户的账号密码授权,不安全
            //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
            //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
            .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
            // 授权的范围,每个resource会设置自己的范围.
            .scopes("scope1")
            .autoApprove(true)
            .authorities("admin")
            .redirectUris("http://www.baidu.com")
    
            // 资源服务配置,因为资源服务也需要校验token,调用授权服务,所以需要配置客户端id
            .and()
            // 设置客户端的id和密码
            .withClient(("client2"))
            .secret(passwordEncoder.encode("123456"))
            // 给client一个id,这个在client的配置里要用的, 能使用的资源id
            .resourceIds("resource2")
    
            //允许的申请token的方式
            //authorization_code授权码模式,这个是标准模式
            //implicit简单模式,这个主要是给无后台的纯前端项目用的
            //password密码模式,直接拿用户的账号密码授权,不安全
            //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
            //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
            .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
            // 授权的范围,每个resource会设置自己的范围.
            .scopes("scope2")
            .autoApprove(true)
            .authorities("admin")
            .redirectUris("http://www.sogou.com");
    }
    
  • Add eureka client dependency

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  • Modify the configuration file

    # 应用名称
    server:
      port: 3001
    spring:
      application:
        name: auth
      cloud:
        client:
          ipAddress: 127.0.0.1
    eureka:
      instance:
        prefer-ip-address: false
        instance-id: ${
          
          spring.cloud.client.ipAddress}:${
          
          server.port}
        hostname: ${
          
          spring.cloud.client.ipAddress}
      client:
        serviceUrl:
          #eurekaServers
          defaultZone: http://127.0.0.1:2001/eureka
    
  • Add @EnableEurekaClientannotations to the main startup class

  • Start, check if eureka is registered

Test authorization center through zuul

  • First obtain the code through browser access authorization

    http://127.0.0.1:5001/auth/oauth/authorize?client_id=client1&response_type=code&scope=scope1&redirect_uri=http://www.baidu.com

  • Get the code and continue to access the authorization center through zuul to get the token

  • Get the token and use zuul to access the authorization center to verify the token

2.5.3 Building a resource center

copy the sameproject twoof resource centers are being revamped

  • Add eureka client dependency

  • Field eureka client annotations on the main startup class

  • Modify the configuration file

    # 应用名称
    server:
      port: 4001
    spring:
      application:
        name: source1
      cloud:
        client:
          ipAddress: 127.0.0.1
    eureka:
      instance:
        prefer-ip-address: false
        instance-id: ${
          
          spring.cloud.client.ipAddress}:${
          
          server.port}
        hostname: ${
          
          spring.cloud.client.ipAddress}
      client:
        serviceUrl:
          #eurekaServers
          defaultZone: http://127.0.0.1:2001/eureka
    
  • Transformation is complete, start

  • Copy the first resource center and rename it to source2

  • Modify the port number

  • Modify resource id, scope2

  • Start a second resource center

2.5.4 Testing

  • First obtain the code through browser access authorization and use scope1

    http://127.0.0.1:5001/auth/oauth/authorize?client_id=client1&response_type=code&scope=scope1&redirect_uri=http://www.baidu.com

  • Get the code and use client1 to access the authorization center through zuul to get the token

  • Get the token and access the authorization center source1 through zuul to access resources

  • Get the token and access the authorization center source2 through zuul to access resources

2.5.6 Transformation into unified authorization in zuul

Configure a filter for zuul

/**
 * @author cVzhanshi
 * @create 2022-11-02 16:56
 */
@Component
public class ScopeFilter extends ZuulFilter {
    
    
    @Override
    public String filterType() {
    
    
        return "pre";
    }

    @Override
    public int filterOrder() {
    
    
        return 0;
    }

    @Override
    public boolean shouldFilter() {
    
    
        return true;
    }

    @Override
    public Object run() throws ZuulException {
    
    
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String requestURI = request.getRequestURI();
        // 如果是访问授权中心,直接放行
        if(requestURI.startsWith("/auth")){
    
    
            return null;
        }

        String token = request.getHeader("Authorization");
        token = token.split("\\.")[1];
        byte[] bytes = Base64.getUrlDecoder().decode(token);
        try {
    
    
            token = new String(bytes,"UTF-8");
        } catch (UnsupportedEncodingException e) {
    
    
            e.printStackTrace();
        }
        JSONObject jsonObject = JSONObject.parseObject(token);
        List<String> scope = jsonObject.getJSONArray("scope").toJavaList(String.class);
        // 判断scope
        if(requestURI.startsWith("/source1") && scope.contains("scope1")){
    
    
            return null;
        }
        if(requestURI.startsWith("/source2") && scope.contains("scope2")){
    
    
            return null;
        }
        // 不满足报403
        currentContext.setSendZuulResponse(false);
        currentContext.setResponseStatusCode(403);
        return null;
    }
}

Guess you like

Origin blog.csdn.net/qq_45408390/article/details/127730870