Previously in the tutorial on dynamic permission control, we implemented dynamic permission control by customizing FilterInvocationSecurityMetadataSource and AccessDecisionManager two interfaces. There are more things we need to do here, and there is a certain learning cost. Today to introduce a more simple and easy to understand approach to implement dynamic permission control.

Expression-based access control

1
2
3
httpSecurity.authorizeRequests()
    .anyRequest()
    .access("hasRole('admin')")

Needless to say, after we configure the expression hasRole('admin'), Spring Security will call the hasRole(String role) method of SecurityExpressionRoot to determine if the current user holds the role admin and thus make a decision on whether to release or not. This approach allows for dynamic access control in addition to static access control.

Bean-based access control expressions

Spring Security extends expressions to support references to any public Spring bean. Suppose we have a Spring Bean that implements the following interface :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 * 角色检查器接口.
 *
 * @author n1
 * @since 2021 /4/6 16:28
 */
public interface RoleChecker extends InitializingBean {

    /**
     * Check boolean.
     *
     * @param authentication the authentication
     * @param request        the request
     * @return the boolean
     */
    boolean check(Authentication authentication, HttpServletRequest request);
}

JDBC-based role checking, preferably using caching here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * 基于jdbc的角色检查 最好这里做个缓存
 * @author n1
 * @since 2021/4/6 16:43
 */
public class JdbcRoleChecker implements RoleChecker {
    // 系统集合的抽象实现,这里你可以采用更加合理更加效率的方式
    private Supplier<Set<AntPathRequestMatcher>> supplier;


    @Override
    public boolean check(Authentication authentication, HttpServletRequest request) {
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

       // 当前用户的角色集合
        System.out.println("authorities = " + authorities);
        //todo 这里自行实现比对逻辑
       //   supplier.get().stream().filter(matcher -> matcher.matches(request));
       // true false 为是否放行
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(supplier.get(), "function must not be null");
    } 
}

We can then configure HttpSecurity like this.

1
2
3
httpSecurity.authorizeRequests()
    .anyRequest()
    .access("@roleChecker.check(authentication,request)")

With Authentication in RoleChecker we can get the information about the current user, especially the permission set. With HttpServletRequest we can get the URI of the current request. This URI intersects the permission set in the system with the user’s permission set to make the correct access decision.

Path parameters

Sometimes our access URI also contains a path parameter, such as /foo/{id}. We can also control this with a bean-based access control expression combined with a specific id value. This would be written like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 * 角色检查器接口.
 *
 * @author n1
 * @since 2021 /4/6 16:28
 */
public interface RoleChecker extends InitializingBean {

    /**
     * Check boolean.
     *
     * @param authentication the authentication
     * @param request        the request
     * @return the boolean
     */
    boolean check(Authentication authentication, String id);
}

The corresponding configurations are as follows.

1
2
3
httpSecurity.authorizeRequests()
    .antMatchers("/foo/{id}/**")
    .access("@roleChecker.check(authentication,#id)")

So that when the /foo/123 request is intercepted, 123 will be assigned to the id handler in the check method.

Summary

This expression for dynamic permission control is much easier to grasp and understand than the previous approach. But it also has its limitations, such as the expression has a single parameter type in the method. The FilterInvocationSecurityMetadataSource approach is more powerful and can customize some access decisions for more complex scenarios.

Reference https://felord.cn/easy-dyn-acl-spring-security.html