Shiro框架:Shiro用户访问控制鉴权流程-Aop注解方式源码解析

news2024/11/24 3:48:28

目录

1.Spring Aop嵌入点解析

2.Shiro框架Aop切面逻辑解析

2.1 通过注解实现切点

2.2 通过增强逻辑执行校验过程

2.2.1 增强实现类AopAllianceAnnotationsAuthorizingMethodInterceptor

2.2.1.1 类图解析

2.2.1.2 实现增强方法 

2.2.1.3 Shiro校验逻辑实现

2.2.1.3.1 获取Shiro自定义方法拦截器

2.2.1.3.1.1 Shiro自定义方法拦截器类图

2.2.1.3.1.2 Shiro自定义方法拦截器创建 

2.2.1.3.2 supports方法过滤方法拦截器

2.2.1.3.3 方法拦截器执行


Shiro作为一款比较流行的登录认证、访问控制安全框架,被广泛应用在程序员社区;Shiro登录验证、访问控制、Session管理等流程内部都是委托给SecurityManager安全管理器来完成的,SecurityManager安全管理器前面文章已经进行了详细解析,详见:Shiro框架:Shiro SecurityManager安全管理器解析;在此基础上,前面文章已对Shiro用户登录认证流程(详见:Shiro框架:Shiro登录认证流程源码解析进行了源码跟踪,Shiro框架:Shiro用户访问控制鉴权流程-内置过滤器方式源码解析 也对用户访问控制鉴权流程-内置过滤器方式进行了详细解析本篇文章继续对用户访问控制鉴权流程-Aop注解方式进行源码解析,了解不同的使用方式以便更好的应用到实际项目中;

想要深入了解Shiro框架整体原理,可移步:

Shiro框架:ShiroFilterFactoryBean过滤器源码解析、

Shiro框架:Shiro内置过滤器源码解析

Shiro的权限校验逻辑是嵌入到Spring Aop的增强逻辑中进行执行的,下面先看一下Shiro与Spring Aop是如何结合的,也即对Shiro校验逻辑在Spring Aop的嵌入点进行分析;

1.Spring Aop嵌入点解析

Shiro的校验逻辑应用到了Aop框架的抽象类StaticMethodMatcherPointcutAdvisor,该类的继承层次结构如下:

可以看出,其实现了切点Pointcut和切面Advisor 2个顶层接口,并通过实现方法匹配接口MethodMatcher定位切点,进而执行增强逻辑;

在Shiro框架中,实现StaticMethodMatcherPointcutAdvisor的具体子类为AopAllianceAnnotationsAuthorizingMethodInterceptor,其同时实现了Shiro框架的切点和增强逻辑,下面进行具体分析;

对Spring Aop的实现逻辑感兴趣,可以参考之前的文章:

Spring源码:Aop中@Aspect切面的解析代理过程

Spring源码:Aop源码分析

2.Shiro框架Aop切面逻辑解析

Shiro框架的Aop切面逻辑主要包含了2部分:

切点(Pointcut):Shiro如何通过自定义注解方式捕捉切点

增强逻辑(Advice):Shiro如何通过增强逻辑执行内部校验过程

Shiro的切点和增强逻辑定义在AuthorizationAttributeSourceAdvisor切面类中的,其类图如下:

可以看出,其继承了StaticMethodMatcherPointcutAdvisor类,并植入了切点和增强逻辑,下面分别进行解析;

2.1 通过注解实现切点

AuthorizationAttributeSourceAdvisor类中,是通过实现接口MethodMatcher的方法matches引入切点的,具体实现如下:

    public boolean matches(Method method, Class targetClass) {
        Method m = method;

        if ( isAuthzAnnotationPresent(m) ) {
            return true;
        }

        //The 'method' parameter could be from an interface that doesn't have the annotation.
        //Check to see if the implementation has it.
        if ( targetClass != null) {
            try {
                m = targetClass.getMethod(m.getName(), m.getParameterTypes());
                return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass);
            } catch (NoSuchMethodException ignored) {
                //default return value is false.  If we can't find the method, then obviously
                //there is no annotation, so just use the default return value.
            }
        }

        return false;
    }

    private boolean isAuthzAnnotationPresent(Class<?> targetClazz) {
        for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
            Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass);
            if ( a != null ) {
                return true;
            }
        }
        return false;
    }

    private boolean isAuthzAnnotationPresent(Method method) {
        for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
            Annotation a = AnnotationUtils.findAnnotation(method, annClass);
            if ( a != null ) {
                return true;
            }
        }
        return false;
    }

