Microservice architecture | 7.1 security authentication based on OAuth2

Posted by petrb on Thu, 03 Feb 2022 06:29:38 +0100

preface

<Spring Microservices in Action>
Principle and practice of Spring Cloud Alibaba microservice
"Spring cloud framework development tutorial in Silicon Valley of station B" Zhou Yang

OAuth2 is a token based security authentication and authorization framework. It allows users to authenticate using third-party authentication services. If the user successfully authenticates, a token is presented, which must be sent with each request. Then, the authentication service can confirm the token;

1. Oauth2 basic knowledge

1.1 four components of security

  • Protected resource: Resource Server, the resource that developers want to protect (such as a micro service), which needs to be ensured that only authenticated users with appropriate authorization can access it;
  • Resource Owner: Resource Owner, which defines which applications can call their services, which users can access the service, and what they can do with the service. Each application registered by the Resource Owner will be given an application name that identifies the application along with the application key. The combination of application name and key is part of the credentials passed when verifying the OAuth2 token;
  • Application: Client, which is the application that invokes the service on behalf of the user. After all, users rarely invoke services directly. Instead, they rely on applications to work for them.
  • OAuth2 authentication server: Authorization Server. OAuth2 authentication server is the intermediary between the application and the service being used. OAuth2 authentication server allows users to authenticate themselves without passing user credentials to each service called by the application on behalf of the user;

1.2 working principle of oauth2

  • The third-party client applies for authentication request from the resource owner (user);
  • [key] the user agrees to the request and returns a license;
  • The client applies to the authentication server for an authentication Token according to the license;
  • The client applies for relevant resources from the resource server according to the authentication token;

1.3 four types of authorization in oauth2 specification

  • password;
  • client credential;;
  • Authorization code;
  • Implicit;

1.4 advantages of oauth2

  • Allow developers to easily integrate with third-party cloud service providers and use these services for user authentication and authorization without constantly passing user credentials to third-party services;

1.5 OAuth2 core principles

  • First, there is an OAuth2 authentication server, which is used to create and manage OAuth2 access tokens;
  • Next, add a note on the protected resource main program class: @EnableResourceServer, which will enforce a filter that intercepts all incoming calls to the service, checks whether there is a OAuth2 access token in the HTTP header of the incoming call, and then calls security.. oauth2. resource. The callback URL defined in userinfouri tells the client to interact with the oauth2 authentication server to check whether the token is valid;
  • Once the token is known to be valid, the "EnableResourceServer" annotation will also apply any access control rules to control who can access the service;

1.6 JSON Web Token


2. Establish OAuth2 server

  • The authentication service will verify user credentials and issue tokens;
  • Whenever a user attempts to access a service protected by, for example, the authentication service will confirm whether the OAuth2 token has been issued by it and has not expired;

2.1 introduction of POM XML dependency file

<!--security General security library-->
<dependency> 
	<groupid>org.springframework.cloud</groupid> 
	<artifactid>spring-cloud-security</artifactid> 
</dependency> 
<!--oauth2.0-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

2.2 add notes on the main program class

  • @Enable authorization server: this service will be used as OAuth2 service;
  • @EnableResourceServer: indicates that the service is a protected resource; (this note is explained in detail in 3.3)

2.3 add endpoint of protected object

Under the controller package;

  • The endpoint will be mapped to the / auth/user endpoint. When the protected service calls / auth/user, it will confirm the OAuth2 access token and retrieve the role assigned by the European tiger service on the back of the sender;
/**
 * User information verification
 * Called by the protected service, confirm the OAuth2 access token and retrieve the role assigned by the user accessing the protected service
 * @param OAuth2Authentication information
 * @return User information
 */
@RequestMapping(value = { "/user" }, produces = "application/json")
public Map<String, Object> user(OAuth2Authentication user) {
    Map<String, Object> userInfo = new HashMap<>();
    userInfo.put("user", user.getUserAuthentication().getPrincipal());
    userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
    return userInfo;
}

2.4 define which applications can use services

