1. Introduction

In the previous chapter, the basic theoretical knowledge and application scenarios of Spring Security Oauth2 have been introduced. In this chapter, annotations are introduced to realize unified authentication and authorization of micro services through Oauth2 in Spring Cloud.

1.1 Solutions

The following solutions are mainly used in this article:

  • In the authentication scheme based on stateless token (JWT), the server does not need to save the user login status.
  • Based on Spring Security framework + OAuth2 protocol;

Why JWT? To avoid invoking the authentication and authorization service remotely on each request, the authentication and authorization service validates once and returns JWT. The JWT returned contains all information about the user, including permission information.

1.2 Case engineering architecture

Three projects:

  • Eureka-server: registration service center, port 8888. (This has been done in previous articles and will not be demonstrated in this article)
  • Auth-server: is responsible for authorization. For authorization, users need to provide clientId and password of the client, as well as username and password of the authorized user. When this information is ready, auth-service returns the JWT, which contains the user’s basic information and permission point information, encrypted with RSA.
  • Auth-client: authentication client, common dependency. All other resource services are introduced
  • User-server: as a resource service, its resources are protected and can be accessed only by corresponding permissions. After the user-server service obtains the JWT requested by the user, it decrypts the JWT through the public key to obtain the information about the user corresponding to the JWT and the user’s permission information, and then determines whether the user has the permission to access the resource
  • The order – server: same as above

Engineering Structure Diagram:

Engineering dependencies:

2. Construct auth-server project

2.1 Adding maven dependencies

Create a new Auth-Server module and add the following dependencies:


      
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-oauth2</artifactId>
        <groupId>com.hxmec</groupId>
        <version>0.0.1 - the SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>auth-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.8.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
Copy the code

2.2 Creating a Data table

The table required by the project mainly has the following:

It is used to store client information, access tokens issued by the OAUTH2 protocol, and refresh tokens. For oauth2 tables, see andaily.com/spring-oaut… . The Access tokens in the demo project are stored as JWT, so only the OAUTH_client_Details table and the SYS_USER user information table are actually used. If you switch to JDBC storage, you will need to use additional tables.

The construction sentences are as follows:

-- Client application registration details
create table oauth_client_details (
  client_id VARCHAR(256) PRIMARY KEY, - Account of the client application
  resource_ids VARCHAR(256),		-- A list of resource servers accessible to the client application,(empty indicates that all resource servers are accessible)
  client_secret VARCHAR(256),	-- Password of the client application
  scope VARCHAR(256),	Get add delete update list of all permissions that the resource server has
  authorized_grant_types VARCHAR(256), -- List of authorization code modes supported by the client
  web_server_redirect_uri VARCHAR(256), Authorization code mode: URI for redirection after an authorization code is applied.
  authorities VARCHAR(256),
  access_token_validity INTEGER.- Set the validity period for issuing tokens
  refresh_token_validity INTEGER.-- Validity period for issuing refresh_token (if not set, refresh_token will not be issued simultaneously)
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(256)     -- Set this parameter to true to enable automatic authorization in authorization code mode
);

create table oauth_client_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256));-- Stores the issued token
create table oauth_access_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256),
  authentication BLOB,
  refresh_token VARCHAR(256));create table oauth_refresh_token (
  token_id VARCHAR(256),
  token BLOB,
  authentication BLOB
);

- In authorization code mode, stores the issued authorization codes
create table oauth_code (
  code VARCHAR(256), authentication BLOB
);

create table oauth_approvals (
	userId VARCHAR(256),
	clientId VARCHAR(256),
	scope VARCHAR(256),
	status VARCHAR(10),
	expiresAt DATETIME,
	lastModifiedAt DATETIME
);

CREATE TABLE `sys_user` (
  `id` bigint(32) NOT NULL,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(200) DEFAULT NULL,
  `enable_` tinyint(1) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `mobile` varchar(20) DEFAULT NULL,
  `del_flag` tinyint(1) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `create_user` bigint(32) DEFAULT NULL,
  `modified_time` datetime DEFAULT NULL,
  `modified_user` bigint(32) DEFAULT NULL.PRIMARY KEY (`id`)
);