如上,通过判断Class级别和Method级别是否包含注解AUTHZ_ANNOTATION_CLASSES,来判断是否命中切点;

这里AUTHZ_ANNOTATION_CLASSES的定义如下:

    private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
            new Class[] {
                    RequiresPermissions.class, RequiresRoles.class,
                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
            };

这里的注解都是Shiro框架内置的自定义注解,比如RequiresPermissions注解用于用户权限校验,RequiresRoles注解用于用户角色校验,RequiresAuthentication注解校验用户登录状态;

因此Shiro通过自定义注解的方式实现了Aop切点;

2.2 通过增强逻辑执行校验过程

Shiro的增强逻辑同样在类AuthorizationAttributeSourceAdvisor中,在构造方法中通过setAdvice指定了增强实现类,如下:

    /**
     * Create a new AuthorizationAttributeSourceAdvisor.
     */
    public AuthorizationAttributeSourceAdvisor() {
        setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
    }

在类AopAllianceAnnotationsAuthorizingMethodInterceptor中,实现了是如何进行增强逻辑处理的,下面进行具体分析;

2.2.1 增强实现类AopAllianceAnnotationsAuthorizingMethodInterceptor

2.2.1.1 类图解析

AopAllianceAnnotationsAuthorizingMethodInterceptor类图如下,

左侧类层次结构是Shiro内部的实现,用于实现Shiro的增强逻辑

右侧类层次结构是Spring Aop中实现增强的切入点,这里实现了MethodInterceptor方法拦截器接口,其内部通过引入的invoke方式执行增强逻辑:

public interface MethodInterceptor extends Interceptor {
	
	/**
	 * Implement this method to perform extra treatments before and
	 * after the invocation. Polite implementations would certainly
	 * like to invoke {@link Joinpoint#proceed()}.
	 * @param invocation the method invocation joinpoint
	 * @return the result of the call to {@link Joinpoint#proceed()};
	 * might be intercepted by the interceptor
	 * @throws Throwable if the interceptors or the target object
	 * throws an exception
	 */
	Object invoke(MethodInvocation invocation) throws Throwable;

}

下面主要分析下AopAllianceAnnotationsAuthorizingMethodInterceptor是如何实现invoke方法的;

2.2.1.2 实现增强方法 

增强方法的实现如下:

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
        return super.invoke(mi);
    }

这里创建了Shiro内部自定义的MethodInvocation,然后调用父类invoke方法,createMethodInvocation实现如下:

    protected org.apache.shiro.aop.MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) {
        final MethodInvocation mi = (MethodInvocation) implSpecificMethodInvocation;

        return new org.apache.shiro.aop.MethodInvocation() {
            public Method getMethod() {
                return mi.getMethod();
            }

            public Object[] getArguments() {
                return mi.getArguments();
            }

            public String toString() {
                return "Method invocation [" + mi.getMethod() + "]";
            }

            public Object proceed() throws Throwable {
                return mi.proceed();
            }

            public Object getThis() {
                return mi.getThis();
            }
        };
    }

父类AuthorizingMethodInterceptor的invoke方法实现如下:

public abstract class AuthorizingMethodInterceptor extends MethodInterceptorSupport {

    /**
     * Invokes the specified method (<code>methodInvocation.{@link org.apache.shiro.aop.MethodInvocation#proceed proceed}()</code>
     * if authorization is allowed by first
     * calling {@link #assertAuthorized(org.apache.shiro.aop.MethodInvocation) assertAuthorized}.
     */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        assertAuthorized(methodInvocation);
        return methodInvocation.proceed();
    }

    /**
     * Asserts that the specified MethodInvocation is allowed to continue by performing any necessary authorization
     * (access control) checks first.
     * @param methodInvocation the <code>MethodInvocation</code> to invoke.
     * @throws AuthorizationException if the <code>methodInvocation</code> should not be allowed to continue/execute.
     */
    protected abstract void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException;

}