Under config package;

  • ClientDetailsServiceConfigurer supports two types of storage: memory storage and JDBC storage, as shown in the following points:

2.4.1 using JDBC storage

  • OAuth2Config class:
@Configuration
//Inheriting the AuthorizationServerConfigurerAdapter class
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    //Define which clients will register with the service
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //JDBC storage:
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT); //Set up our custom sql lookup statement
        clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT); //Set up our custom sql lookup statement
        clients.withClientDetails(clientDetailsService); //Find out the data from jdbc to store
    }
    
    @Override
    //Use the default authentication manager and user details service provided by Spring
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
    }
}
  • SecurityConstants class: it stores the SQL query statements mentioned above;
public interface SecurityConstants {
    /**
     * sys_oauth_client_details Table fields, excluding client_id,client_secret
     */
    String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, "
            + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
            + "refresh_token_validity, additional_information, autoapprove";

    /**
     *JdbcClientDetailsService Query statement
     */
    String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";

    /**
     * Default query statement
     */
    String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";

    /**
     * By condition client_id query
     */
    String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
}

2.4.2 using memory storage

@Configuration
//Inheriting the AuthorizationServerConfigurerAdapter class
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    //Define which clients will register with the service
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("eagleeye")  //name
                .secret("thisissecret")  //secret key
                .authorizedGrantTypes("refresh_token", "password", "client_credentials")  //List of authorization types
                .scopes("webclient", "mobileclient");  //The range of operations that can be performed when obtaining an access token
    }

    @Override
    //Use the default authentication manager and user details service provided by Spring
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
    }
}

2.5 define user ID, password and role for application

Under config package:

  • User information can be stored and retrieved from memory data storage, relational database supporting JDBC or LDAP server;
@Configuration
@EnableWebSecurity
//WebSecurityConfigurerAdapter that extends core Spring Security
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    //Used to process validation
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //Processing returned user information
    @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return super.userDetailsServiceBean();
    }
    
    //The configure() method defines users, passwords, and roles
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("john.carnell").password("password1").roles("USER")
                .and()
                .withUser("william.woodward").password("password2").roles("USER", "ADMIN");
    }
}
  • In the above example, John Carnell USER owns USER user;
  • william.woodward has ADMIN users;

2.6 authenticate users by sending POST requests

  • Send: Post http://localhost:8901/auth/oauth/token ;
  • The application name, key, user ID and password are carried in the request body of POST, which can simulate the user to obtain OAuth2 token;

3. Use OAuth2 to establish and protect service resources

  • Creating and managing OAuth2 access token is the responsibility of OAuth2 server;
  • Define which user roles are authorized to perform which operations at a single service level;

3.1 introduction of POM XML dependency file

<!--security General security library-->
<dependency> 
	<groupid>org.springframework.cloud</groupid> 
	<artifactid>spring-cloud-security</artifactid> 
</dependency> 
<!--oauth2.0-->
<dependency>
	<groupId>org.springframework.security.oauth</groupId>
	<artifactId>spring-security-oauth2</artifactId>
</dependency>

3.2 add bootstrap YML profile

security:
  oauth2:
   resource:
      userInfoUri: http://localhost:8901/auth/user
  • Add a callback URL here. When the client accesses the protected service, the protected service will call the / auth/user endpoint to check whether the access token is effective to the OAuth2 server;

3.3 add comments on the main program class

  • @EnableResourceServer: indicates that the service is a protected resource;
  • The annotation will enforce a filter that intercepts all incoming calls to the service, checks whether there is a OAuth2 access token in the HTTP header of the incoming call, and then calls security.. oauth2. resource. The callback URL defined in userinfouri to check whether the token is valid;
  • Once the token is known to be valid, the "EnableResourceServer" annotation will also apply any access control rules to control who can access the service;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //Circuit breaker
