After reading this article, do you still feel that spring security integration OAuth2 custom query users are complex?

Keywords: Programming Spring Mybatis JDBC Database

Spring security integration OAuth2 is recognized by developers as the best matching partner for resource protection and service authentication. This pair of good friends have been quietly guarding the security of application services. According to the different roles of visitors, they can control the granularity to specific interfaces, so as to realize the fine division of permissions.

The Spring security framework is relatively high in the team of security framework. Although Spring is encapsulated by Spring boot, there are many configurations that are easy to be missed in use. Because of the large number of configurations, it is difficult for beginners to understand. To solve this problem, ApiBoot encapsulates Spring security and OAuth2, which greatly simplifies the process Configuration (only for simplification and enhancement, the basic syntax and configuration of Spring security can also be used normally)

ApiBoot Security Series

Create project

Use the IDEA development tool to create a SpringBoot project.

The underlying layer of ApiBoot is SpringBoot. In order to support the 2.2.x branch of SpringBoot, ApiBoot also creates the 2.2.x branch version.

Add ApiBoot unified dependency

After creating the project, we need to add the unified version dependency of ApiBoot in pom.xml, as follows:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.minbox.framework</groupId>
      <artifactId>api-boot-dependencies</artifactId>
      <version>2.2.0.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Add dependency

In this chapter, we need to query the user information in the database for authentication, so we need to add database related dependencies in pom.xml, as follows:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.minbox.framework</groupId>
    <artifactId>api-boot-starter-security-oauth-jwt</artifactId>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
  </dependency>
  <dependency>
    <groupId>org.minbox.framework</groupId>
    <artifactId>api-boot-starter-mybatis-enhance</artifactId>
  </dependency>
</dependencies>

In this chapter, ApiBoot Mybatis Enhance is used. For specific use, please visit the official document ApiBoot MyBatis Enhance use document

Configure data sources

After adding database related dependencies, add the following configuration information in the application.yml file:

spring:
  application:
    name: apiboot-security-oauth-custom-certification-user
  # Data source configuration
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 9090

Configure ApiBoot Security

ApiBoot Security uses memory to read user information by default. In this chapter, we need to modify it to JDBC mode, and disable the default reading of user information (the default table structure is provided in ApiBoot Security, and the user information can be directly used for authentication after adding data after the table is built. For details: API boot security usage document).

Add the following configuration to the application.yml configuration file:

# ApiBoot configuration
api:
  boot:
    security:
      # ApiBoot Security uses JDBC to read users
      away: jdbc
      # Disable the default read user mode
      enable-default-store-delegate: false

The default value of api.boot.security.enable-default-store-delegate configuration parameter is true, that is to say, it will automatically read the API boot user info user information table in the database corresponding to the data source. When we set it to false, we need to implement the API bootstoredelete interface to customize the user information for query.

Configure ApiBoot OAuth

The API boot starter security OAuth JWT dependency also integrates OAuth2 internally by default, and the default data storage mode is the same as Spring Security. The main purpose of this chapter is to query the authentication user information, not the client information, so we still use the default memory mode, but modify the default configuration information of the client , add the configuration in the application.yml file as follows:

# ApiBoot configuration
api:
  boot:
    oauth:
      # List of clients for ApiBoot OAuth2
      clients:
        - clientId: hengboy
          clientSecret: chapter
          grantTypes: password,refresh_token

For the default client configuration information of OAuth2 in API boot, you can check the source code of org.minbox.framework.api.boot.autoconfigure.oauth.ApiBootOauthProperties.Client for details.

User authentication

The configuration has been completed. Let's write and query the user information, and give the user information to the ApiBoot Security framework for authentication, access token generation and other operations.

The persistence framework used in this chapter is ApiBoot MyBatis Enhance. Please refer to Official documents.

Create user table

We create a system user information table named system "user in the database. The table structure is as follows:

CREATE TABLE `system_user` (
  `su_id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'User number',
  `su_login_name` varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Login name',
  `su_nick_name` varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Nickname?',
  `su_password` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'User password',
  `su_create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
  `su_status` int(11) DEFAULT '1' COMMENT 'User status, 1: normal, 0: frozen,-1: Deleted',
  PRIMARY KEY (`su_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='System user information table';

After the system user table is created, we add a piece of user data to this table, as shown below:

INSERT INTO `system_user` VALUES ('9b69fd26-14db-11ea-b743-dcd28627348e','yuqiyu','Heng Yu junior - Yu Qi Yu','$2a$10$RbJGpi.v3PwkjrYENzOzTuMxazuanX3Qa2hwI/f55cYsZhFT/nX3.','2019-12-02 08:13:22',1);

When we log in, the user name corresponds to the Su login name field, and the password corresponds to the Su password field. The password of yuqiyu is initialized to 123456, and the format of the password must be BCryptPasswordEncoder encrypted ciphertext.

Create user entity

For the system user table, we need to create an entity used by apiboot mybatis strength, and create an entity named SystemUser as follows:


/**
 * Basic information of system user
 *
 * @author Heng Yu junior
 */
@Data
@Table(name = "system_user")
public class SystemUser implements UserDetails {
    /**
     * User number
     */
    @Id(generatorType = KeyGeneratorTypeEnum.UUID)
    @Column(name = "su_id")
    private String userId;
    /**
     * Login name
     */
    @Column(name = "su_login_name")
    private String loginName;
    /**
     * Nickname?
     */
    @Column(name = "su_nick_name")
    private String nickName;
    /**
     * Password
     */
    @Column(name = "su_password")
    private String password;
    /**
     * Creation time
     */
    @Column(name = "su_create_time")
    private String createTime;
    /**
     * User status
     * 1: Normal, 0: frozen, - 1: deleted
     */
    @Column(name = "su_status")
    private Integer status;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.EMPTY_LIST;
    }

    @Override
    public String getUsername() {
        return this.loginName;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    /**
     * UserDetails Provided method, whether the user has not expired
     * It can be extended according to the fields in the user data table. For the sake of demonstration, it is configured as true
     *
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * UserDetails Provided method, whether the user is not locked
     * It can be extended according to the fields in the user data table. For the sake of demonstration, it is configured as true
     *
     * @return
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * UserDetails Method provided, whether the voucher has not expired
     * It can be extended according to the fields in the user data table. For the sake of demonstration, it is configured as true
     *
     * @return
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * UserDetails Method provided, enable or not
     *
     * @return
     */
    @Override
    public boolean isEnabled() {
        return this.status == 1;
    }
}

Please refer to the apiboot mybatis strength document for the specific annotation usage. Here, we also need to note that SystemUser implements the UserDetails interface. If you have used Spring Security, you should know that this is the user details interface definition provided by Spring Security. If you want to query users, you should let us customize the user entity The user entity (SystemUser entity) implements this interface and all the methods provided in the UserDetails interface.

Create user data interface

The user's entity has been created. In this chapter, we need to query the user's basic data interface according to the user's login name. Create an interface named SystemUserEnhanceMapper as follows:

/**
 * ApiBoot Enhance Enhanced Mapper provided
 * Automatically scanned and registered to IOC
 *
 * @author Heng Yu junior
 * @see org.minbox.framework.api.boot.autoconfigure.enhance.ApiBootMyBatisEnhanceAutoConfiguration
 */
public interface SystemUserEnhanceMapper extends EnhanceMapper<SystemUser, Integer> {
    /**
     * Query user information according to user login name
     *
     * @param loginName {@link SystemUser#getLoginName()}
     * @return {@link SystemUser}
     */
    SystemUser findByLoginName(@Param("loginName") String loginName);
}

This interface inherits the enhancemapper < entity, ID > interface. It can be automatically scanned to the instance where the agent is created and added to IOC, so that we can inject it directly in other parts of the project.

Note: findByXxx method is a method naming rule query provided by ApiBoot MyBatis Enhance. Multiple query conditions can be appended with And Or, And the corresponding SQL will be generated automatically according to the rules of the method.

Implement API bootstoredelete interface

ApiBoot Security provides an interface, apibootstoredelete, which is mainly used to query the specific information of the logged in user. When we obtain the access token through grant ﹐ type = password & username = XXX, ApiBoot Security will directly pass the parameter value of username to the method of apibootstoredelete ﹐ loaduserbyusername, so that we can Query users according to username and return to ApiBoot Security for subsequent authentication.

Let's create a class named UserService and implement the apibootstoredelete interface, as follows:


/**
 * Custom read user information
 *
 * @author Heng Yu junior
 */
@Service
public class UserService implements ApiBootStoreDelegate {
    /**
     * logger instance
     */
    static Logger logger = LoggerFactory.getLogger(UserService.class);
    /**
     * User data interface
     */
    @Autowired
    private SystemUserEnhanceMapper mapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDetails userDetails = mapper.findByLoginName(username);
        if (ObjectUtils.isEmpty(userDetails)) {
            throw new UsernameNotFoundException("User:" + username + ",Non-existent.");
        }
        logger.info("Login user information:{}", JSON.toJSONString(userDetails));
        return userDetails;
    }
}

The return value of the loadUserByUsername method is the UserDetails interface type. We have implemented SystemUser before, so we can directly use the SystemUser instance as the return value.

Operation test

The code is ready to start the project in the way of xxxapplication.

Test point: get AccessToken

Before obtaining the AccessToken, we need to confirm the clientId and clientSecret configuration contents of the client of api.boot.oauth.clients configured in the application.yml file. The following is the way of CURL:

➜ ~ curl hengboy:chapter@localhost:9090/oauth/token -d 'grant_type=password&username=yuqiyu&password=123456'
{"access_token":"3beb1bee-9ca6-45e1-9fb8-5fc181670f63","token_type":"bearer","refresh_token":"d2243e18-8ab3-4842-a98f-ebd79da94e2e","expires_in":7199,"scope":"api"}

Test point: refresh AccessToken

Copy the value of the refresh_token obtained above to refresh. The following is the CURL method for refreshing the AccessToken:

➜ ~ curl hengboy:chapter@localhost:9090/oauth/token -d 'grant_type=refresh_token&refresh_token=d2243e18-8ab3-4842-a98f-ebd79da94e2e'
{"access_token":"e842c2ee-5672-49db-a530-329186f36492","token_type":"bearer","refresh_token":"d2243e18-8ab3-4842-a98f-ebd79da94e2e","expires_in":7199,"scope":"api"}

hengboy, the OAuth2 client, authorizes two grant types, password and refresh token, in application.yml by configuring grant types. If you need other ways, you can add them in the configuration file.

Knock on the blackboard

After ApiBoot integrates Spring Security and OAuth2 to read user-defined information, we only need to pay attention to how to read user information. Previously, those ignorant code configurations can be replaced by configuration files. The main content of this chapter is the interface of ApiBoot storedelete. The functions provided by ApiBoot are not only these, but will be shared with you in succession.

Code example

Wechat scan the following two-dimensional code to pay attention to "programmer Hengyu youth", reply to "source code" to get the source code warehouse address.

The source code of this chapter is SpringBoot2.x/apiboot-security-oauth-custom-certification-user in the spring boot chapter warehouse

Author individual Blog Using open source framework ApiBoot Help you become Api interface service architect

Posted by charlieholder on Sat, 07 Dec 2019 05:17:58 -0800