INSERT INTO `oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('app'.NULL.'$2a$10$JWSnLszKQANW7OF3p2b8IuIQXTVD8OUN//Q4l/sZGmzyaLEWnC5/u'.'server'.'password,authorization_code,refresh_token,client_credentials'.NULL.NULL.60000.300.NULL.NULL);
INSERT INTO `oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('order'.NULL.'$2a$10$JWSnLszKQANW7OF3p2b8IuIQXTVD8OUN//Q4l/sZGmzyaLEWnC5/u'.'server'.'password,authorization_code,refresh_token,client_credentials'.NULL.NULL.60000.300.NULL.NULL);
INSERT INTO `oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('user'.NULL.'$2a$10$JWSnLszKQANW7OF3p2b8IuIQXTVD8OUN//Q4l/sZGmzyaLEWnC5/u'.'server'.'password,authorization_code,refresh_token,client_credentials'.NULL.NULL.60000.300.NULL.NULL);

INSERT INTO `sys_user` VALUES (1282941563927805954.'trazen'.'$2a$10$JWSnLszKQANW7OF3p2b8IuIQXTVD8OUN//Q4l/sZGmzyaLEWnC5/u'.NULL.'[email protected]'.'18559756159'.0.'the 2020-07-14 15:34:39'.NULL.'the 2020-07-14 15:40:45'.NULL);
Copy the code

2.3 Adding The Configuration

The bootstrap.yml configuration is as follows

server:
  port: 8889
spring:
  application:
    name: auth-server
logging:
  pattern:
    console: '%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} - %msg%n'
  config: classpath:logback-spring.xml
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/
Copy the code

Application. Yml configuration is as follows:

# Mybatis - plus configuration
mybatis-plus:
  Multiple directories are separated by commas or semicolons
  mapper-locations: classpath:mapper/*.xml
  The following configurations have default values
  global-config:
    db-config:
      # primary key type AUTO:" database ID increment "INPUT:" user INPUT ID",ID_WORKER:" globally unique ID (number type unique ID)", UUID:" globally unique ID UUID";
      id-type: ASSIGN_ID
  configuration:
    Enable automatic camel name mapping: a similar mapping from database column names to Java attribute camel names
    map-underscore-to-camel-case: true
    True: null is returned if the map is null. False: hidden if the map is null
    call-setters-on-nulls: true

spring:
  datasource:
    #driver-class-name: com.mysql.cj.jdbc.Driver
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      url: JDBC: p6spy: mysql: / / 192.168.29.188:3306 / sc_oauth2? useUnicode=true&characterEncoding=utf-8
      username: root
      password: root
      # initial connection number
      initial-size: 10
      # Maximum number of connection pools
      max-active: 100
      Minimum number of connection pools
      min-idle: 10
      Set the connection wait timeout
      max-wait: 60000
      Turn on PSCache and specify the size of PSCache on each connection
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      Configure how often to detect idle connections that need to be closed, in milliseconds
      timeBetweenEvictionRunsMillis: 60000
      Set the minimum time for a connection to live in the pool in milliseconds
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      stat-view-servlet:
        enabled: false
Copy the code

2.4 Customizing UserDetailsService

  • When a client requests to issue tokens, it actually accesses a TokenEndPoint that Spring Security OAuth2 has encapsulated. The interface eventually calls the UserDetailsService method to obtain user information based on the requested account information. Note that the user information is only retrieved from the Mysql table. The verification of the account information is left to Spring Security.
  • When a user requests to obtain the token, the UserDetailsService class loads the user information according to the username of the user and returns. After the operation of verifying the user account password, the AuthenticationManager invokes an authenticator to verify the user account password.

Interface MyUserDetailsService. Java

/** * Inherits UserDetailsService interface *@author  Trazen
 * @date2020/7/14 15:43 * /
public interface MyUserDetailsService extends UserDetailsService {}Copy the code

The implementation class MyUserDetailsServiceImpl

/** * User-defined UserDetailsService *@author  Trazen
 * @date2020/7/14 15:43 * /
@Primary
@Service
@AllArgsConstructor
public class MyUserDetailsServiceImpl implements MyUserDetailsService {

    private final SysUserMapper sysUserMapper;

    @Override
    public AuthUserDetail loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getUsername,username);
        SysUser sysUser = sysUserMapper.selectOne(wrapper);
        if(sysUser == null) {throw new UsernameNotFoundException("User does not exist");
        }else {
            returnUserDetailConverter.convert(sysUser); }}public static class UserDetailConverter{
        static AuthUserDetail convert(SysUser user){
            return newAuthUserDetail(user); }}}Copy the code

2.5 Authentication and Authorization Configuration

Certification center of the spring security configuration is as follows: Oauth2WebSecurityConfig. Java

/** *@author  Trazen
 * @date2020/7/14 16:00 * /
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class Oauth2WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final MyUserDetailsService myUserDetailsService;

    /** * Re-injects the authentication manager *@return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean(a) throws Exception {
        return super.authenticationManagerBean();
    }


    /** * BCryptPasswordEncoder *@return* /
    @Bean
    public PasswordEncoder passwordEncoder(a) {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Load user information in a customized way
        auth.userDetailsService(myUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .csrf().disable() .httpBasic(); }}Copy the code

2.6 Extend TokenEnhancer

In the previous article of Spring Security Oauth2, it can be seen that the default interface that obtains tokens only obtains accessToken,refreshToken and other information. In a real-world scenario where we might need to add some user-specific information (such as user ID, user name, user organization ID, etc.), the default returned information would not be sufficient, so we could implement the Enhance method by adding and rewriting TokenEnhancer.

MyTokenEnhancer.java

/** * Function description: Enhances the information carried by the issued token *@author  Trazen
 * @date2020/7/14 16:07 * /
public class MyTokenEnhancer implements TokenEnhancer {


    /** * Client mode */
    private final static String CLIENT_CREDENTIALS = "client_credentials";

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {

        // Client mode is not enhanced
        if (CLIENT_CREDENTIALS.equals(authentication.getOAuth2Request().getGrantType())) {
            return accessToken;
        }
        // Get the field to be enhanced with
        AuthUserDetail authUserDetail = (AuthUserDetail) authentication.getPrincipal();


        final Map<String, Object> additionalInfo = new HashMap<>(3);

        // Add the fields carried by the token
        additionalInfo.put("id", authUserDetail.getSysUser().getId());
        additionalInfo.put("username", authUserDetail.getSysUser().getUsername());
        additionalInfo.put("email", authUserDetail.getSysUser().getEmail());
        additionalInfo.put("mobile", authUserDetail.getSysUser().getMobile());

        DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
        token.setAdditionalInformation(additionalInfo);
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        returnaccessToken; }}Copy the code

2.7 Authentication and Authorization Server Configuration

Configure the OAuth2 components of the authentication authority, such as the custom TokenEnhancer, the custom UserDetailService, the reconfigured AuthenticationManager, and the data source from which the database tables were created earlier.

Certification authorization server code below Oauth2AuthServerConfig. Java

/** * Oauth2 Authentication server configuration *@author  Trazen
 * @date2020/7/14 tightly with * /
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class Oauth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    private final MyUserDetailsService myUserDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        MyClientDetailsService clientDetailsService = new MyClientDetailsService(dataSource);
        clients.withClientDetails(clientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // Set the enhanced token to the enhanced chain
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), jwtAccessTokenConverter()));

        endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager)
                // // is used in the request to refresh the token
                .userDetailsService(myUserDetailsService)
                .tokenEnhancer(enhancerChain);
    }


    /** * Change the policy for storing tokens, default is memory policy, change it to JWT *@return* /
    @Bean
    public TokenStore tokenStore(a) {
        // Token based authentication
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(a){
        JwtAccessTokenConverter jat = new JwtAccessTokenConverter();
        // JWT uses this key for signature, and the token verification service also uses this key for token verification
        jat.setSigningKey("hxmec");
        return jat;
    }


    /** * Add a custom token enhancer to issue tokens with additional information because the default fields for issuing tokens are username and ROLE *@return* /
    @Bean
    public TokenEnhancer customTokenEnhancer(a) {
        // Custom implementation
        return newMyTokenEnhancer(); }}Copy the code

2.8 Authentication and Authorization Server Testing

Start eureka-Server, Auth-Server project, and perform the access Token test. Test data using the data created in the previous script (password encryption using BCryptPasswordEncoder mode). Oauth_client_details Test data: client_id:app client_secret:123456

Sys_user Test data is as follows: username: Trazen Password :123456

The test procedure is as follows: Obtain the endpoint address of the token: http://{ip}:{port}/oauth/token

The returned access_tokenken can be found on the JWT website [JWT. IO / # decoded – jw…Parse the address.

3. Construct auth-client project

This project mainly encapsulates resource server-related configurations as common dependencies.

3.1 Adding maven dependencies


      
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-oauth2</artifactId>
        <groupId>com.hxmec</groupId>
        <version>0.0.1 - the SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>auth-client</artifactId>

    <dependencies>
        <! -- security ouath2 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <! -- Import config file handler, config file binding will be prompted -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.3.3. RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Copy the code

3.2 Rewriting the Token parser

The code is as follows: MyUserAuthenticationConverter. Java

/** * function description: rewrite the token parser, according to the checkToken result to convert user information *@author  Trazen
 * @date2020/7/14 "* /
public class MyUserAuthenticationConverter implements UserAuthenticationConverter {

    private static final String N_A = "N/A";


    @Override
    publicMap<String, ? > convertUserAuthentication(Authentication userAuthentication) {return null;
    }

    @Override
    public Authentication extractAuthentication(Map
       
         map)
       ,> {
        if(! map.containsKey(USERNAME)){return null;
        }else{
            CurrentUser user = CurrentUser.builder()
                    .id((Long) map.get("id"))
                    .username((String) map.get(USERNAME))
                    .email((String) map.get("email"))
                    .mobile((String) map.get("mobile"))
                    .build();
            // Format permission information if there is permission information
            if (map.containsKey("authorities") && map.get("authorities") != null){
                Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
                user.setAuthorities(authorities);
                return new UsernamePasswordAuthenticationToken(user, N_A,authorities);
            }else {

                return new UsernamePasswordAuthenticationToken(user, N_A,null); }}}privateCollection<? extends GrantedAuthority> getAuthorities(Map<String, ? > map) { Object authorities = map.get(AUTHORITIES);if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                    .collectionToCommaDelimitedString((Collection<?>) authorities));
        }else if (authorities == null){

        }
        throw new IllegalArgumentException("Authorities must be either a String or a Collection"); }}Copy the code

3.3 Resolve the Token transfer problem when Feign interface invokes tokens

The main code (other reference project source code) as follows: MyFeignClientInterceptor. Java

/** * Extension OAuth2FeignRequestInterceptor * if special scenarios such as scheduling tasks call Feign interface * can be specified by filtering the headers, such as head, prevent accessTokenContextRelay. CopyToken error * ()@author  Trazen
 * @date2020/7/17 21:17 * /
public class MyFeignClientInterceptor extends OAuth2FeignRequestInterceptor {

    private final OAuth2ClientContext oAuth2ClientContext;
    private final AccessTokenContextRelay accessTokenContextRelay;

    /**
     * Default constructor which uses the provided OAuth2ClientContext and Bearer tokens
     * within Authorization header
     *
     * @param oAuth2ClientContext     provided context
     * @param resource                type of resource to be accessed
     * @param accessTokenContextRelay
     */
    public MyFeignClientInterceptor(OAuth2ClientContext oAuth2ClientContext , OAuth2ProtectedResourceDetails resource, AccessTokenContextRelay accessTokenContextRelay) {
        super(oAuth2ClientContext, resource);
        this.oAuth2ClientContext = oAuth2ClientContext;
        this.accessTokenContextRelay = accessTokenContextRelay;
    }


    /** * Create a template with the header of provided name and extracted extract * 1. If a non-Web request is used, the header is different from *. 2. Restore the request token * * according to authentication@param template
     */
    @Override
    public void apply(RequestTemplate template) {
        accessTokenContextRelay.copyToken();
        if(oAuth2ClientContext ! =null&& oAuth2ClientContext.getAccessToken() ! =null) {
            super.apply(template); }}}Copy the code

3.4 Configuring resource Servers

The code is as follows: MyResourceServerConfig. Java

/** * Function description: Resource server configuration *@author  Trazen
 * @date2020/7/14 21:37 * /
@Slf4j
@Configuration
@EnableResourceServer
@AllArgsConstructor
@ComponentScan("com.hxmec.auth")
@EnableConfigurationProperties(AuthClientProperties.class)
public class MyResourceServerConfig extends ResourceServerConfigurerAdapter {

    private final MyAuthenticationEntryPoint baseAuthenticationEntryPoint;

    private final AuthClientProperties authClientProperties;

    private final RestTemplate lbRestTemplate;

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

        // Release swagger UI
        http.authorizeRequests().antMatchers(
                "/v2/api-docs"."/swagger-resources/configuration/ui"."/swagger-resources"."/swagger-resources/configuration/security"."/swagger-ui.html"."/webjars/**"."/api/**/v2/api-docs")
                .permitAll();

        // Configure URL access based on the customization
        if(authClientProperties.getIgnoreUrls() ! =null) {for(String url: authClientProperties.getIgnoreUrls()){ http.authorizeRequests().antMatchers(url).permitAll(); }}// All other requests require token access
        http.authorizeRequests().anyRequest().authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        if(authClientProperties.getResourceId() ! =null) {
            resources.resourceId(authClientProperties.getResourceId());
        }

        // The signature key must be consistent with that of the authentication center
        if (authClientProperties.getSigningKey() == null) {
            log.info("SigningKey is null cant not decode token.......");
        }

        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(new MyUserAuthenticationConverter());

        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // Set the key to parse JWT
        converter.setSigningKey(authClientProperties.getSigningKey());
        converter.setVerifier(new MacSigner(authClientProperties.getSigningKey()));

        MyTokenServices tokenServices = new MyTokenServices();

        // Inject three dependency objects into CustomTokenServices
        // Set the token storage policy
        tokenServices.setTokenStore(newJwtTokenStore(converter)); tokenServices.setJwtAccessTokenConverter(converter); tokenServices.setDefaultAccessTokenConverter(accessTokenConverter); tokenServices.setRestTemplate(lbRestTemplate); resources.tokenServices(tokenServices) .authenticationEntryPoint(baseAuthenticationEntryPoint); }}Copy the code

4. Construct the user-server/ ORder-server project for resource server application

Resource server applications are built in the same steps, and order-Server mainly demonstrates the function of passing tokens through feign interface calls.

4.1 Adding Maven dependencies


      
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-oauth2</artifactId>
        <groupId>com.hxmec</groupId>
        <version>0.0.1 - the SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.hxmec</groupId>
            <artifactId>auth-client</artifactId>
            <version>0.0.1 - the SNAPSHOT</version>
        </dependency>
        <! -- SpringRetry framework dependencies -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Copy the code

4.2 Creating a Spring Boot Boot Class

UserServerApplication.java

/** * User Server startup class * @author Trazen * @date 2020/7/15 9:55 */ @springBootApplication @enableDiscoveryClient @EnableFeignClients @EnableGlobalMethodSecurity(prePostEnabled = true) public class UserServerApplication { public static void main(String[] args) { SpringApplication.run(UserServerApplication.class, args); }}Copy the code

4.3 Adding The Configuration

The bootstrap. Yml configuration

server:
  port: 8890
spring:
  application:
    name: user-server
  cloud:
    loadbalancer:
      retry:
        Enable the retry mechanism
        enabled: true
logging:
  pattern:
    console: '%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} - %msg%n'
  config: classpath:logback-spring.xml
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/
Copy the code

Application. Yml configuration

hx:
  oauth2:
    client:
      # JWT key
      signingKey: hxmec
      resourceId: ${spring.application.name}
      # pass url
      ignoreUrls:
        - /oauth/**
        - /user/**



#ribbon global configuration
ribbon:
  # Timeout for processing requests, in ms, default 1000
  ReadTimeout: 3000
  Timeout for establishing a connection, in ms, default 1000
  ConnectTimeout: 3000

feign:
  compression:
    request:
      # Whether to enable request GZIP compression, true: enable, false: disable
      enabled: true
      # Compress supported MIME types
      mime-types: text/xml,application/xml,application/json
      # Minimum value of compressed data
      min-request-size: 2048
    response:
      # Whether to enable GZIP compression for response. True: yes, false: no
      enabled: true
  client:
    config:
      #feign Global configuration
      default:
        Basic: only log request method, URL, response status code, and execution time (suitable for production environment)
        #headers: Records the headers of the request and response based on basic. Full: records the headers, body, and metadata of the request and response. Default: None
        loggerLevel: basic
      #feign Specifies the client configuration, that is, only for the specified invoked service
      eureka-client:
        loggerLevel: full
Copy the code

4.4 Writing test Interfaces

UserController.java

/** **@author  Trazen
 * @date2020/7/15 seated * /
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @GetMapping("/a")
    @PreAuthorize("isAuthenticated()")
    public String get(@AuthenticationPrincipal CurrentUser currentUser){
        return "1";
    }

    @GetMapping("/b")
    public String get02(a){
        log.info("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - > {}",SecurityUtils.getCurrentUser());
        return SecurityUtils.getCurrentUser().getUsername();
    }

    @GetMapping("/c")
    public String get03(@AuthenticationPrincipal CurrentUser currentUser){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        log.info("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - > {}",currentUser);
        return "3"; }}Copy the code

4.5 Build the ORder-Server based on the preceding deployment

Feign interface call demo code is as follows:

/** **@author  Trazen
 * @dateCame 2020/7/15 * /
@FeignClient(value = "user-server")
public interface UserFeignApi {

    /** * user service B interface *@return* /
    @GetMapping("/user/b")
    String get02(a);

}
Copy the code
/** **@author  Trazen
 * @date2020/7/15 laboureth * /
@RestController
@RequestMapping("/order")
@Slf4j
@AllArgsConstructor
public class OrderController {

    private final UserFeignApi userFeignApi;

    @GetMapping("/a")
    public String get02(@AuthenticationPrincipal CurrentUser currentUser){
        log.info("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - > {}", SecurityUtils.getCurrentUser());
        return "1";
    }

    @GetMapping("/b")
    public String getFeign(a){
        returnuserFeignApi.get02(); }}Copy the code

4.6 Start the project for testing

Start the following projects: Eureka-server —-> Auth-server —-> user-server/order-server

  • 1. Obtain the Token through the authentication and authorization service interface

  • 2. Access the user-server interface with a token

If the token is valid and returns the result normally; If the token is invalid, the customized authorization expiration information is returned.

  • 3. Visit order-Server interface to demonstrate feIGN interface call to pass token

5. Project Address:

Github.com/ty197287300…

6. Reference Documents:

  • Ehedgehog.net/2019/03/23/…
  • The projects. Spring. IO/spring – secu…
  • Andaily.com/spring-oaut…