Spring Securityは動的管理権限を実装しています(3)

SpringBootはSpringSecurityを統合して、動的なインターフェイス管理権限を実装します

前回の記事に続き、パーミッション管理はバックグラウンド管理に欠かせない要素です。現在、Spring Securityと組み合わせて、インターフェースの動的管理を実装しています。

動的な権利管理

Spring Securityは、権限の動的管理を実装します。最初のステップは、フィルターを作成することです。doFilterメソッドは、OPTIONSに直接注意を払う必要があります。そうしないと、クロスドメインの問題が発生します。また、前の記事で説明した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;
    }

}

DynamicSecurityFilterでsuper.beforeInvocation(fi)メソッドが呼び出されると、AccessDecisionManagerのdecideメソッドが認証操作のために呼び出され、decideメソッドのconfigAttributesパラメーターはSecurityMetadataSourceのgetAttributesメソッドを介して取得されます。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;
        }
    }
}

上記の紹介では、次に、SecurityMetadataSourceインターフェイスのgetAttributesメソッドを実装して、現在アクセスされているパスリソースを取得します。

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

SpringSecurityと組み合わせたインターフェースの動的管理権限は基本的に実現されています。明日Redis + AOP最適化権限管理について説明します。

公共

Spring Securityは動的管理権限を実装しています(3)
公式アカウントhttps://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA

おすすめ

転載: blog.51cto.com/15077535/2593738