当前位置: 首页 > news >正文

SpringBoot-Shiro安全权限框架

Apache Shiro是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。
官网:

http://shiro.apache.org/

源码:

https://github.com/apache/shiro
在这里插入图片描述

Subject:代表当前用户或者当前程序,在Shiro中Subject是一个接口,他定义了很多认证授权的方法。
认证就是判断你这个用户是不是合法用户,授权其实就是你认证成功之后,你的权限能访问系统的那些资源。

SecurityManage:安全管理器,Subject去认证的时候,需要通过SecurityManage安全管理器来负责认证和授权
安全管理器又要通过Authenticator认证器进行认证,通过Authorizer授权器进行授权,通过SessionManag会话管理器进行会话管理,有没有发现他就相当于一个中介,他来接收这些事情,而干这些事情的不是他来做的。

Authenticator:认证器,Realm从数据库中去获取到用户信息,然后认证器来做身份认证来进行身份认证。
Authorizer:授权器,通过认证器认证权限之后,得通过授权器来判断这个用户身份有什么权限,他可以访问那些资源

Realm:相当于数据源,从Realm中获取到用户的数据,比如用户的数据在MYSQL数据库,那么Realm就需要从MYSQL数据库中去获取到用户的信息,然后来做身份认证。
在Realm中也有一些认证授权相关的操作。

SessionManager:会话管理器,不依赖web容器的session,所以shiro可以使用在非web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

SessionDAO:会话,比如要将Session存储到数据库,那么可以通过jdbc来存储到数据库。
在这里插入图片描述

一、引入依赖


<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.5.3</version>
</dependency>

二、shiro配置文件shiro.ini(用户名或者密码)

[users]
relaysec=123456

三、测试代码

public class ShiroDemo{public static void main(String[] args){//1.创建安全管理器对象DefaultSecurityManager securityManager = new DefaultSecurityManager();//2.给安全管理器设置realmsecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));//3.SecurityUtils给全局安全工具类设置安全管理器SecurityUtils.setSecurityManager(securityManager);//4.关键对象subject主体Subject subject = SecurityUtils.getSubject();//5.创建令牌UsernamePasswordToken token = new UsernamePasswordToken("relaysec","123456");try{subject.login(token);//用户认证System.out.println("登录成功");}catch(UnknownAccountException e){e.printStackTrace();System.out.println("认证失败: 用户名不存在~");}catch(IncorrectCredentialsException e){e.printStackTrace();System.out.println("认证失败: 密码错误~");}}
}

SpringBoot整合shrio

一、创建一个war项目

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-juli</artifactId><version>8.5.23</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--引入JSP解析依赖--><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><!--引入shiro整合Springboot依赖shiro-spring-boot-web-starter--><!--CVE-2020-1957 Shiro <= 1.5.1--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.4.2</version></dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shiro</groupId>-->
<!--            <artifactId>shiro-spring</artifactId>-->
<!--            <version>1.4.2</version>-->
<!--        </dependency>--><!--CVE-2020-11989 shiro < 1.5.3-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shiro</groupId>-->
<!--            <artifactId>shiro-web</artifactId>-->
<!--            <version>1.4.2</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shiro</groupId>-->
<!--            <artifactId>shiro-spring</artifactId>-->
<!--            <version>1.4.2</version>-->
<!--        </dependency>--><!--        <dependency>-->
<!--            <groupId>org.apache.shiro</groupId>-->
<!--            <artifactId>shiro-spring</artifactId>-->
<!--            <version>1.5.3</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shiro</groupId>-->
<!--            <artifactId>shiro-web</artifactId>-->
<!--            <version>1.5.3</version>-->
<!--        </dependency>--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.7.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.7.0</version></dependency>

二、创建ShiroConfig(@Configuration修饰)

配置3个bean,ShiroFilterFactory、DefaultWebSecurityManager、Realm

@Configuration
public class ShiroConfig implements EnvironmentAware{private Environment env;
}

①、创建ShiroFilter

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean setSecrityManager(defaultWebSecurityManager);//配置系统受限资源和系统公共资源
/**map的key值代表的是我们的资源,map的value值代表的是我们的权限authc代表我们是需要认证和授权的,anon代表我们不需要认证和授权其实代码审计去审的就是shiroConfig文件,看他的jar包,以及ShiroConfig配置文件
*/
Map<String,String> map = new HashMap<>();
//authc 请求这个资源需要认证和授权
map.put("/admin/**","anon");
map.put("/admin/users","authc");
map.put("/demo/**","anon");
map.put("/index.jsp","authc");
map.put("/hello/*", "authc");
map.put("/toJsonList/*","authc");shiroFilterFactoryBean.setLoginUrl("/login.jsp");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;

还有一种方式:

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager sessionManager) {
//构建ShiroFilterFactoryBean对象,负责创建过滤器工厂ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置登录路径shiroFilterFactoryBean.setLoginUrl("/login");    
//注意:必须设置SecuritManager
shiroFilterFactoryBean.setSecurityManager(sessionManager);
//设置访问未授权的需要跳转到的路径shiroFilterFactoryBean.setUnauthorizedUrl("/403");
//设置登录成功访问路径shiroFilterFactoryBean.setSuccessUrl("/");
//自定义的过滤设置注入到shiroFilter中shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());shiroFilterFactoryBean.getFilters().put("user", new UserAuthcFilter());
//定义map指定请求过滤规则Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);ShiroUtils.ignoreCsrfFilter(filterChainDefinitionMap);filterChainDefinitionMap.put("/**", "apikey, csrf, authc");return shiroFilterFactoryBean;
}

Shiro有两种方式可进行精度控制,一种是过滤器方式,根据访问的URL进行控制,该种方式允许使用*匹配URL,可以进行粗粒度控制;另一种是注解的方式,实现细粒度控制,但只能是在方法上控制,无法控制类级别访问。
过滤器的类型有很多,本文代码只用到anon和authc两种类型

定义一个Map类型的filterChainDefinitionMap,使用ShiroFilterChainDefinition来控制请求路径的鉴权与授权。

创建ShiroUtils类,自定义静态方法loadBaseFilterChain()和ignoreCsrfFilter()方法,判断哪些请求路径需要用户登录才能访问,哪些不需要登录就能访问,实现粗粒度控制。

②、创建安全管理器

将用户认证信息源设置到安全管理器

@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();defaultWebSecurityManager.setRealm(realm);return defaultWebSecurityManager;
}

还有一种方式:
管理内部组件实例,并通过它来提供安全管理的各种服务。
modularRealmAuthenticator是shiro提供的realm管理器,用来设置realm生效, 通过setAuthenticationStrategy来设置多个realm存在时的生效规则

@Bean(name="securityManager")
public DefaultWebSecurityManager securityManager(SessionManager sessionManager, MemoryConstrainedCacheManager memoryConstrainedCacheManager){DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();dwsm.setSessionManager(sessionManager);dwsm.setCacheManager(memoryConstrainedCacheManager);dwsm.setAuthenticator(modularRealmAuthenticator());return dwsm;
}

重写ModularRealmAuthenticator,只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息

@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());return modularRealmAuthenticator;
}

securityManager不用直接注入Realm可能导致事务失效
可以定义一个handleContextRefresh方法,利用监听去初始化,等到ApplicationContext加载完成之后,完成shiroReaml

@EventListener
public void handleContextRefresh(ContextRefreshedEvent event){ApplicationContext context = event.getApplicationContext();List<Realm> realmList = new ArrayList<>();LocalRealm localRealm = context.getBean(LocalRealm.class);LdapRealm ldapRealm = context.getBean(LdapRealm.class);realmList.add(LocalRealm);realmList.add(ldapRealm);context.getBean(DefaultWebSecurityManager.class).setRealms(realmList);
}

③、自定义Realm

@Bean
public Realm getRealm(){CustomerRealm customerRealm = new CustomerRealm();return customerRealm;
}/**自定义Realm一般继承AuthorizingRealm,然后实现getAuthenticationInfo()和getAuthorizationInfo()方法,来完成身份认证和权限获取。
*/
public class CustomerRealm extends AuthorizingRealm{/**用于授权PrincipalCollection 是一个身份集合首先通过getPrimaryPrincipal()得到传入的用户名,然后调用getAuthorizationInfo()方法。再根据用户名调用 UserService接口获取角色及权限信息,并将得到的用户roles放到authorizationInfo中,并返回。*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){String userId = (String) principals.getPrimaryPrincipal();return getAuthorizationInfo(userId,usserService);}public static AuthorizationInfo getAuthorizationInfo(String userId,UserService userService){SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();UserDTO userDTO = userService.getUserDTO(userId);Set<String> roles = userDTO.getRoles().stream().map(Role::getId).collect(Collectors.toSet());authorizationInfo.setRoles(roles);return authorizationInfo();}//用于验证账户和密码@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {/**//直接从参数中获取用户名和密码String username = token.getUsername();String password = String.valueOf(token.getPassword())*/System.out.println("=============");//从传过来的token获取用户名String principal = (String) token.getPrincipal();System.out.println("用户名"+principal);//假设从数据库中获得用户名,密码String password_db="123";String username_db="zhangsan";if (username_db.equals(principal)){
//            SimpleAuthenticationInfo simpleAuthenticationInfo =return new SimpleAuthenticationInfo(principal,"123", this.getName());}return null;}
}

还有方式:
展示一个LdapReam Bean,注解@DependsOn表示组件依赖,下图中表示依赖lifecycleBeanPostProcessor
LifecycleBeanPostProcessor用来管理shiro Bean的生命周期,在LdapReam创建之前先创建lifecycleBeanPostProcessor

@Bean
@DependsOn(lifecycleBeanPostProcessor)
public LdapRealm ldapRealm(){return new LdapRealm();
}@Bean(name="lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){return new LifecycleBeanPostProcessor();
}

三、Controller进行访问,登录成功之后转发到index.jsp,否则直接转发到login.jsp文件。

@RequestMapping("login")
public String login(String username,String password){Subject subject = Security.getSubject();try{//认证成功UsernamePasswordToken token = new UsernamePasswordToken(uername,password);subject.login(token);return "redirect:/index.jsp";}catch(UnknownAccountException e){e.printStackTrace();System.out.println("用户名错误");}catch(IncorrectCredentialsException e){e.printStackTrace();System.out.println("密码错误");}catch(Exception e){e.printStackTrace();System.out.println(e.getMessage());}return "redirect:/login.jsp";
}

漏洞复现

Shiro层面绕过之后,SpringBoot也需要解析路径的,所以如果Springboot版本过高的话,可能是复现不成功的。并且不能使用Springboot集成的shiro吗,那样子也有可能导致复现不成功

<groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.5.0</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.5.0</version>
</dependency>

ShiroConfig配置


LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();map.put("/login","anon");//anon 设置为公共资源  放行资源放在下面
//        map.put("/user/register","anon");//anon 设置为公共资源  放行资源放在下面
//        map.put("/register.jsp","anon");//anon 设置为公共资源  放行资源放在下面
//        map.put("/user/getImage","anon");map.put("/doLogin", "anon");map.put("/demo/**","anon");map.put("/unauth", "user");map.put("/admin/*","authc");//默认认证界面路径---当认证不通过时跳转shiroFilterFactoryBean.setLoginUrl("/login.jsp");shiroFilterFactoryBean.setFilterChainDefinitionMap(map);return shiroFilterFactoryBean;

Controller:

绕过方式: /demo/…;/admin/index
在这里插入图片描述
在这里插入图片描述

漏洞分析:
定位到PathMatchingFilterChainResolver类的getchain方法,这个方法是处理过滤的
首先调用getPathWithinApplication方法获取路径,跟进去。
来到getPathWithinApplication方法,继续跟进WebUtils的getPathWithinApplication方法
首先getContextPath方法获取工程路径,调用getRequestUri获取访问路径,跟进去getRequestUri方法
来到getRequestUri方法,首先从域中获取,获取不到的话,调用getRequestURI方法获取路径,获取的就是我们访问的//demo/…;/admin/users 这个路径,然后调用decodeAndCleanUriString方法进行处理。

来到decodeAndCleanUriString方法,通过indexOf方法,因为我们的路径中存在分号,所以他获取到的位置是第9个,

然后判断如果不等于-1的话,调用substring方法进行字符串截取,从0到9 包前不包后 ,也就是说分号不需要截取,截取出来的字符串就是//demo/…。然后返回上一个方法。
来到normalize方法,这里进行了字符的替换,

替换反斜线

替换 // 为 /

替换 /./ 为 /

替换 /…/ 为 /

然后返回。

回到getChain方法,首先判断如果url不等于null并且他的最后一位是 / 的话,进行字符串截取然后赋值,我们拿到的字符串路径是/demo/… 所以往下走。

然后循环遍历我们的map中的内容,就是我们在Shiroconfig中写的那些过滤的内容,然后进行一一匹配,最后匹配到/demo/**的时候,然后调用proxy方法,我们跟进去。

来到proxy方法,首先调用getChain方法获取到请求路径对应的过滤器,然后调用过滤器的proxy方法,来到proxy方法
来到proxy方法,首先创建了一个ProxiedFilterChain对象,这个对象是一个代理对象。

基本上到这里我们的原始请求就会进入到 springboot中. springboot对于每一个进入的request请求也会有自己的处理方式,找到自己所对应的controller。

我们定位到Spring处理请求的地方。我们跟进去getPathWithinApplication方法
到getPathWithinApplication方法,调用getContextPath方法获取到工程路径,调用getRequestUri获取访问路径,我们跟进getRequestUri方法
来到getRequestUri方法,首先从域中获取,获取不到的话然后通过getRequestURI方法获取到url,然后调用decodeAndCleanUriString方法,我们跟进去。
来到decodeAndCleanUriString方法,跟进removeSemicolonContent方法。
首先获取到分号的位置,然后while循环如果不等于-1的话,然后进行字符串截取,将我们的分号截取掉 然后返回的路径就是//demo…

回到decodeAndCleanUriString方法,调用decodeRequestString进行decode解码,然后调用getSanitizedPath方法进行过滤 //

然后返回。
回到getPathWithinApplication方法,可以发现我们的分号已经被去掉了。
到这里基本上的流程就结束了,可以发现在Spring中会过滤分号,而在Shiro中不会。导致权限绕过。

===================================================

应用案例登录认证

在这里插入图片描述

  1. 客户端提交用户账号和密码,在Controller中拿到账号和密码封装到token对象.
  2. 然后借助subject的login方法,把数据提交给SecurityManager
  3. 使用Authenticator处理token,Authenticator从Realm列表中获取LdapRealm
  4. LdapRealm从token中获取数据,交给authenticate进行比对,对比通过返回AuthenticationInfo

一、创建maven工程,并导入相关依赖

shiro-core commons-logging

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.2.5</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.2.5</version>
</dependency>
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>1.2.1</version>
</dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-quartz</artifactId></dependency>

登录控制器Controller

@PostMapping(value="/signin")
public ResultHolder login(@RequestBody LoginRequest request){SessionUser sessionUser = SessionUtils.getUser();if(sessionUser!=null){if(!StringUtils.equals(sessionUser.getId(), request.getUsername())){return ResultHolder.error(Translator.get("please_logout_current_user"));}SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LOCAL.name());}return userService.login(request);
}

在login方法中,把用户名和密码封装为UsernamePasswordToken对象token,然后通过SecurityUtils.getSubject()获取Subject对象,并将前面获取token对象作为参数。若调用subject.login(token)时不抛出任何异常,说明认证通过,调用subject.isAuthenticated()返回true表示当前的用户已经登录。后续可以根据subject实例获取用户信息。

public ResultHolder login(LoginRequest request) {String login = (String) SecurityUtils.getSubject().getSession().getAttribute("authenticate");String username = StringUtils.trim(request.getUsername());String password = "";if (!StringUtils.equals(login, UserSource.LDAP.name())) {password = StringUtils.trim(request.getPassword());……}UsernamePasswordToken token = new UsernamePasswordToken (username, password, login);Subject subject = SecurityUtils.getSubject();try {subject.login(token);if (subject.isAuthenticated()) {UserDTO user = (UserDTO) subject.getSession().getAttribute(ATTR_USER);……return ResultHolder.success(subject.getSession().getAttribute("user"));
} else {return ResultHolder.error(Translator.get("login_fail"));}
} catch (ExcessiveAttemptsException e) {throw new ExcessiveAttemptsException(Translator.get("excessive_attempts"));
}
……
}

=============================================================

案例二:

@RestController
@CrossOrigin
@RequestMapping("/")
public class LoginController{private static final Logger logger = LoggerFactory.getLogger(LoginController.class);@Reference //Dubbo远程调用的服务private UserService userService;@RequestMapping(value="/login",method=RequestMethod.POST)public ResponseEntity login(){//获取存储在系统的用户ShiroUser user = (ShiroUser)SecurityUtils.getSubject().getPrincipal();//为获取的用户添加tokenuser.setToken(SecurityUtils.getSubpect().getSession().getId().toString());return ResponseEntity.ok(user);}/**获取当前登陆人的信息(包括角色权限)*/@GetMapping("/logininfo")public ResponsseEntity loginInfo(){ShiroUser shiroUser = (ShiroUser) SecurityUtils.getSubject().getPrincipal();Map<String,Object> map = new HashMap<>();Set<String> permissions = Sets.newHashSet();//将获取的角色和权限存入指定的mapUser user = userService.getById(shiroUser.getUserId().intValue());map.put("roleList",user.getRoles());map.put("permissionList",permissions);map.put("userId",shiroUser.getUserId());map.put("username",shiroUser.getLoginName());return ResponseEntity.ok(map);}
}

======================================================

相关文章:

SpringBoot-Shiro安全权限框架

Apache Shiro是一个强大而灵活的开源安全框架&#xff0c;它干净利落地处理身份认证&#xff0c;授权&#xff0c;企业会话管理和加密。 官网&#xff1a; http://shiro.apache.org/ 源码&#xff1a; https://github.com/apache/shiro Subject&#xff1a;代表当前用户或…...

PostgreSQL基础语法

当谈到关系型数据库管理系统&#xff08;RDBMS&#xff09;时&#xff0c;PostgreSQL是一个备受推崇的选择。它是一个开源的、强大的RDBMS&#xff0c;具有广泛的功能和支持。本文将介绍一些PostgreSQL的基础语法&#xff0c;以帮助您入门。 1. 安装和配置 在开始使用PostgreS…...

编程前置:处理Excel表格,定位单元格位置,输入文字前,让AI机器人知道我说什么

原提问&#xff1a; input输入表头 &#xff08;input内除了/&#xff0c;空格 回车 标点符号等 全部作为单元格分隔符&#xff09; 由我设置input输入的是行or列 给选项 1. 行 2. 列 默认回车或没输入值是列由我设置起始位置行列 例如 3,2 表示3行2列 当我输入3,2 就表示在第…...

Linux基本指令介绍系列第四篇

文章目录 前言一、Linux基本指令介绍1、more指令2、less指令3、head指令4、tail指令5、bc指令6、管道文件介绍7、与时间相关的指令 总结 前言 本文介绍Linux使用时的部分指令&#xff0c;读者如果想了解更多基本指令的使用&#xff0c;可以关注博主的后续的文章。 博主使用的实…...

读取vivo手机截图尺寸移动.jpg等文件

这个代码的设计初衷是为了解决图片处理过程中的一些痛点。想象一下&#xff0c;我们都曾遇到过这样的情况&#xff1a;相机拍摄出来的照片、网络下载的图片&#xff0c;尺寸五花八门&#xff0c;大小不一。而我们又渴望将它们整理成一套拥有统一尺寸的图片&#xff0c;让它们更…...

Web前端-Vue2+Vue3基础入门到实战项目-Day2(指令补充, computed计算属性, watch侦听器, 水果购物车)

Web前端-Vue2Vue3基础入门到实战项目-Day2 指令补充指令修饰符v-bind 对样式控制的增强控制class案例 - 京东秒杀tab导航高亮控制style案例 - 控制进度条 v-model 应用于其他表单元素 computed计算属性基本使用computed计算属性 vs methods方法计算属性完整写法案例 - 成绩 wat…...

ffmpeg之去除视频水印

ffmpeg去除水印使用delogo视频滤镜。 delogo参数: x,y,w,h分别表示logo区域的左上角位置及宽度和高度&#xff1b; show:0表示不显示logo区域&#xff0c;1表示显示logo区域。 执行下面的命令&#xff1a; ffmpeg -i 1.mp4 -vf delogox300:y10:w80:h30:show0 out.mp4 效果…...

第二章 线性表

线性表 线性表的基本概念线性表的顺序存储线性表顺序存储的类型定义线性表基本运算在顺序表上的实现顺序表实现算法的分析 线性表的链接存储单链表的类型定义线性表的基本运算在单链表上的实现 其他运算在单链表上的实现建表删除重复结点 其他链表循环链表双向循环链表 顺序实现…...

Java 超高频常见字符操作【建议收藏】

文章目录 前言1. 字符串拼接2. 字符串查找3. 字符串截取4. 字符串替換5. 字符串分割6. 字符串比较7. 字符串格式化8. 字符串空格处理 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一…...

MongoDB数据库网站网页实例-编程语言Python+Django

程序示例精选 PythonDjangoMongoDB数据库网站网页实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjangoMongoDB数据库网站网页实例》编写代码&#xff0c;代码整洁&#xff0c;…...

开箱报告,Simulink Toolbox库模块使用指南(七)——S-Fuction Builter模块

S-Fuction Builter S-Fuction Builter模块&#xff0c;Mathworks官方Help对该部分内容的说明如下所示。 DFT算法的原理讲解和模块开发在前几篇文章中已经完成了&#xff0c;本文介绍如何使用S-Fuction Builter模块一步到位地自动开发DFT算法模块&#xff0c;包括建立C MEX S-Fu…...

spring-boot 操作 mongodb 数据库

如何使用 spring-boot 操作 mongodb 数据库 配置文件&#xff1a; spring:data:mongodb:host: 127.0.0.1database: fly_articleDbport: 27017# 可以采取 mysql 写法# uri: mongodb://127.0.0.1/fly_articleDb依赖信息: <?xml version"1.0" encoding"UTF-…...

JVM篇---第三篇

系列文章目录 文章目录 系列文章目录一、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?二、Java内存结构三、说说对象分配规则一、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文…...

建筑施工行业招投标资源众包分包系统站点开发

一款针对建筑、施工行业开发的程序系统平台&#xff0c;运营方可以招募企业发布招投标信息以及招聘信息。 核心功能&#xff1a;一、项目招投标众包发布和投标 企业可以根据自身资源或者实际需求发布参与招投标信息&#xff0c;程序后台可以管理、审核用户发布的信息。参与招…...

【Linux基础】Linux发展史

&#x1f449;系列专栏&#xff1a;【Linux基础】 &#x1f648;个人主页&#xff1a;sunny-ll 一、前言 本篇主要介绍Linux的发展历史&#xff0c;这里并不需要我们掌握&#xff0c;但是作为一个合格的Linux学习者与操作者&#xff0c;这些东西是需要了解的&#xff0c;而且…...

openGauss学习笔记-90 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用重试中止事务

文章目录 openGauss学习笔记-90 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用重试中止事务 openGauss学习笔记-90 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用重试中止事务 在乐观并发控制&#xff08;OCC&#xff09;中&…...

【Docker】搭建 Docker 镜像仓库

文章目录 前言&#xff1a;公有仓库和私有仓库公共镜像仓库私有镜像仓库 一、搭建 Docker 镜像仓库1.1 搭建简化版的镜像仓库1.2 搭建带有图形化界面的镜像仓库1.3 配置 Docker 信任地址 二、向私有镜像仓库推送和拉取镜像2.1 推送本地镜像到私有仓库2.2 拉取私有仓库中的镜像 …...

Python数据攻略-Pandas的数据计算、拼接与可视化

如何将数据转化为有用的信息?在数据分析的世界里,仅仅拥有大量数据是不够的。需要有方法去“翻译”这些数据,让它们告诉我们一些有用的信息。 本篇文章要探讨的内容:如何使用Pandas进行数据计算、拼接和可视化,从而让数据“说话”。 文章目录 Pandas的数据计算基本数学运…...

【计算机网络】HTTPS协议详解

文章目录 一、HTTPS协议 介绍 1、1 HTTP协议不安全的体现 1、2 什么是 HTTPS协议 二、加密的一些概念 2、1 怎么理解加密 2、2 为什么要加密 2、3 常见的加密方式 2、2、1 对称加密 2、2、2 非对称加密 三、HTTPS协议探究加密过程 3、1 只使用对称加密 3、2 只是用非对称加密 3…...

Septentrio接收机二进制的BDS b2b改正数解码

Galileo的HAS和BDS B2b改正数为实时PPP提供了可能&#xff0c;要实现实时PPP解算&#xff0c;必须对对应的数据进行解码。由于没有做过解码的工作&#xff0c;现结合qzsl6tool代码对Septentrio的解码代码进行学习。 1. 二进制枕头的识别和解码 定义一个读取数据的类&#xff…...

nvm 管理 node版本

下载地址 https://nvm.uihtm.com/download.html 基础命令 查看所有可安装的node版本 nvm list available 查看本地已经安装的所有版本&#xff1a; nvm list 安装指定的node版本 nvm install 14.18.1 使用指定node版本 nvm use 14.18.1 卸载指定node版本 nvm uninstall …...

LeetCode 15.三数之和

三数之和 问题描述 LeetCode 15.三数之和 给你一个整数数组 nums&#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k&#xff0c;同时还满足 nums[i] nums[j] nums[k] 0。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答…...

Linux实用操作(固定IP、进程控制、监控、文件解压缩)

目录 一、快捷键 1、ctrl c强制停止 2、ctrl d退出或登出 3、历史命令搜索history 4、光标移动快捷键 5、清屏 二、软件安装 1、CentOS的yum命令 2、Ubantu的apt命令 三、systemctl命令 四、软连接 五、日期、时区 1、date命令 2、修改Linux时区为东八区 3、nt…...

Redis高可用之哨兵模式、集群

文章目录 一、Redis哨兵模式1.1 简介1.2 哨兵模式的作用1.3 哨兵结构1.4 故障转移机制&#xff08;重要&#xff09;1.5 主节点选举机制 二、部署Redis哨兵模式Step1 修改 Redis 哨兵模式的配置文件&#xff08;所有节点操作&#xff09;Step2 实现基于VIP&#xff08;虚拟IP&a…...

Python数据攻略-DataFrame的创建与基础特性

在数据分析、科学计算或者任何需要处理表格数据的领域,DataFrame都是一个非常重要的工具。就像Excel让处理表格数据变得简单一样,DataFrame也有类似的功能,但更加强大,特别是在处理大量数据时。了解DataFrame不仅能帮你更高效地处理数据,还能让你更容易进行数据清洗、可视…...

【word】从正文开始设置页码

在写报告的时候&#xff0c;会要求有封面和目录&#xff0c;各占一页。正文从第3页开始&#xff0c;页码从正文开始设置 word是新建的 分出三节&#xff08;封面、目录、正文&#xff09; 布局--->分割符--->分节符--->下一页 这样就能将word分为3节&#xff0c;分…...

计算机网络 快速了解网络层次、常用协议、常见物理设备。 掌握程序员必备网络基础知识!!!

文章目录 0 引言1 基础知识的定义1.1 计算机网络层次1.2 网络供应商1.3 猫、路由器、交换机1.4 IP协议1.5 TCP、UDP协议1.6 HTTP、HTTPS、FTP协议1.7 Web、Web浏览器、Web服务器 2 总结 0 引言 在学习的过程中总是会对IP、TCP、UDP、HTTP、HTTPS、FTP这些常见的协议不熟悉&…...

CUDA 安装

查看自己电脑的cuda版本&#xff1a;见文章 查看CUDA版本 我的是&#xff1a; 他的意思就是说&#xff1a;俺的显卡支持的cuda版本是12.0的&#xff08;向下兼容&#xff09; 然后我的项目tensorflow-gpu版本是1.13.2版本的&#xff0c;对应的cuda为10&#xff1a; &#xff…...

Springboot+vue的在线试题题库管理系统(有报告),Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的在线试题题库管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的在线试题题库管理系统&#xff0c;采用M&…...

【简单的留言墙】HTML+CSS+JavaScript

目标&#xff1a;做一个简单的留言墙 1.首先我们用HTML的一些标签&#xff0c;初步构造区域 样式。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>留言墙</title><style>/* ...... */ …...

网站建设如何投放广告/怎么可以让百度快速收录视频

原文发布时间为&#xff1a;2008-11-08 —— 来源于本人的百度文章 [由搬家工具导入]<html> <head> <title>js</title> </head> <body><input type"button" name"goto" Value"Back" onClick"histor…...

邯郸做移动网站多少钱/品牌推广策略有哪几种

自己动手做MGX [教程] 自己动手做MGX之一——认识MGX和MPKGbox2.0 本帖最后由 robertchan 于 2009-10-17 13:43 编辑 * h2 U, K, R“ ~1 A o# v; m ! D- ^- v; f2 _5 c) ? i 此教程完全抛砖引玉&#xff0c;激发大家的创作兴趣。 j/ ?4 j g$ a5 ?) ) ^所以&#xff0c;mgx的…...

金融类网站模板/电子商务网站建设与维护

大家好&#xff0c;我们今日继续讲解VBA代码解决方案的第139讲内容&#xff1a;理解Word的段落标记以及如何通过VBA来设置段落的标记。当通过VBA处理文本时&#xff0c;我们必须理解 Word中段落标记的意义。其实&#xff0c;Word 文档就是一个巨大的字符流。这种字符流是单词&a…...

wordpress 兼容php7/销售人员培训课程有哪些

个人认为现在市面上不用编写代码就能轻松制作生成 h5 页面的工具都是一些噱头&#xff0c;其大致都是套用现成的模板&#xff0c;替换里面的文字或者图片&#xff0c;作为一个开发者使用类似网站这些功能根本不能真正意义上达到自己想要的效果&#xff0c;目前这类工具以邀请函…...

中国市政建设局网站/品牌营销策略包括哪些内容

运行环境:Python3.4 # -*- coding: utf-8 -*-#datetime类#datetime是date与time的结合体&#xff0c;包括date与time的所有信息。#它的构造函数如下&#xff1a;#datetime. datetime (year, month, day[ , hour[ , minute[ , second[ , microsecond[ , tzinfo] ] ] ] ] )#各参数…...

网站建设需要多久/百度seo优化怎么做

感知器&#xff1a; https://www.zybuluo.com/hanbingtao/note/433855 线性单元和梯度下降 &#xff1a; https://www.zybuluo.com/hanbingtao/note/448086 神经网络和反向传播算法 &#xff1a; https://www.zybuluo.com/hanbingtao/note/476663 卷积神经网络&#xff1a; htt…...