可以看到,invoke方法内,首先调用assertAuthorized方法执行增强逻辑(Shiro框架中即为自定义校验逻辑),校验通过,再继续执行目标方法,下面重点分析下assertAuthorized的具体实现逻辑;

2.2.1.3 Shiro校验逻辑实现

assertAuthorized方法的实现是在类AnnotationsAuthorizingMethodInterceptor中,如下:

    protected void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException {
        //default implementation just ensures no deny votes are cast:
        Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors();
        if (aamis != null && !aamis.isEmpty()) {
            for (AuthorizingAnnotationMethodInterceptor aami : aamis) {
                if (aami.supports(methodInvocation)) {
                    aami.assertAuthorized(methodInvocation);
                }
            }
        }
    }

如上,这里主要包含了3个部分:

  1. 首先获取Shiro自定义的方法拦截器
  2. 然后通过supports方法过滤方法拦截器
  3. 最后调用方法拦截器的assertAuthorized方法实现拦截

下面进行具体分析:

2.2.1.3.1 获取Shiro自定义方法拦截器

Shiro自定义方法拦截器保存在了成员变量methodInterceptors中,其具体赋值是在AopAllianceAnnotationsAuthorizingMethodInterceptor构造函数中,如下:

    public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
        List<AuthorizingAnnotationMethodInterceptor> interceptors =
                new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);

        //use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
        //raw JDK resolution process.
        AnnotationResolver resolver = new SpringAnnotationResolver();
        //we can re-use the same resolver instance - it does not retain state:
        interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
        interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
        interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
        interceptors.add(new UserAnnotationMethodInterceptor(resolver));
        interceptors.add(new GuestAnnotationMethodInterceptor(resolver));

        setMethodInterceptors(interceptors);
    }

下面首先对Shiro自定义方法拦截器进行分析;

2.2.1.3.1.1 Shiro自定义方法拦截器类图

如上,实例化了多个Shiro自定义方法拦截器,整体类图如下:

可以看出这里的PermissionAnnotationMethodInterceptor方法拦截器等也是实现了Shiro MethodInterceptor接口,并且通过组合的方式注入到了前面的增强类AopAllianceAnnotationsAuthorizingMethodInterceptor中;

2.2.1.3.1.2 Shiro自定义方法拦截器创建 

在上面Shiro多个自定义方法拦截器创建时,这里以PermissionAnnotationMethodInterceptor为例分析,其创建过程如下:

    public PermissionAnnotationMethodInterceptor(AnnotationResolver resolver) {
        super( new PermissionAnnotationHandler(), resolver);
    }

这里绑定了注解处理器PermissionAnnotationHandler,注解处理器的整体类图图示如下:

 在PermissionAnnotationHandler内部通过构造函数绑定了注解@RequiresPermissions,如下:

public class PermissionAnnotationHandler extends AuthorizingAnnotationHandler {

    /**
     * Default no-argument constructor that ensures this handler looks for
     * {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations.
     */
    public PermissionAnnotationHandler() {
        super(RequiresPermissions.class);
    }

    //……
}

另外,我们可以看到实例化时注入了注解解析器,这里实际为SpringAnnotationResolver,主要完成对指定注解的解析工作,实现如下:

public class SpringAnnotationResolver implements AnnotationResolver {

    public Annotation getAnnotation(MethodInvocation mi, Class<? extends Annotation> clazz) {
        Method m = mi.getMethod();

        Annotation a = AnnotationUtils.findAnnotation(m, clazz);
        if (a != null) return a;

        //The MethodInvocation's method object could be a method defined in an interface.
        //However, if the annotation existed in the interface's implementation (and not
        //the interface itself), it won't be on the above method object.  Instead, we need to
        //acquire the method representation from the targetClass and check directly on the
        //implementation itself:
        Class<?> targetClass = mi.getThis().getClass();
        m = ClassUtils.getMostSpecificMethod(m, targetClass);
        a = AnnotationUtils.findAnnotation(m, clazz);
        if (a != null) return a;
        // See if the class has the same annotation
        return AnnotationUtils.findAnnotation(mi.getThis().getClass(), clazz);
    }
}

