Spring Security implementa permisos de administración dinámica (3)

SpringBoot integra SpringSecurity para implementar permisos de administración de interfaz dinámica

Continuando con el artículo anterior, la gestión de permisos es una parte indispensable de la gestión en segundo plano Hoy, combinada con Spring Security para implementar la gestión dinámica de interfaces.

Gestión dinámica de derechos

Spring Security implementa la gestión dinámica de permisos. El primer paso es crear un filtro. El método doFilter debe prestar atención a las OPCIONES directamente, de lo contrario se producirán problemas entre dominios. Y la lista blanca en IgnoreUrlsConfig mencionada en el artículo anterior también se publica directamente, y todas las operaciones de permisos se implementarán en 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;
    }

}

Cuando se llama al método super.beforeInvocation (fi) en DynamicSecurityFilter, se llama al método decide en AccessDecisionManager para la operación de autenticación, y el parámetro configAttributes en el método decide se obtendrá a través del método getAttributes en SecurityMetadataSource. ConfigAttributes está realmente configurado para acceder a la corriente Los permisos requeridos por la interfaz, la siguiente es una versión simplificada del código fuente 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;
        }
    }
}

La introducción anterior, a continuación, implementamos el método getAttributes de la interfaz SecurityMetadataSource para obtener los recursos de ruta a los que se accede actualmente.

/**
 * 动态权限数据源,用于获取动态权限规则
 * 
 */
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;
    }

}

Nuestros recursos de fondo se almacenan en caché en un objeto MAP por reglas. Cuando los recursos de fondo cambian, la caché debe borrarse y recargarse en la siguiente consulta. Necesitamos modificar MyMesResourceController para inyectar DynamicSecurityMetadataSource. Al modificar el recurso de fondo, necesitamos llamar al método clearDataSource para borrar los datos en caché.

/**
 * 后台资源管理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();
        }
    }
 }

Necesitamos implementar la interfaz AccessDecisionManager para lograr la verificación de permisos. Para las interfaces que no están configuradas con recursos, permitimos el acceso directamente. Para las interfaces con recursos configurados, comparamos los recursos requeridos para acceder con los recursos propiedad del usuario y, si coinciden, se permite el acceso.

/**
 * 动态权限决策管理器,用于判断用户是否有访问权限
 * 
 */
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;
    }

}

Anteriormente inyectamos un objeto DynamicSecurityService en DynamicSecurityMetadataSource, que es una interfaz de negocios de permisos dinámicos que personalicé, que se usa principalmente para cargar todas las reglas de recursos en segundo plano.

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

La autoridad de gestión dinámica de la interfaz combinada con Spring Security se ha realizado básicamente. Mañana explicaré la gestión de la autoridad de optimización de Redis + AOP.

el público

Spring Security implementa permisos de administración dinámica (3)
Cuenta oficial https://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA

Supongo que te gusta

Origin blog.51cto.com/15077535/2593738
Recomendado
Clasificación