preface

There is a requirement that users be prohibited from changing personal information for a period of time.

The previous writing in the project was:

1. In a data table, record the types of operations to be prohibited, such as modifying user information

2. Query the data in the table on the related interface and check whether the table is in the Effective state

3. Modify the status values recorded in the data table within a specified period of time

There are two problems with this approach:

  • Permission control codes are in the service logic and are not easy to maintain
  • Record status values need to be changed each time within a specified period of time, and sometimes late at night

The optimization is also simple:

  • Prohibit users to modify personal information, is a kind of interface access control, the best practice is to use the section, with the interface as the pointcut, control interface access in the section;
  • Use “time” instead of “status” to disable the action if the current request is within a specified period of time, so that the time is set in advance and the status is not changed late at night

The implementation of the scheme is described below.

The implementation of the scheme is very simple, the main purpose of the implementation of the scheme is to build a project of their own, constantly enrich the project functions, and do some technical practice in the project.

The project structure

The goal is to build a microservice project WPM (Work Plan Manager), which is still a single application of “multi-module” at the present stage, mainly divided into four layers, as shown in the figure below:

1. Business aggregation layer: the business entrance of the project, mainly APP module and Admin module

2. Business logic layer: provides business services such as “users, departments and resources”

3. Public service layer: provides basic function services such as “permission verification, statistics, message push”

4. Base dependency layer: The dependent components of each module exist in the form of modules at present and should exist in the form of components in the future

The organization structure of the project code is similar to the figure above, as shown below:

Currently, only “APP, User, Authority and Common” modules have been built. Currently, only app module provides external interfaces, so user and Authority modules only exist as dependent modules of APP module, rather than as a service.

Request process

According to our scheme in “Introduction”, the user initiates the request of “update user information”, and the process is as follows:

  • Request access to the section where the database configuration is queried
  • Check whether the request can be executed based on the configuration data
  • If the check passes, the execution continues; otherwise, a rejection result is returned

The request process and sequence diagram are shown below:

Request process diagram

Request sequence diagram

Code implementation

Create table

CREATE TABLE `common_authority` ( `id` int(11) NOT NULL AUTO_INCREMENT, 'operate_type' varchar(30) NOT NULL COMMENT 'Operation type ',' start_time 'datetime DEFAULT NULL COMMENT' Start time ', 'end_time' datetime DEFAULT NULL COMMENT 'end time ', 'created_at' TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time ', `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_operate_type` (`operate_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Copy the code

Annotation class

@target (ElementType.METHOD) @Retention(retentionPolicy.runtime) public @interface EnableCommonAuthority {/** * The default is to modify the user information * / String operateType () the default ICommonAuthConstant. OperateType. CHANGE_USER_INFO; }Copy the code

section

Check before entering the cut point.

@Aspect @Component public class CommonAuthorityAspect { @Resource private LoadingCache<String, CommonAuthority> commonAuthorityCache; @Pointcut("@annotation(com.shimengjie.wpm.authority.domain.model.commonauthority.annotation.EnableCommonAuthority)") public void pointcut() { } @Before(value = "pointcut()") public void before(JoinPoint pjp) { EnableCommonAuthority annotation = BeanUtils.findAnnotation(pjp, EnableCommonAuthority.class); if (null == annotation) { return; } String operateType = annotation.operateType(); CommonAuthority CommonAuthority = CommonAuthorityCache. get(operateType); if (commonAuthority ! = null && ! commonAuthority.isAllow()) { throw new AccessDeniedException(); }}}Copy the code

The cache

To reduce the time of each database query, LoadingCache is used to cache the data to be queried:

// cache loader @Component public class CommonAuthorityCacheLoader implements CacheLoader<String, CommonAuthority> { @Resource private MybatisCommonAuthorityRepository mybatisCommonAuthorityRepository; @Override public CommonAuthority load(String key) { return mybatisCommonAuthorityRepository.queryOfOperateType(key); @configuration public class CacheConfig {@bean public LoadingCache<String, CommonAuthority> commonAuthorityCache(CommonAuthorityCacheLoader cacheLoader) { return Caffeine.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .build(cacheLoader); }}Copy the code

interface

Add notes to the interface of “Modify User Information” in app module:

@Slf4j @RestController @RequestMapping(value = "api/wpm/app/user", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public class UserController { @Resource private UserAdapter userAdapter; @apiOperation (" Modify user info ") @putMapping ("/{id}") @enablecommonAuthority Public AbstractResponse updateById(@PathVariable("id") Long id, @RequestBody UserUpdateCommand command) { userAdapter.updateById(id, command); return new AbstractResponse(); }}Copy the code

test

Add data in the data table, set the current time (2021-09-24 20:28:00) within the forbidden time range:

INSERT INTO common_authority (operate_type,start_time,end_time) VALUES ('changeUserInfo','2021-09-24 20:00:00 ', '2021-09-24 23:00:00');Copy the code

Start the app, use the Postman execution interface, and set a breakpoint on the section. The result is as follows:

At this point, a simple “section-based interface access control” function is implemented.

conclusion

The interface access control is realized by using the section. Through the optimization of this simple function, a project prototype is built, which will continue to enrich the function of the project.

Project address: github.com/ShiMengjie/…

CodeGo Programming Notes, a public account, publishes weekly programming articles from beginner to master.