归纳总结,在PermissionAnnotationMethodInterceptor创建过程中,注入了PermissionAnnotationHandler和SpringAnnotationResolver:

  1. PermissionAnnotationHandler注解处理器:绑定了注解@RequiresPermissions
  2. SpringAnnotationResolver注解解析器:用于判断目标方法是否存在指定注解
2.2.1.3.2 supports方法过滤方法拦截器

supports方法的实现是在父类AnnotationMethodInterceptor中,如下:

    public boolean supports(MethodInvocation mi) {
        return getAnnotation(mi) != null;
    }

    protected Annotation getAnnotation(MethodInvocation mi) {
        return getResolver().getAnnotation(mi, getHandler().getAnnotationClass());
    }

这里通过getHandler().getAnnotationClass()方法获取绑定的注解,同样以PermissionAnnotationMethodInterceptor为例,getHandler()即为PermissionAnnotationHandler,getAnnotationClass()即为@RequiresPermissions;

这里getResolver()即为前面创建的SpringAnnotationResolver,通过getAnnotation方法获取目标方法上的@RequiresPermissions注解,并判断该注解是否存在,存在则匹配成功,然后继续调用方法拦截器进行执行;

2.2.1.3.3 方法拦截器执行

通过调用assertAuthorized方法执行拦截逻辑,以PermissionAnnotationMethodInterceptor为例,其实现是在父类AuthorizingAnnotationMethodInterceptor中实现的,如下:

    public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
        try {
            ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));
        }
        catch(AuthorizationException ae) {
            // Annotation handler doesn't know why it was called, so add the information here if possible. 
            // Don't wrap the exception here since we don't want to mask the specific exception, such as 
            // UnauthenticatedException etc. 
            if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
            throw ae;
        }         
    }

这里又委托给注解处理器,即PermissionAnnotationHandler,调用其方法assertAuthorized执行校验逻辑,参数为匹配到的目标方法上的注解@RequiresPermissions,具体实现如下:

    public void assertAuthorized(Annotation a) throws AuthorizationException {
        if (!(a instanceof RequiresPermissions)) return;

        RequiresPermissions rpAnnotation = (RequiresPermissions) a;
        String[] perms = getAnnotationValue(a);
        Subject subject = getSubject();

        if (perms.length == 1) {
            subject.checkPermission(perms[0]);
            return;
        }
        if (Logical.AND.equals(rpAnnotation.logical())) {
            getSubject().checkPermissions(perms);
            return;
        }
        if (Logical.OR.equals(rpAnnotation.logical())) {
            // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
            boolean hasAtLeastOnePermission = false;
            for (String permission : perms) if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true;
            // Cause the exception if none of the role match, note that the exception message will be a bit misleading
            if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]);
            
        }
    }

这里获取到了注解上配置的访问目标方法所需要的权限,也获取了当前登录用户Subject,然后调用其checkPermission方法执行权限校验,实现如下:

    public void checkPermission(String permission) throws AuthorizationException {
        assertAuthzCheckPossible();
        securityManager.checkPermission(getPrincipals(), permission);
    }

可以看到,这里也是委托给了安全管理器执行权限校验,并获取了当前登录用户的账户名Principals,进一步跟踪如下:

    public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
        this.authorizer.checkPermission(principals, permission);
    }

这里继续委托给权限校验器authorizer执行权限校验,这里具体类为ModularRealmAuthorizer,继续跟踪如下:

    /**
     * If !{@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, Permission) isPermitted(permission)}, throws
     * an <code>UnauthorizedException</code> otherwise returns quietly.
     */
    public void checkPermission(PrincipalCollection principals, Permission permission) throws AuthorizationException {
        assertRealmsConfigured();
        if (!isPermitted(principals, permission)) {
            throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
        }
    }