@EnableResourceServer //Represents a protected resource
public class Application {
    //Injecting a filter will intercept all incoming calls to the service
    @Bean
    public Filter userContextFilter() {
        UserContextFilter userContextFilter = new UserContextFilter();
        return userContextFilter;
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3.4 define access control rules

Under config package or security package;

  • To define access control rules, you need to extend the ResourceServerConfigurerAdapter class to override the configure() method;
  • There are many definition methods. Here are two common definition examples:

3.4.1 user protection service through authentication

  • That is, it can only be accessed by authenticated users;
//This annotation must be used and the ResourceServerConfigurerAdapter class needs to be extended
@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    //The access rules are defined in the configure() method and configured through the HttpSecurity object passed in to the method
    @Override
    public void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests().anyRequest().authenticated();
    }
}
  • anyRequest().authenticated() means that it needs to be accessed by authenticated users;

3.4.2 protect services through specific roles

  • Restrict that only ADMIN users can call the DELETE method of the service;
@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception{
        http
        .authorizeRequests()
          .antMatchers(HttpMethod.DELETE, "/v1/xxxservices/**")  //The developer of the operation Department restricts the call to the protected URL and HTTP DELETE verb
          .hasRole("ADMIN")  //List of roles allowed to access
          .anyRequest()
          .authenticated();
    }
}
  • anyRequest().authenticated() means that it still needs to be accessed by authenticated users;
  • In combination with the example of "2.5 defining user ID, password and role for application" in this article, John Carnell user will be denied access to the resource and will use William Woodward admin users will access resources through;

4. Propagate OAuth2 access token in upstream and downstream services

  • The user has verified with the OAuth2 server and called the EagleEye Web client;
  • EagleEye Web application (OAuth2 server) will add OAuth2 access token through HTTP Authorization;
  • Zuul will find the license service endpoint, and then forward the call to the server of one of the license services;
  • The service gateway needs to copy the HTTP header Authorization from the incoming call;
  • The protected service uses the OAuth2 server to confirm the token;

4.1 configure the blacklist of service gateway

In Zuul's application In the configuration file of YML;

  • Because in the whole verification process, we need to pass the HTTP header Authorization to the upstream and downstream for permission authentication;

  • However, by default, Zuul will not forward sensitive HTTP headers (such as cookies, set cokkie and Authorization) to downstream services;

  • Zuul's blacklist release Authorization needs to be configured;

    zuul:
      sensitiveHeaders: Cookie , Set-Cookie
    
  • The above configuration means that cookies are intercepted, set cookies are transmitted downstream, and Authorization will be released;

4.2 modify upstream service business code

  • The business code needs to ensure that the HTTP header Authorization is injected into the upstream and downstream of the service;

4.2.1 downstream services

  • Downstream services here are protected services;
  • The construction method is the same as that in 3. Using OAuth2 to establish and protect service resources

4.2.2 expose OAuth2RestTemplate class in upstream service

You can create classes on the main program class or in the package where the main program is located and its sub packages;

  • This class can be automatically assembled to call another OAuth2 protected service;

    @Bean
    public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, OAuth2ProtectedResourceDetails details) {
        return new OAuth2RestTemplate(details, oauth2ClientContext);
    }
    

4.2.3 use OAuth2RestTemplate in upstream service to propagate OAuth2 access token

  • Automatically assemble OAuth2RestTemplate;
@Component
public class OrganizationRestTemplateClient {
    //OAuth2RestTemplate is an enhanced alternative to the standard RestTemplate and can handle OAuth2 access tokens
    @Autowired
    OAuth2RestTemplate restTemplate;

    public Organization getOrganization(String organizationId){
        //The way to invoke the organization service is exactly the same as the standard RestTemplate
        ResponseEntity<Organization> restExchange =
                restTemplate.exchange(
                        "http://zuulserver:5555/api/organization/v1/organizations/{organizationId}",
                        HttpMethod.GET,
                        null, Organization.class, organizationId);
        return restExchange.getBody();
    }
}

last

Newcomer production, if there are mistakes, welcome to point out, thank you very much! Welcome to the official account and share some more everyday things. If you need to reprint, please mark the source!

Topics: Distribution Spring Cloud oauth2 microservice