Spring Security реализует разрешения динамического управления (3)

SpringBoot интегрирует SpringSecurity для реализации разрешений динамического управления интерфейсом

В продолжение предыдущей статьи, управление разрешениями является неотъемлемой частью фонового управления. Сегодня в сочетании с Spring Security для реализации динамического управления интерфейсами.

Динамическое управление правами

Spring Security реализует динамическое управление разрешениями. Первым шагом является создание фильтра. Метод doFilter должен обращать внимание непосредственно на ОПЦИИ, иначе возникнут междоменные проблемы. Белый список в IgnoreUrlsConfig, упомянутый в предыдущей статье, также напрямую освобождается, и все операции с разрешениями будут реализованы в super.beforeInvocation (fi).

/**
 * 动态权限过滤器,用于实现基于路径的动态权限过滤
 * 
 */
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请求直接放行
        if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            return;
        }
        //白名单请求直接放行
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String path : ignoreUrlsConfig.getUrls()) {
            if(pathMatcher.match(path,request.getRequestURI())){
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }
        }
        //此处会调用AccessDecisionManager中的decide方法进行鉴权操作
        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;
    }

}

Когда метод super.beforeInvocation (fi) вызывается в DynamicSecurityFilter, метод решения в AccessDecisionManager вызывается для операции аутентификации, а параметр configAttributes в методе решения будет получен с помощью метода getAttributes в SecurityMetadataSource. ConfigAttributes фактически настроен для доступа к текущему Разрешения, требуемые интерфейсом, ниже - это упрощенная версия исходного кода beforeInvocation.

public abstract class AbstractSecurityInterceptor implements InitializingBean,
        ApplicationEventPublisherAware, MessageSourceAware {

protected InterceptorStatusToken beforeInvocation(Object object) {

        //获取元数据
        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);

        Authentication authenticated = authenticateIfRequired();

        //进行鉴权操作
        try {
            this.accessDecisionManager.decide(authenticated, object, attributes);
        }
        catch (AccessDeniedException accessDeniedException) {
            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
                    accessDeniedException));

            throw accessDeniedException;
        }
    }
}

Вышеупомянутое введение, затем мы реализуем метод getAttributes интерфейса SecurityMetadataSource для получения ресурсов пути, к которым в настоящее время осуществляется доступ.

/**
 * 动态权限数据源,用于获取动态权限规则
 * 
 */
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<>();
        //获取当前访问的路径
        String url = ((FilterInvocation) o).getRequestUrl();
        String path = URLUtil.getPath(url);
        PathMatcher pathMatcher = new AntPathMatcher();
        Iterator<String> iterator = configAttributeMap.keySet().iterator();
        //获取访问该路径所需资源
        while (iterator.hasNext()) {
            String pattern = iterator.next();
            if (pathMatcher.match(pattern, path)) {
                configAttributes.add(configAttributeMap.get(pattern));
            }
        }
        // 未设置操作请求权限,返回空集合
        return configAttributes;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

Наши фоновые ресурсы кэшируются в объекте MAP по правилам.При изменении фоновых ресурсов кеш необходимо очистить и перезагрузить в следующем запросе. Нам нужно изменить MyMesResourceController, чтобы внедрить DynamicSecurityMetadataSource. При изменении фонового ресурса нам нужно вызвать метод clearDataSource для очистки кэшированных данных.

/**
 * 后台资源管理Controller
 * 
 */
@Controller
@Api(tags = "MyMesResourceController", description = "后台资源管理")
@RequestMapping("/resource")
public class MyMesResourceController {

    @Autowired
    private MyMesResourceService resourceService;
    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;

    @ApiOperation("添加后台资源")
    @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();
        }
    }
 }

Нам необходимо реализовать интерфейс AccessDecisionManager для проверки разрешений. Для интерфейсов, для которых не настроены ресурсы, мы напрямую разрешаем доступ. Для интерфейсов с настроенными ресурсами мы сравниваем ресурсы, необходимые для доступа, с ресурсами, принадлежащими пользователю, и, если они совпадают, доступ разрешен.

/**
 * 动态权限决策管理器,用于判断用户是否有访问权限
 * 
 */
public class DynamicAccessDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        // 当接口未被配置资源时直接放行
        if (CollUtil.isEmpty(configAttributes)) {
            return;
        }
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //将访问所需资源或用户拥有资源进行比对
            String needAuthority = configAttribute.getAttribute();
            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("抱歉,您没有访问权限");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

Ранее мы внедрили объект DynamicSecurityService в DynamicSecurityMetadataSource, который представляет собой настроенный мной бизнес-интерфейс динамических разрешений, который в основном используется для загрузки всех правил фоновых ресурсов.

/**
 * 动态权限相关业务类
 *
 */
public interface DynamicSecurityService {
    /**
     * 加载资源ANT通配符和资源对应MAP
     */
    Map<String, ConfigAttribute> loadDataSource();
}

Полномочия динамического управления интерфейсом в сочетании с Spring Security в основном реализованы.Я объясню управление полномочиями оптимизации Redis + AOP завтра.

публика

Spring Security реализует разрешения динамического управления (3)
Официальный аккаунт https://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA

рекомендация

отblog.51cto.com/15077535/2593738