如上会校验权限组件Realm已配置,并委托给isPermitted方法校验当前用户是否具备指定权限,没有配置指定权限时,会抛出权限校验异常UnauthorizedException;

后续isPermitted方法的处理流程可以参见的Shiro框架:Shiro用户访问控制鉴权流程-内置过滤器方式源码解析 的章节“3.2.1 isPermitted权限校验”。

至此,Shiro用户访问控制鉴权流程-Aop注解方式源码解析完成,Over~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1400736.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录27期|Python|Day33|贪心算法|1005.K次取反后最大化的数组和|134. 加油站|135. 分发糖果

1005. K 次取反后最大化的数组和 思路比较简单&#xff0c;把所有的负数绝对值大的全部取反之后再在新的数组里把绝对值最小的重复取反即可。 class Solution(object):def largestSumAfterKNegations(self, nums, k):""":type nums: List[int]:type k: int:rt…

VS里那些实用的调试(debug)技巧

前言——————希望现在在努力的各位都能感动以后享受成功的自己&#xff01; 首先我们要来了解什么是bug——————bug本意是“昆虫”或“虫子”&#xff0c;现在⼀般是指在电脑系统或程序中&#xff0c;隐藏着的⼀些未被发现的缺陷或 问题&#xff0c;简称程序漏洞。 “…

Java导出Excel并合并单元格

需求&#xff1a;需要在导出excel时合并指定的单元格 ruoyi excel 项目基于若伊框架二次开发&#xff0c;本着能用现成的就不自己写的原则&#xff0c;先是尝试了Excel注解中needMerge属性 /*** 是否需要纵向合并单元格,应对需求:含有list集合单元格)*/public boolean needMer…

11 - PXC集群|MySQL存储引擎

PXC集群&#xff5c;MySQL存储引擎 数据库系列文章PXC集群配置集群测试集群 MySQL存储引擎存储引擎介绍mysql服务体系结构mysql服务的工作过程处理查询访问的工作过程处理存储insert访问的工作过程 什么是搜索引擎 存储引擎管理查看存储引擎修改存储引擎 存储引擎特点myisam存储…

20240119-子数组最小值之和

题目要求 给定一个整数数组 arr&#xff0c;求 min(b) 的总和&#xff0c;其中 b 的范围涵盖 arr 的每个&#xff08;连续&#xff09;子数组。由于答案可能很大&#xff0c;因此返回答案模数 Example 1: Input: arr [3,1,2,4] Output: 17 Explanation: Subarrays are [3]…

【排序算法】五、冒泡排序(C/C++)

「前言」文章内容是排序算法之冒泡排序的讲解。&#xff08;所有文章已经分类好&#xff0c;放心食用&#xff09; 「归属专栏」排序算法 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 冒泡排序1.1 原理1.2 代码实现&#xff08;C/C&#xff09;1.3 特性总结 冒泡排序 1.1…

基于Springboot的民宿在线预定平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的民宿在线预定平台(有报告)。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring…

SAI实例研究

实现目标 接到特定任务后&#xff0c;召唤生物攻击当前角色 例子 creature.id 15402&#xff08;即 smart_script.entryorguid&#xff09;共分为0和1两个事件阶段 第0阶段&#xff1a;第1条(id 0&#xff09;&#xff0c;第2条(id 1&#xff09;&#xff0c;第3条(id 2…

基于YOLOv8的目标识别、计数、电子围栏的项目开发过程

0 前言 用于生产环境中物体检测、识别、跟踪&#xff0c;人、车流量统计&#xff0c;越界安全识别 1 YOLOv8概述 YOLOv8 是Ultralytics的YOLO的最新版本。作为一种前沿、最先进(SOTA)的模型&#xff0c;YOLOv8在之前版本的成功基础上引入了新功能和改进&#xff0c;以提高性…

构建STM32MP133的Buildroot环境

意法半导体ST在坚持用 Yocto构建他们的OpenSTLinux MP1系列MCU&#xff0c;编译费劲&#xff0c;而且我们的应用不需要Yocto的环境&#xff0c;所以基于Buildroot的最小Linux系统更适合我们。 STM32MP133微处理器基于单Arm Cortex-A7内核&#xff0c;运行频率可达1 GHz&#x…

PACS医学影像采集传输与存储管理、影像诊断查询与报告管理系统,MPR多平面重建

按照国际标准IHE规范&#xff0c;以高性能服务器、网络及存储设备构成硬件支持平台&#xff0c;以大型关系型数据库作为数据和图像的存储管理工具&#xff0c;以医疗影像的采集、传输、存储和诊断为核心&#xff0c;集影像采集传输与存储管理、影像诊断查询与报告管理、综合信息…

使用JFLASH实现文件程序自动化合并及下载功能

主要总结下使用 SEGGER 工具集的 JFLASH 软件实现hex/bin文件合并以及程序的自动下载使用方法。 起因是最近使用到LVGL字库文件的制作&#xff0c;每次都要将分散的bin文件按既定分配的偏移作合并处理&#xff0c;刚开始使用的是二进制文件合并工具,文件少的时候还行&#xff…

C#使用DateTime.Now静态属性动态获得系统当前日期和时间

目录 一、实例 1.源码 2.生成效果 二、相关知识点 1.Thread类 &#xff08;1&#xff09;Thread.Sleep()方法 &#xff08;2&#xff09;Thread(ThreadStart) &#xff08;3&#xff09;IsBackground &#xff08;4&#xff09;Invoke( &#xff09; 2.CreateGrap…

【c++函数重载】

文章目录 一. 命名空间二 .全缺省参数和半缺省参数三 . 函数重载 一. 命名空间 1.不指定域&#xff1a;先在局部找&#xff0c;再全局。 2. 指定域&#xff1a;到指定的命名空间去找。 3. 当把指定命名空间放开时&#xff0c;即using namespace std&#xff1b;例如放开标准c库…

分布式 session

分布式 session 种 session 的时候需要注意范围&#xff0c;也就是 cookie.domain。 比如两个域名&#xff1a;a.heo.com&#xff0c;b.heo.com。如果要共享 cookie&#xff0c;可以种一个更高层的公共域名&#xff0c;比如 heo.com。 当服务器 A &#xff08;localhost:808…

大数据平台的硬件规划、网络调优、架构设计、节点规划

1.大数据平台硬件选型 要对Hadoop大数据平台进行硬件选型,首先需要了解Hadoop的运行架构以及每个角色的功能。在一个典型的Hadoop架构中,通常有5个角色,分别是NameNode、Standby NameNode、ResourceManager、NodeManager、DataNode以及外围机。 其中 NameNode 负责协调集群…

OneNote使用总结

试一下OneNote表格复制到CSDN的编辑器 用表格整理内容挺方便的&#xff0c;不过复制过来格式还是有些变化 目录 常用快捷键 高级应用 常用快捷键 文字编辑与排版 字号增加减小 Ctrl Shift 大于小于号 整行上下移动 左右缩进 Alt Shift方向 插入或转为公式 Alt 等于…

软件需求规格说明书-word

软件需求规格说明书编写规范 1.项目背景 2.项目目标 3.系统架构 4.总体流程 5.名称解释 6.功能模块 软件开发全文档获取&#xff1a;软件项目开发全套文档下载_软件项目文档-CSDN博客

Python笔记10-数据可视化练习折线图

文章目录 JSON数据Python数据和Json数据的相互转化pyecharts模块构建折线图全局配置绘制疫情数据折线图 JSON数据 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据 。本质上是一个带有特定格式的字符串 主要功能&#xff1a;可以在各个编程语言中流通…

R语言的ggplot2绘制分组折线图?

R绘制分组折线图.R 首先看数据情况&#xff1a;group有3组。Time有3组&#xff0c;数据意思是在3组3个时间点测量了某指标&#xff0c;现在要绘制组1、组2、组3某指标y按时间的变化趋势 数据情况&#xff1a; 看看最终的效果图如下&#xff1a; 下面是本次使用的代码 .libPat…