Shiro源码分析-----认证流程/授权流程----------Subject

2023-11-07

本文转载自:认证流程和授权流程

源码分析的第二篇以Subject的初始化为题。

一、回顾Apache Shiro创建Subject的步骤如下: 

 //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager  
	    Factory<org.apache.shiro.mgt.SecurityManager> factory =  
	            new IniSecurityManagerFactory("classpath:shiro.ini");  
	    //2、得到SecurityManager实例 并绑定给SecurityUtils  
	    org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();  
	    SecurityUtils.setSecurityManager(securityManager);  
	    //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)  
	    Subject subject = SecurityUtils.getSubject(); 

Apache Shiro 创建Subject 规则如下:

首先通过new IniSecurityManagerFactory并指定一个ini配置文件来创建一个SecurityManager工厂;

接着获取SecurityManager并绑定到SecurityUtils,这是一个全局设置,设置一次即可;

通过SecurityUtils得到Subject,其会自动绑定到当前线程;


接口Subject的文档介绍如下:

外界通过Subject接口来和SecurityManager进行交互,该接口含有登录、退出、权限判断、获取session,其中的Session可不是平常我们所使用的HttpSession等,而是shiro自定义的,是一个数据上下文,与一个Subject相关联的。

Apache Shiro 源代码创建步骤如下:

1、org.apache.shiro.Subject类的静态方法getSubject();

 public static Subject getSubject()
    {
        Subject subject = ThreadContext.getSubject();
        if(subject == null)
        {
            subject = (new org.apache.shiro.subject.Subject.Builder()).buildSubject();
            ThreadContext.bind(subject);
        }
        return subject;
    }
一看就是使用的是ThreadLocal设计模式,获取当前线程相关联的Subject 对象,如果没有则创建一个,然后绑定到当前线程。


2、ThreadContext是org.apache.shiro.util包下的一个工具类,它是用来操作和当前线程绑定的SecurityManager和Subject,它必然包含了一个ThreadLocal对象如下:

 private static final Logger log = LoggerFactory.getLogger(org/apache/shiro/util/ThreadContext);
    public static final String SECURITY_MANAGER_KEY = (new StringBuilder()).append(org/apache/shiro/util/ThreadContext.getName()).append("_SECURITY_MANAGER_KEY").toString();
    public static final String SUBJECT_KEY = (new StringBuilder()).append(org/apache/shiro/util/ThreadContext.getName()).append("_SUBJECT_KEY").toString();
    private static final ThreadLocal resources = new InheritableThreadLocalMap();
    /*省略*/
ThreadLocal中所存放的数据是一个Map集合,集合中所存的key有两个SECURITY_MANAGER_KEY 和SUBJECT_KEY ,就是通过这两个key来存取SecurityManager和Subject两个对象的。


3、当前线程还没有绑定一个Subject时,就需要通过Subject.Builder来创建一个然后绑定到当前线程。Builder是Subject的一个内部类,它拥有两个重要的属性,SubjectContext和SecurityManager,创建Builder时使用SecurityUtils工具来获取它的全局静态变量SecurityManager,SubjectContext则是使用newSubjectContextInstance创建一个DefaultSubjectContext对象:

  public Builder()
        {
            this(SecurityUtils.getSecurityManager());
        }

        public Builder(SecurityManager securityManager)
        {
            if(securityManager == null)
                throw new NullPointerException("SecurityManager method argument cannot be null.");
            this.securityManager = securityManager;
            subjectContext = newSubjectContextInstance();
            if(subjectContext == null)
            {
                throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' cannot be null.");
            } else
            {
                subjectContext.setSecurityManager(securityManager);
                return;
            }
        }
    }

4、 Builder准备工作完成后,调用buildSubject来创建一个Subject:

  public Subject buildSubject()
        {
            return securityManager.createSubject(subjectContext);
        }
最终还是通过securityManager根据subjectContext来创建一个Subject。最终是通过一个SubjectFactory来创建的,SubjectFactory是一个接口,接口方法为Subject createSubject(SubjectContext context),默认的SubjectFactory实现是DefaultSubjectFactory,DefaultSubjectFactory创建的Subject是DelegatingSubject。【至此,我们通过源码分析已知Subject创建流程


5、调用Subject.isPermitted/hasRole方法,其本质是委托给SecurityManager的hasRole方法。

   public boolean hasRole(String roleIdentifier)
    {
        return hasPrincipals() && securityManager.hasRole(getPrincipals(), roleIdentifier);
    }


6、 AuthorizingSecurityManager实现了Authorizer接口,但是AuthorizingSecurityManager是通过内部Authorizer引用来完成具体的功能,默认采用的是ModularRealmAuthorizer。如下:
public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager
{

    public AuthorizingSecurityManager()
    {
        authorizer = new ModularRealmAuthorizer();
    }
     public boolean hasRole(PrincipalCollection principals, String roleIdentifier)
    {
        return authorizer.hasRole(principals, roleIdentifier);
    }
    /*省略*/
}
来看看这个Authorizer模块的接口设计:
/*jadclipse*/// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) radix(10) lradix(10) 
// Source File Name:   Authorizer.java

package org.apache.shiro.authz;

import java.util.Collection;
import java.util.List;
import org.apache.shiro.subject.PrincipalCollection;

// Referenced classes of package org.apache.shiro.authz:
//            AuthorizationException, Permission

public interface Authorizer
{

    public abstract boolean isPermitted(PrincipalCollection principalcollection, String s);

    public abstract boolean isPermitted(PrincipalCollection principalcollection, Permission permission);

    public transient abstract boolean[] isPermitted(PrincipalCollection principalcollection, String as[]);

    public abstract boolean[] isPermitted(PrincipalCollection principalcollection, List list);

    public transient abstract boolean isPermittedAll(PrincipalCollection principalcollection, String as[]);

    public abstract boolean isPermittedAll(PrincipalCollection principalcollection, Collection collection);

    public abstract void checkPermission(PrincipalCollection principalcollection, String s)
        throws AuthorizationException;

    public abstract void checkPermission(PrincipalCollection principalcollection, Permission permission)
        throws AuthorizationException;

    public transient abstract void checkPermissions(PrincipalCollection principalcollection, String as[])
        throws AuthorizationException;

    public abstract void checkPermissions(PrincipalCollection principalcollection, Collection collection)
        throws AuthorizationException;

    public abstract boolean hasRole(PrincipalCollection principalcollection, String s);

    public abstract boolean[] hasRoles(PrincipalCollection principalcollection, List list);

    public abstract boolean hasAllRoles(PrincipalCollection principalcollection, Collection collection);

    public abstract void checkRole(PrincipalCollection principalcollection, String s)
        throws AuthorizationException;

    public abstract void checkRoles(PrincipalCollection principalcollection, Collection collection)
        throws AuthorizationException;

    public transient abstract void checkRoles(PrincipalCollection principalcollection, String as[])
        throws AuthorizationException;
}


/*
	DECOMPILATION REPORT

	Decompiled from: E:\webapp2\workspace\ShiroAuthentication\lib\shiro-core-1.2.4.jar
	Total time: 56 ms
	Jad reported messages/errors:
	Exit status: 0
	Caught exceptions:
*/
从上面的接口中,可以分成两大类,第一类是验证用户的某个或某些权限,第二类是验证用户的某个角色或某些角色。角色则是一组权限的集合,所以后者是粗粒度的验证,而前者是细粒度的验证。对于那些check方法则是验证不通过时抛出异常。
接口实现类图为:



可以看到很多的Realm都实现了该接口,即这些Realm不仅提供登陆验证,还提供权限验证。
先来看下默认使用的ModularRealmAuthorizer:

public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
    protected Collection realms;

    protected PermissionResolver permissionResolver;

    protected RolePermissionResolver rolePermissionResolver;
    //略
}
可以看到,它有三个重要属性,Realm集合和PermissionResolver 、RolePermissionResolver 。PermissionResolver 是什么呢?

package org.apache.shiro.authz.permission;

import org.apache.shiro.authz.Permission;

public interface PermissionResolver
{

    public abstract Permission resolvePermission(String s);
}
就是将权限字符串解析成Permission 对象,同理RolePermissionResolver 如下:
package org.apache.shiro.authz.permission;

import java.util.Collection;

public interface RolePermissionResolver
{

    public abstract Collection resolvePermissionsInRole(String s);
}
将角色字符串解析成Permission 集合


我们首先来看看ModularRealmAuthorizer类的几个方法:

public ModularRealmAuthorizer(Collection<Realm> realms) {
        setRealms(realms);
    }
public void setRealms(Collection<Realm> realms) {
        this.realms = realms;
        applyPermissionResolverToRealms();
        applyRolePermissionResolverToRealms();
    }
public void setPermissionResolver(PermissionResolver permissionResolver) {
        this.permissionResolver = permissionResolver;
        applyPermissionResolverToRealms();
    }
public void setRolePermissionResolver(RolePermissionResolver rolePermissionResolver) {
        this.rolePermissionResolver = rolePermissionResolver;
        applyRolePermissionResolverToRealms();
    }
protected void applyRolePermissionResolverToRealms() {
        RolePermissionResolver resolver = getRolePermissionResolver();
        Collection<Realm> realms = getRealms();
        if (resolver != null && realms != null && !realms.isEmpty()) {
            for (Realm realm : realms) {
                if (realm instanceof RolePermissionResolverAware) {
                    ((RolePermissionResolverAware) realm).setRolePermissionResolver(resolver);
                }
            }
        }
    }
protected void applyPermissionResolverToRealms() {
        PermissionResolver resolver = getPermissionResolver();
        Collection<Realm> realms = getRealms();
        if (resolver != null && realms != null && !realms.isEmpty()) {
            for (Realm realm : realms) {
                if (realm instanceof PermissionResolverAware) {
                    ((PermissionResolverAware) realm).setPermissionResolver(resolver);
                }
            }
        }
    }
以上这几个方法,其目的都是如果哪些Realm 想要PermissionResolver 或RolePermissionResolver 参数,则将ModularRealmAuthorizer 的对应参数传给它。

7、我们再来看看ModularRealmAuthorizer 是如何实现Authorizer接口的:

protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms();
        if (realms == null || realms.isEmpty()) {
            String msg = "Configuration error:  No realms have been configured!  One or more realms must be " +
                    "present to execute an authorization operation.";
            throw new IllegalStateException(msg);
        }
    }
public boolean isPermitted(PrincipalCollection principals, String permission) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }
首先是判断Collection<Realm> realms集合是否为空,然后就是将那些实现了Authorizer接口的Realm 来判断是否具有某个权限,也就是ModularRealmAuthorizer本身并不去权限验证,而是交给那些具有权限验证功能的Realm去验证(即那些Realm实现了Authorizer接口)。所以
ModularRealmAuthorizer并不具有太多实际内容,我们转战那些实现了Authorizer接口的Realm,去看看他们的验证过程。
这时候,就需要看Authorizer接口的另一个分支即下图AuthorizingRealm分支:


AuthorizingRealm涉及到Realm,所以再把Realm说清楚。Realm接口如下

public interface Realm {
    String getName();
    boolean supports(AuthenticationToken token);
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}
Realm 本身只具有验证用户是否合法的功能,不具有授权的功能。


AuthenticatingRealm及其子类主要完成认证流程,首先是其的初始化,AuthenticatingRealm及其子类都实现了Initializable接口,初始化的时候会首先获取其缓存,如下:

public final void init() {
        //trigger obtaining the authorization cache if possible
        getAvailableAuthenticationCache();
        onInit();
    }
private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
        Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
        boolean authcCachingEnabled = isAuthenticationCachingEnabled();
        if (cache == null && authcCachingEnabled) {
            cache = getAuthenticationCacheLazy();
        }
        return cache;
    }
private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() {

        if (this.authenticationCache == null) {

            log.trace("No authenticationCache instance set.  Checking for a cacheManager...");

            CacheManager cacheManager = getCacheManager();

            if (cacheManager != null) {
                String cacheName = getAuthenticationCacheName();
                log.debug("CacheManager [{}] configured.  Building authentication cache '{}'", cacheManager, cacheName);
                this.authenticationCache = cacheManager.getCache(cacheName);
            }
        }

        return this.authenticationCache;
    }
首先会获取Cache<Object, AuthenticationInfo> cache属性,如果没有,再判断是否允许缓存,如果允许,则通过CacheManager 来获取,之前已分析过,如果还没有则会创建一个Cache,然后返回。
再看下认证过程如下:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        if (info == null) {
            //otherwise not cached, perform the lookup:
            info = doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {
                cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {
            assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }
首先从缓存中尝试是否能找到AuthenticationInfo ,如果找不到,则需要子类去完成具体的认证细节,然后再存储到缓存中,因为本类并没有具体的数据源,只有缓存源,所以本类只是搭建了认证流程,具体的认证细节则由具体的子类来完成,所以 doGetAuthenticationInfo(token)是一个protected的抽象方法,如下:

protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
当缓存中存在或者子类进行具体的认证后,下一步的操作是要进行密码匹配的过程,AuthenticatingRealm有一个属性CredentialsMatcher credentialsMatcher,接口如下:

public interface CredentialsMatcher {
    boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}
就是匹配我们认证时的AuthenticationToken 和刚才已找到的AuthenticationInfo 是否匹配。有如下的实现类:AllowAllCredentialsMatcher、PasswordMatcher、SimpleCredentialsMatcher等等。AuthenticatingRealm的构造函数默认使用的是SimpleCredentialsMatcher:

public AuthenticatingRealm() {
        this(null, new SimpleCredentialsMatcher());
    }

    public AuthenticatingRealm(CacheManager cacheManager) {
        this(cacheManager, new SimpleCredentialsMatcher());
    }

    public AuthenticatingRealm(CredentialsMatcher matcher) {
        this(null, matcher);
    }
这一块内容先暂时不讲,后续文章再来详细说明。
当你匹配通过了,则就算认证成功了。认证流程就在AuthenticatingRealm中完成了。

我们再向它的子类AuthorizingRealm研究,这个就有涉及到授权的功能了。AuthenticatingRealm是将整个认证流程框架化,AuthorizingRealm则是将整个授权流程框架化,AuthorizingRealm也有授权缓存,所以会通过父类CachingRealm来获取CacheManager,同时也有一个子缓存开关authorizationCachingEnabled,和AuthenticatingRealm基本类似,属性如下:

public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {

    private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authorizationCache";

    private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();

    private boolean authorizationCachingEnabled;
    private Cache<Object, AuthorizationInfo> authorizationCache;
    private String authorizationCacheName;

    private PermissionResolver permissionResolver;

    private RolePermissionResolver permissionRoleResolver;

    public AuthorizingRealm() {
        this(null, null);
    }

    public AuthorizingRealm(CacheManager cacheManager) {
        this(cacheManager, null);
    }

    public AuthorizingRealm(CredentialsMatcher matcher) {
        this(null, matcher);
    }

    public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        super();
        if (cacheManager != null) setCacheManager(cacheManager);
        if (matcher != null) setCredentialsMatcher(matcher);

        this.authorizationCachingEnabled = true;
        this.permissionResolver = new WildcardPermissionResolver();

        int instanceNumber = INSTANCE_COUNT.getAndIncrement();
        this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        if (instanceNumber > 0) {
            this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
        }
    }
//略
}

AtomicInteger 同样是用于对那些具有授权功能的Realm进行数量统计的,authorizationCachingEnabled缓存子开关,authorizationCache缓存,authorizationCacheName缓存名字。 PermissionResolver permissionResolver、RolePermissionResolver permissionRoleResolver这两个则是对字符串进行解析对应的Permission和Collection<Permission>的。我们来看下AuthorizingRealm的主要功能,对于授权接口Authorizer的实现:

public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
        AuthorizationInfo info = getAuthorizationInfo(principal);
        return hasRole(roleIdentifier, info);
    }

首先就是获取授权信息,看下getAuthorizationInfo:

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            return null;
        }

        AuthorizationInfo info = null;

        if (log.isTraceEnabled()) {
            log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
        }

        Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
        if (cache != null) {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
            }
            Object key = getAuthorizationCacheKey(principals);
            info = cache.get(key);
            if (log.isTraceEnabled()) {
                if (info == null) {
                    log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                } else {
                    log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                }
            }
        }

        if (info == null) {
            // Call template method if the info was not found in a cache
            info = doGetAuthorizationInfo(principals);
            // If the info is not null and the cache has been created, then cache the authorization info.
            if (info != null && cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Caching authorization info for principals: [" + principals + "].");
                }
                Object key = getAuthorizationCacheKey(principals);
                cache.put(key, info);
            }
        }

        return info;
    }

同样很容易理解,先得到缓存,从缓存中去找有没有授权信息,如果没有,则需要子类去完成具体的授权细节即doGetAuthorizationInfo,授权完成后放置缓存中。同样doGetAuthorizationInfo是protected的抽象方法,由子类去实现。PermissionResolver permissionResolver、RolePermissionResolver permissionRoleResolver则是发挥如下作用:

private Collection<Permission> getPermissions(AuthorizationInfo info) {
        Set<Permission> permissions = new HashSet<Permission>();

        if (info != null) {
            Collection<Permission> perms = info.getObjectPermissions();
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }
            perms = resolvePermissions(info.getStringPermissions());
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }

            perms = resolveRolePermissions(info.getRoles());
            if (!CollectionUtils.isEmpty(perms)) {
                permissions.addAll(perms);
            }
        }

        if (permissions.isEmpty()) {
            return Collections.emptySet();
        } else {
            return Collections.unmodifiableSet(permissions);
        }
    }

即有了授权信息AuthorizationInfo 后,获取所有的权限Permission,有三种途径来收集,第一种就是info.getObjectPermissions() info中直接含有Permission对象集合,第二种就是info.getStringPermissions() info中有字符串形式的权限表示,第三种就是info.getRoles() info中含有角色集合,角色也是一组权限的集合,看下resolvePermissions(info.getStringPermissions()):

private Collection<Permission> resolvePermissions(Collection<String> stringPerms) {
        Collection<Permission> perms = Collections.emptySet();
        PermissionResolver resolver = getPermissionResolver();
        if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) {
            perms = new LinkedHashSet<Permission>(stringPerms.size());
            for (String strPermission : stringPerms) {
                Permission permission = getPermissionResolver().resolvePermission(strPermission);
                perms.add(permission);
            }
        }
        return perms;
    }

也很简单,对于每一个strPermission 通过PermissionResolver 转化成Permission 对象,对于resolveRolePermissions也同理,不再说明。这里具体的转化细节先暂且不说,后续再将。
现在终于把认证流程和授权框架流程大致说完了,即AuthenticatingRealm和AuthorizingRealm的内容,他们分别留给子类protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;具体的认证方法和protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)具体的授权方法。












本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Shiro源码分析-----认证流程/授权流程----------Subject 的相关文章

  • OpenGL-着色器

    OpenGL渲染架构 组成模块 OpenGL中的渲染架构主要分为两个模块 Client 上层代码和OpenGL API方法 这部分在CPU中运行 Server OpenGL底层的渲染处理 这部分在GPU中运行 流程 上层代码通过调用Open
  • torch.nn中GRU使用

    一 pytorch中GRU实现原理 对于输入序列中的每个元素 每层计算以下函数 r t W i
  • odoo中视图继承的xpath写法以及隐藏原来字段

    父级试图 xpath找到元素继承时 xpath和爬虫时候的xpath的语法略微不同 odoo中xpath 如下 div class app settings block 1 sheet notebook 3 page group group
  • 【Django REST framework电商项目笔记】第10章 购物车, 订单和支付宝支付功能(上)

    购物车接口实现 在交易 trade 应用上 在商品详情页点击加入购物车 弹出提示框 去结算 继续购物 右上角会新增商品到购物车 这是从后台取出来的数据 可以显示商品 数量 总价等信息 添加商品 在商品数量上加一 直接更新数量即可 注意sho
  • 【无标题】尤破金11.24黄金还会跌吗?黄金原油今日价格走势分析及操作解套

    黄金最新行情解析 黄金消息面解析 周三 11月24日 亚洲时段 现货黄金小幅上涨 交投于1791附近 周二 11月23日 金价强势跌破1800关口 录得四连跌 因市场加码押注美联储转鹰 美债收益率与美元继续保持强势 但疫情反弹可能提升一些黄
  • 安装DevEco Studio跑鸿蒙第一个应用

    下载 安装 https hmxt org deveco studio 安装启动 双击安装以后重启电脑 启动程序 Node 修改环境 Start using DevEco Studio Next Next Accept 开始下载 Finish
  • Python测试框架 Pytest —— mock使用(pytest-mock)

    pytest mock 安装 pip install pytest mock 这里的mock和unittest的mock基本上都是一样的 唯一的区别在于pytest mock需要导入mock对象的详细路径 weateher r py cla
  • Python 实现完整的 RSA 算法

    参考资料 25行代码实现完整的RSA算法
  • 运维面试大全

    文章目录 第一阶段 平常怎么处理故障 思路是什么样的 公网和私网分类以及范围 本机地址 网络地址 广播地址 交换机的工作原理 ICMP是什么干什么用的 它有哪些命令 TCP和UDP协议的区别 tcp有哪些控制位 分别是什么意思 你是用过哪些
  • STM32——串口概念及应用

    目录 通讯接口背景知识 两种通讯方式对比 串口通讯 常见的串行通信接口 STM32串口通信基础 STM32的串口通信接口 UART异步通信方式引脚连接方法 UART异步通讯方式特点 串口通信过程 串口异步通讯需要定义的参数 STM32常用串
  • 使用Tensorflow2.0执行视觉显著性检测(Visual Saliency Detection)

    使用Tensorflow2 0执行视觉显著性检测 Visual Saliency Detection 上一篇博客介绍了如何使用Python OpenCV执行视觉显著性检测 Visual Saliency Detection 这篇博客将介绍如
  • HTTP服务器(一)HTTP服务器入门介绍

    一 简介 1 1 做项目的时候 要请求REST服务器 而FEST服务器还是用http基本原理 即阉割版 来请求 1 2 请求端口EndPoint 请求方法Method 请求内容格式ContentType 请求的数类型PostData 一般为
  • C++ 代码评审最终指南——第 2 部分

    在第 1 部分中 我们对代码评审做了一个广泛讨论 这一部分将重点讨论 C 提供一个代码评审清单和一些最佳实践 您可以按任何顺序阅读 但是我们建议您先返回去阅读一下我们之前的帖子 C 评审最终清单 代码评审清单从来都不是全面的 因为要检查的问
  • linux 安装dotnet sdk

    linux 安装dotnet sdk 以dotnet6 0为例 1 下载linux内核对应的dotnet6 0 sdk文件 进入https dotnet microsoft com en us download dotnet 6 0 找到l
  • stn32cubemx生成代码有问题的解决方法

    问题1 生成代码时 有显示你的生成目录还说一个问题的问题 就是环境没装好 要装一个java的环境 即使你本来已经装了这个环境 但是还是出现了同样的问题 那就装我这个版本 我刚才使用这个版本就成功了 问题2 就是要下相应的固件 manage
  • pycharm、idea插件代理设置,插件安装

    pycharm和idea都是intellij的 所以插件安装是设置代理方法相似 以pycharm举例 1 已经安装的插件列表 2 查找要安装的插件 没有 会给出下载插件的链接地址 3 打开链接地址 弹出如下 搜索在线插件 左下角是http代

随机推荐

  • 14-----关于error while loading shared libraries: xxx.so: cannot open shared object file: No such file

    一 linux找不到动态链接库 so文件的解决方法 关于这种问题 很明显是自己生成的动态库在被使用时 无法找到准确的路径而导致的 1 问题分析 首先运行可以执行程序 若出现上述错误 说明找不到动态库的路径 我们需要使用ldd去查看哪个动态库
  • 在Cisco设备上配置接口速度和双工

    默认情况下 思科交换机将自动协商速度和双工设置 将设备 交换机 路由器或工作站 连接到 Cisco 交换机上的端口时 将发生协商过程 设备将就传输参数达成一致 当今的大多数网络适配器都支持此功能 在本文中 您将了解如何使用 NCM 应用程序
  • FPGA UltraScale+ 利用ICAP原语实现Multiboot功能

    例程参考 https blog csdn net xiaomingzi55 article details 124365631 1 这个贴子说的很清楚 唯一一点就是它是ICAP2 这样写是没问题的 1 对于BPI模式来说 可以通过RS 1
  • python Excel处理

    Python 前言 我是一个菜鸡 写文章是为了记录自己成长的过程 如有错误 请各位大佬轻喷 欢迎大家指正 基础知识不做赘述 下文介绍Excel办公自动化 openpyxl导入 安装步骤 win R 输入cmd 然后打出pip install
  • 单片机上云流程(使用STM32和esp8266构建物联网体系)

    1 首先是给esp8266烧录MQTT固件 咳咳 首先当然是准备一个esp8266 01s和对应的烧录器啊qwq 选择好要烧录进去的固件 没有的话去安信可官网下载 然后配置好对应的选项 点击ERASE先进行擦除 再点击START进行烧录 这
  • Makefile中使用Shell

    http blog csdn net zdl1016 article details 6448989 http blog csdn net absurd article details 636418 Makefile与Shell的问题 大概
  • FPGA程序上板调试问题

    FPGA程序上板调试问题 1 memory block 找不到coe文件 原因1 coe文件格式有问题 memory initialization radix 是数值格式 memory initialization vector 是初始化的
  • Neuronal Dynamics:第五章笔记

    Neuronal Dynamics 第五章笔记
  • 一文读懂C++的if与else判断语句

    说个明白 先从最简单的说起 if语句 if语句是C 中最简单的判断语句 if S 语句1 语句2 如果S为真 非零 则执行语句1 否则执行语句2 如果语句1就是单个语句 则可以这么写 if S 语句1 这里的分号不能少 或者这么写 if S
  • DevExpress v15.2.4帮助文档下载(全)

    原文地址 http www devexpresscn com Resources Documentation 498 html DevExpress v15 2帮助文档下载列表大全来啦 包含 net系列所有帮助文档 提供CHM和PDF两个版
  • 一个产品的风险预测怎么写_创业计划书中,项目风险评估怎么写?

    展开全部 首先需要了解在实施过程中我们可能碰到哪些风险 按照一般意义 我们常常所说的风险分为两大e69da5e6ba903231313335323631343130323136353331333436316264类 一种是不可预知的 一种是
  • STM32CubeMX定时器输出比较模式——输出相位可调矩形波

    1 介绍 STM32的定时器通道输出矩形波 可以使用PWM模式和输出比较模式 PWM模式能够产生频率和占空比可调的矩形波信号 但不能对信号的相位进行调节 使用输出比较模式 可以实现信号的相位调节和频率调节 但不能对信号的占空比进行调节 输出
  • 100天精通Python(基础篇)——第30天:数据容器-list列表索引

    从前往后索引 name list 0 name list 123 666 print name list 0 print name list 1 print type name list 从后向前索引 name list 1 name li
  • 【数据结构与算法】1.树、二叉树、字典树、红黑树

    文章目录 简介 1 树 Tree 2 二叉树 Binary Tree 2 1 二叉树数据结构 2 2 二叉树的三种遍历方式 3 二叉查找树 Binary Search Tree 3 1 二叉查找树的概念和定义 3 2 二分查找算法 4 字典
  • 手动安装Python第三库vtk库

    我们在使用py进行可视化操作时大概率会用到vtk库 一般方法是通过pip 安装 但是这玩意得看人品 本人人品十分不好 哈哈哈哈 所以我们就要自己下载轮子手动安装 附上第三方库地址 https www lfd uci edu gohlke p
  • 真实!大概五分之一的年轻人存款在一万元以内。

    近日 有调查称 大概五分之一的年轻人存款在一万元以内 10万元存款是一个 坎 存款超过10万就会超过53 7 的人 年轻人 存款 两个词碰撞在一起 引来了广泛的关注和讨论 你认为年轻人存款难吗 可以从以下几个角度发表你的看法 角度一 你的目
  • 计算机应用问题,计算机应用的现状与发展的问题

    计算机应用的现状与发展的问题 来源 职称阁时间 2018 07 27 11 59热度 这篇论文主要介绍的是计算机应用的现状与发展的问题的相关内容 本文作者就是通过对计算机的应用现状等内容做出详细的阐述与介绍 特推荐这篇优秀的论文供相关人士参
  • 【C++设计模式】依赖倒转原则

    2023年8月30日 周三上午 目录 概述 含义 举个简单的例子 传统做法 使用依赖倒转原则 代码说明 再举一个具体的例子 以生活为例 概述 依赖倒转原则 Dependency Inversion Principle DIP 是面向对象设计
  • Hadoop 端口

    1 系统 8080 80 用于tomcat和apache的端口 22 ssh的端口 2 Web UI 用于访问和监控Hadoop系统运行状态 Daemon 缺省端口 配置参数 HDFS Namenode 50070 dfs http add
  • Shiro源码分析-----认证流程/授权流程----------Subject

    本文转载自 认证流程和授权流程 源码分析的第二篇以Subject的初始化为题 一 回顾Apache Shiro创建Subject的步骤如下 1 获取SecurityManager工厂 此处使用Ini配置文件初始化SecurityManage