SpringBoot integrates SpringSecurity to dynamically manage interface permissions

Access management is an indispensable part of background management, today combined with SpringSecurity interface dynamic management.

Dynamic Rights Management

SpringSecurity implements dynamic permission management. The first step is to create a filter. The doFilter method needs to be paid attention to. The IgnoreUrlsConfig white list mentioned in the previous article is also allowed directly, all permissions are implemented in super. BeforeInvocation (fi).

/ * *

* Dynamic permission filter, used to achieve path-based dynamic permission filtering

 * 

* /

public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {



    @Autowired

    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;

    @Autowired

    private IgnoreUrlsConfig ignoreUrlsConfig;



    @Autowired

    public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {

        super.setAccessDecisionManager(dynamicAccessDecisionManager);

    }



    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

    }



    @Override

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;

        FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);

//OPTIONS requests permission directly

        if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){

            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

            return;

        }

// Whitelist requests direct release

        PathMatcher pathMatcher = new AntPathMatcher();

        for (String path : ignoreUrlsConfig.getUrls()) {

            if(pathMatcher.match(path,request.getRequestURI())){

                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

                return;

            }

        }

// The decide method in AccessDecisionManager is called for authentication

        InterceptorStatusToken token = super.beforeInvocation(fi);

        try {

            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

        } finally {

            super.afterInvocation(token, null);

        }

    }



    @Override

    public void destroy() {

    }



    @Override

public Class<? >getSecureObjectClass() {

        return FilterInvocation.class;

    }



    @Override

    public SecurityMetadataSource obtainSecurityMetadataSource() {

        return dynamicSecurityMetadataSource;

    }



}



Copy the code

The Decide method in AccessDecisionManager is called for authentication when the super-.beforeInvocation (FI) method is called in DynamicSecurityFilter, and the configAttribut method in Decide is called The ES parameter is obtained by the getAttributes method in SecurityMetadataSource. ConfigAttributes is the configured permission to access the interface. Here is the simplified beforeInvocation source code

public abstract class AbstractSecurityInterceptor implements InitializingBean,

        ApplicationEventPublisherAware, MessageSourceAware {





protected InterceptorStatusToken beforeInvocation(Object object) {



// Get metadata

        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()

                .getAttributes(object);



        Authentication authenticated = authenticateIfRequired();



// Perform authentication

        try {

            this.accessDecisionManager.decide(authenticated, object, attributes);

        }

        catch (AccessDeniedException accessDeniedException) {

            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,

                    accessDeniedException));



            throw accessDeniedException;

        }

    }

}

Copy the code

Following that, we implement the getAttributes method of the SecurityMetadataSource interface to retrieve the currently accessed path resource

/ * *

* Dynamic permission data source, used to obtain dynamic permission rules

 * 

* /

public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {



    private static Map<String, ConfigAttribute> configAttributeMap = null;

    @Autowired

    private DynamicSecurityService dynamicSecurityService;



    @PostConstruct

    public void loadDataSource() {

        configAttributeMap = dynamicSecurityService.loadDataSource();

    }



    public void clearDataSource() {

        configAttributeMap.clear();

        configAttributeMap = null;

    }



    @Override

    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {

        if (configAttributeMap == null) this.loadDataSource();

        List<ConfigAttribute>  configAttributes = new ArrayList<>();

// Get the current access path

        String url = ((FilterInvocation) o).getRequestUrl();

        String path = URLUtil.getPath(url);

        PathMatcher pathMatcher = new AntPathMatcher();

        Iterator<String> iterator = configAttributeMap.keySet().iterator();

// Get the resources needed to access the path

        while (iterator.hasNext()) {

            String pattern = iterator.next();

            if (pathMatcher.match(pattern, path)) {

                configAttributes.add(configAttributeMap.get(pattern));

            }

        }

// Return an empty set

        return configAttributes;

    }



    @Override

    public Collection<ConfigAttribute> getAllConfigAttributes() {

        return null;

    }



    @Override

public boolean supports(Class<? > aClass) {

        return true;

    }



}

Copy the code

Our background resources are regularly cached in a MAP object, so when the background resources change, we need to clear the cache and reload in the next query. We need to modify MyMesResourceController injection DynamicSecurityMetadataSource, when change the background resources, you need to call clearDataSource method to clear the cache data.

/ * *

* Background resource management Controller

 * 

* /

@Controller

@Api(tags = "MyMesResourceController", description = "Background Resource Management")

@RequestMapping("/resource")

public class MyMesResourceController {



    @Autowired

    private MyMesResourceService resourceService;

    @Autowired

    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;



    @ApiOperation("Add Background Resources")

    @RequestMapping(value = "/create", method = RequestMethod.POST)

    @ResponseBody

    public CommonResult create(@RequestBody UmsResource umsResource) {

        int count = resourceService.create(umsResource);

        dynamicSecurityMetadataSource.clearDataSource();

        if (count > 0) {

            return CommonResult.success(count);

        } else {

            return CommonResult.failed();

        }

    }

 }

Copy the code

We need to implement the AccessDecisionManager interface to verify permissions. For interfaces that are not configured with resources, we directly allow access. For interfaces configured with resources, we compare the resources required for access with the resources owned by the user, and allow access if they match.

/ * *

* Dynamic permission decision manager, used to determine whether a user has access rights

 * 

* /

public class DynamicAccessDecisionManager implements AccessDecisionManager {



    @Override

    public void decide(Authentication authentication, Object object,

                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

// If no resource is configured on the interface, the interface is allowed

        if (CollUtil.isEmpty(configAttributes)) {

            return;

        }

        Iterator<ConfigAttribute> iterator = configAttributes.iterator();

        while (iterator.hasNext()) {

            ConfigAttribute configAttribute = iterator.next();

// Compare access required resources or user-owned resources

            String needAuthority = configAttribute.getAttribute();

            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {

                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {

                    return;

                }

            }

        }

        throw new AccessDeniedException("Sorry, you don't have access.");

    }



    @Override

    public boolean supports(ConfigAttribute configAttribute) {

        return true;

    }



    @Override

public boolean supports(Class<? > aClass) {

        return true;

    }



}

Copy the code

Before us in the DynamicSecurityMetadataSource injected a DynamicSecurityService object, it is my custom a dynamic access business interface, it is mainly used for loading all the background resources rules.

/ * *

* Dynamic permission related business classes

 *

* /

public interface DynamicSecurityService {

/ * *

* Load resource ANT wildcard and resource MAP

* /

    Map<String, ConfigAttribute> loadDataSource();

}

Copy the code

Combined with SpringSecurity interface dynamic management permissions have been basically realized, tomorrow and the day after tomorrow ready to explain the Redis+AOP optimization permission management

No. The public https://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA