springboot security 权限校验_十二、SpringBoot 优雅的集成Spring Security

前言

至于什么是Spring security ,主要两个作用,用户认证和授权。即我们常说的,用户只有登录了才能进行其他操作,没有登录的话就重定向到登录界面。有的用户有权限执行某一操作,而有的用户不能执行则是授权。算是一个项目安全框架。和shiro 框架一样。二者的不同大家可以百度小。Spring security 是Spring家族的一员,所以Springboot算是对Spring security 进行的天然的支持。

之所以这样说,spring security 被人诟病的配置繁琐复杂,在springboot中变的简单起来。如果我们只是demo 效果,可以做到0配置实现。

下面我们就一起来见识一下吧

依赖

我们在pom.xml 文件中引入Spring security 的statter

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

测试

我们先来0配置的看看。引入依赖以后,我们创建一个HelloController 内容如下:

@RestController
public class HelloController {@RequestMapping("/hello")public String hello(){return "hello world";}
}

v2-a2cf7c38fd003a8f63ea0d2f7fb81798_b.jpg

然后我们启动项目,按照我们正常的理解,直接访问

localhost:8080/hello 

会返回hello world 。但结果却是重定向到了/login 。下面的界面是Spring security 自带的。

v2-3b3d1910f17908097cdd88d4854a26d4_b.jpg

其实上面可以看到,Spring security 已经起作用了,没有登录不能访问 /hello 接口。

默认的用户名为 user;密码在我们项目启动时控制台有打印,每次都会不一样,随机生成的。

v2-c89055584dd32adec7454bd56886ddc7_b.jpg

我们输入账号密码,再试试

v2-b19b77df728d5a071e2b70d33bbc1ac9_b.gif

可以看到,在登录之后,我们在请求 /hello 会直接返回hello world , 那是不是只要登录一次,后面就可以一直访问呢?当然不是的,登录成功之后,会将信息保存在session 中,再登录的时候,就会通过session 校验,这样就可以访问到了,当session过期获取我们手动清理掉后,就需要重新登录了。我们来试试。打开控制台,application 中的cookies 中的jsessionid 清理掉。

v2-c8de948531c023d14d9f52e0014c156e_b.jpg

我们接着请求试试,可以发现删除后,就会重新回到登录界面。

v2-cbcb4b78e7f89d20d5f73dda50b8cbb5_b.gif

简单配置用户和密码

上面我们使用的默认的用户名和密码,但是实际上我们肯定不会这么做的,上面只是说明springboot 完全的集成了Spring security 。下面我们先来简单的配置用户名密码,之所以这样说,因为我们实际过程中应该还是不会这么用的。之所以要讲,让大家了解的更全面,也为下面铺垫。

application.properties 中配置

首先我们来简单的,我们可以直接在application.properties 中配置用户名和密码。来代替默认用户名和密码的效果。

spring.security.user.name=quellanan
spring.security.user.password=123456
spring.security.user.roles=admin

分别是设置用户名,密码,角色。我们这里暂时只用了用户认证,所以角色设不设置无所谓。配置好这些之后我们重启项目在界面上试试再。

v2-850473462ab05e42b7a642f41eba1117_b.gif

没有问题,但是没有什么用,我们实际中是不会这么干的吧。

内存中配置

在内存中配置的话,相对来说要复杂点,我们创建一个config 包,在包下创建SecurityConfig 类继承 WebSecurityConfigurerAdapter

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()) // 指定加密方式.withUser("qaz").password(passwordEncoder().encode("123456")).roles("admin").and().withUser("test").password(passwordEncoder().encode("123456")).roles("USER");}@Beanpublic PasswordEncoder passwordEncoder() {// BCryptPasswordEncoder:Spring Security 提供的加密工具return new BCryptPasswordEncoder();}
}

这里我们重写了configure(AuthenticationManagerBuilder auth) 方法,就是将定义的用户配置到内存中。这里有一个问题需要说明一下,就是这里配置的话,密码需要用BCryptPasswordEncoder 加密。如果不加密的话,项目编译启动不会报错,但是登陆的时候就会提示账号密码错误。还有一个问题就是,如果我们在这配置了,那我们在application.peoperties 中配置的就会失效。

v2-63ee1d0c6a6d80bfe921b061c229e151_b.gif

上面说的这两种方法,其实都是不常用的,我们在实际项目中根本不会在项目中写死用户信息的。基本上都是存在数据库中。所以下面我们就开始讲解我们最常用的模式吧。

由于这一类,涉及的较多,就单独一级标题出来,不放在二级标题里面了。

从数据库进行用户认证

既然是用到数据库,项目中自然要引入数据的配置啦,我这里用的是mysql 和mybatis.这是整个项目成型后的目录结构,先放出来,大家心里有底,然后一步一步的来。

v2-78b9e13d2f91435e13312491e4102c3a_b.jpg

建库建表

简单的三张表,user,roles,roles_user 。

v2-83adcfe8cd978e188b264e4b47dd1d69_b.jpg

下面是 sql。直接执行就可以

/*
Date: 2017-12-26 18:36:12
*/CREATE DATABASE `quellanan` DEFAULT CHARACTER SET utf8;USE `quellanan`;SET FOREIGN_KEY_CHECKS=0;-- ----------------------------
-- Table structure for roles
-- ----------------------------
DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(32) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of roles
-- ----------------------------
INSERT INTO `roles` VALUES ('1', '超级管理员');
INSERT INTO `roles` VALUES ('2', '普通用户');
INSERT INTO `roles` VALUES ('3', '测试角色1');
INSERT INTO `roles` VALUES ('4', '测试角色2');
INSERT INTO `roles` VALUES ('5', '测试角色3');-- ----------------------------
-- Table structure for roles_user
-- ----------------------------
DROP TABLE IF EXISTS `roles_user`;
CREATE TABLE `roles_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`rid` int(11) DEFAULT '2',`uid` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `rid` (`rid`),KEY `roles_user_ibfk_2` (`uid`),CONSTRAINT `roles_user_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`),CONSTRAINT `roles_user_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=131 DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of roles_user
-- ----------------------------
INSERT INTO `roles_user` VALUES ('1', '1', '1');
INSERT INTO `roles_user` VALUES ('2', '2', '2');
INSERT INTO `roles_user` VALUES ('3', '3', '3');
INSERT INTO `roles_user` VALUES ('4', '1', '4');-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(64) DEFAULT NULL,`nickname` varchar(64) DEFAULT NULL,`password` varchar(255) DEFAULT NULL,`enabled` tinyint(1) DEFAULT '1',`email` varchar(64) DEFAULT NULL,`userface` varchar(255) DEFAULT NULL,`regTime` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'quellanan', '', '$2a$10$Hv0YGLi/siOswCTP236MtOTWbClcM6rN1LCyqwfRmrwCJZqXHsj5a', '1', 'quellanan@qq.com','', '2017-12-08 09:30:22');INSERT INTO `user` VALUES ('2', 'qaz', '', '$2a$10$6H69XLebCrGhHeHzDXEoH.0x8tMFS0XfdDPwI5s.Eu9pbqRpncA.G', '1', 'quellanan@qq.com','', '2017-12-08 09:30:22');INSERT INTO `user` VALUES ('3', 'wsx', '', '$2a$10$6H69XLebCrGhHeHzDXEoH.0x8tMFS0XfdDPwI5s.Eu9pbqRpncA.G', '1', 'quellanan@qq.com','', '2017-12-08 09:30:22');INSERT INTO `user` VALUES ('4', 'test', '', '$2a$10$6H69XLebCrGhHeHzDXEoH.0x8tMFS0XfdDPwI5s.Eu9pbqRpncA.G', '1', 'quellanan@qq.com','', '2017-12-08 09:30:22');SET FOREIGN_KEY_CHECKS=1;

pom.xml 增加依赖

我们首先在原先pom 文件基础上增加,如下依赖。

        <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

前面三个是mysql 和mybatis的依赖。lombok 是一个工具类插件。

同时我们需要修改一下pom 文件中的build ,不然我们项目可能会找不到mybatis 的xml文件。

    <build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources</directory></resource></resources><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

配置application.properties

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/quellanan?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5

这里如果想要打印mybatis 的sql 日志。可以添加一个mybatis-config.xml文件,和application.properties 同目录

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><setting name="logImpl" value="STDOUT_LOGGING" /></settings>
</configuration>

并在application.properties 中加上

mybatis.config-location=classpath:/mybatis-config.xml

entry

我们在entry 包下创建 RoleEntry。代码如下:

@Getter
@Setter
public class RoleEntry {private Long id;private String name;
}

我们在创建 UserEntry ,但是UserEntry 比较特殊,因为我们需要使用Spring security 。所以这里,UserEntry 需要实现 UserDetails。代码如下:

@Setter
@Getter
public class UserEntry implements UserDetails {private Long id;private String username;private String password;private String nickname;private boolean enabled;private List<RoleEntry> roles;private String email;private String userface;private Timestamp regTime;/*** 获取角色权限* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> authorities = new ArrayList<>();for (RoleEntry role : roles) {authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));}return authorities;}/*** 获取密码* @return*/@Overridepublic String getPassword() {return password;}/*** 获取用户名* @return*/@Overridepublic String getUsername() {return username;}/*** 用户账号是否过期*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** 用户账号是否被锁定*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** 用户密码是否过期*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** 用户是否可用*/@Overridepublic boolean isEnabled() {return enabled;}
}

可以看到,基本上都是重写的方法。也比较简单。

mapper

这里我将xml 文件和接口放在一起了,你们也可以在resources 中创建一个mapper,将xml 文件放在哪里。mapper层没有什么好说的,是mybatis 的一些知识,我们这里讲代码贴出来。

RolesMapper

@Mapper
public interface RolesMapper {List<RoleEntry> getRolesByUid(Long uid);
}

RolesMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zlflovemm.security.mapper.RolesMapper"><select id="getRolesByUid" parameterType="long" resultType="com.zlflovemm.security.entry.RoleEntry">SELECT r.* FROM roles r,roles_user ru WHERE r.`id`=ru.`rid` AND ru.`uid`=#{uid}</select>
</mapper>

UserMapper

@Mapper
public interface UserMapper {UserEntry loadUserByUsername(@Param("username") String username);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zlflovemm.security.mapper.UserMapper"><select id="loadUserByUsername" resultType="com.zlflovemm.security.entry.UserEntry">SELECT * FROM user WHERE username=#{username}</select>
</mapper>

service

在service 层我们要注意一点,我们需要实现 UserDetailsService 接口。我们先创建一个UserService 继承 UserDetailsService。然后创建一个UserServiceImpl 来时实现UserService 从而达到实现UserDetailsService的目的。这样做是为了保证项目结构的统一层次。

UserService

public interface UserService extends UserDetailsService {
}

UserServiceImpl

@Service
@Slf4j
@Transactional
public class UserServiceImpl implements UserService {@AutowiredUserMapper userMapper;@AutowiredRolesMapper rolesMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {UserEntry user = userMapper.loadUserByUsername(s);if (user == null) {//避免返回null,这里返回一个不含有任何值的User对象,在后期的密码比对过程中一样会验证失败return new UserEntry();}//查询用户的角色信息,并返回存入user中List<RoleEntry> roles = rolesMapper.getRolesByUid(user.getId());user.setRoles(roles);return user;}
}

可以看到,主要是为了实现 loadUserByUsername的方法。在这个方法中我们 loadUserByUsername和getRolesByUid 就是我们在mapper 定义的查询数据库数据的方法。

SecurityConfig

前面做了这么多,其实都是准备工作,主要的目的就是提供一个Bean 。做完上面这些,我们再回到 SecurityConfig 中,其实我们现在需要修改的很少了。我们将用户写在内存的方法注释掉。通过数据库查询。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredUserService userService;@Beanpublic PasswordEncoder passwordEncoder() {// BCryptPasswordEncoder:Spring Security 提供的加密工具return new BCryptPasswordEncoder();}@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(passwordEncoder());//passwoldEncoder是对密码的加密处理,如果user中密码没有加密,则可以不加此方法。注意加密请使用security自带的加密方式。}}

可以和开始的 SecurityConfig 文件对比下,其实你就是多了一个userService,然后configure(AuthenticationManagerBuilder auth)中是通过userService 进行校验的。

测试

好了,其实到这里,我们就已经完成了,我们启动项目,就可以看到和之前写在内存中达到一样的效果。

过滤

以为到这就完了,其实还有一点哈哈。我们现在是所有的接口都需要先登录才能访问,没有登录的话就跳转到login界面。实际上我们肯定有些是不需要认证也可以访问的,比如以下静态文件或者注册的请求。所以我们还是要配置一下过滤。

其实也很简单,一样的在 SecurityConfig 文件中 重写 configure(HttpSecurity http) 方法。这里我直接参考官网上的。https://spring.io/guides/gs/securing-web/

v2-0aef7a85bda007ca94abdfd5035d8fd2_b.jpg

该configure(HttpSecurity)方法定义应保护哪些URL路径,不应该保护哪些URL路径。具体而言,“ /”和“ / home”路径配置为不需要任何身份验证。所有其他路径必须经过验证。用户成功登录后,他们将被重定向到之前要求身份验证的页面。有一个由指定的自定义“ /登录”页面loginPage(),每个人都可以查看它。

我们代码中 把 loginPage("/login") 注释掉就好了,如果不注释的话,就需要我们自己写login 界面和请求。我们这里就用框架自带的。

    @Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/", "/hello").permitAll().anyRequest().authenticated().and().formLogin()//.loginPage("/login").permitAll().and().logout().permitAll();}

这样配置就说明 /hell 和 / 请求不会拦截,其他的请求,需要先登录才能访问。为了更方便的看到效果,我们在HelloController 中再加两个方法

    @RequestMapping("/hello2")public String hello2(){return "hello adada";}@RequestMapping("/")public String hello3(){return " qazqeee";}
}

现在我们启动来看下效果。

v2-825a2fe1c5d176e04b189b70a318de90_b.gif

证明我们配置的过滤是有效果的。

番外

到此算是差不多结束了,其实还有很多知识点,不是一篇文章能讲完的,这里算是抛转引玉,希望对大家有帮助。后面我也会持续更新

好了,源码我上传到github 上啦https://github.com/QuellanAn/security

后续加油♡

欢迎大家关注个人公众号 "程序员爱酸奶"

分享各种学习资料,包含java,linux,大数据等。资料包含视频文档以及源码,同时分享本人及投递的优质技术博文。

如果大家喜欢记得关注和分享哟❤

http://weixin.qq.com/r/lSpcRJ-EBsJ5rdsn93_H (二维码自动识别)

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

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

相关文章

python-week5

1、现在有如下流程&#xff1a;A->B->C->D->E->F&#xff0c;要求根据输入的参数从制定的流程执行下去 """ File: 01流程执行.py Author: chde_wang Date: 2020-08-16 18&#xff1a;51&#xff1a;35 Description:将输入存储到列表中&#xf…

excel学习1-查看一列数值是否在另一列中存在

出现次数用COUNTIF 是否出现使用 IFCOUNTIF 加入$的作用是将需要比对的列固定范围。写好一个函数后&#xff0c;使用右下角号&#xff0c;直接下拉就可以。 参考自https://blog.csdn.net/gywtzh0889/article/details/72910377

fx5u以太网通讯设置_操作示例 | 实现S7300和FX5U的数据交换

北辰BCNet-S7300Plus(以下简称BCNet)&#xff0c;在BCNet-S7300的基础上&#xff0c;以太网口增加了支持与西门子带网口PLC(S7-200SMART 、S7-1200、S7-1500、S7300PN)之间的通讯功能。这些通讯功能包括基于S7TCP协议的数据交换、ModbusTCP服务器功能、ModbusTCP客户机功能&…

excel基础-固定某一列的输入内容

Office&#xff1a;需要固定的单元格-点击数据-数据有效性-点击&#xff0c;选择有效性条件 允许为&#xff1a;序列&#xff0c;然后需要下拉框内容&#xff0c;并选择下拉框。即可 切记&#xff1a;开源中的逗号为英文逗号。 最后效果如下&#xff1a; 参考自https://jingya…

为什么多个线程不可能同时抢到一把锁_HFL Redis_12_redis分布式锁的3种实现方式...

HotFrameLearning(简称 HFL) Redis_12_redis分布式锁的3种实现方式-一、大致介绍1、昨天介绍完redis的数据结构后&#xff0c;有小伙伴让本人讲讲redis的分布式锁&#xff0c;因此才有了此篇的由来&#xff0c;只是把我的节奏提前了而已&#xff1b;2、接下来我就通过几种方式介…

linux下mysql5.7修改密码

开始修改MySQL用户密码 ① 输入use mysql回车&#xff1b; ② 输入update user set password password(“147258”)where user’root’; “147258”为你自己设置的密码&#xff1b; 执行第二步后如果报错&#xff1a; 执行后报错  ERROR 1…

react项目中的参数解构_重构复杂的React组件:编写高效且可读组件的5个最佳实践...

随着 React.js 的不断进化&#xff0c;现在的它已经成为 Web 组件中最受欢迎的视图库之一。但是你手中的它&#xff0c;是否真的能够正常工作呢&#xff1f;本文将主要描述 5 个关于React 组件的最佳实践&#xff0c;希望对正在关注 React 组件的你有所帮助。我自己是一名从事了…

robotFramework-ride使用1-关键字驱动与数据类型

RF项目结构 项目文件夹&#xff08;new project&#xff09; 用例集&#xff08;test suit&#xff09; 测试用例&#xff08;test case&#xff09; RF基本功能-设置区介绍 项目 --》settings----》Document&#xff1a;添加用例说明 suit setup&#xff1a;执行此测试套件…

opencv threshold_基于深度学习OpenCV与python进行字符识别

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达当我们在处理图像数据集时&#xff0c;总是会想有没有什么办法以简单的文本格式检索图像中的这些字符呢&#xff1f;今天我们就一起看看有没有什么简单的办法可以实现这一…

python3 robotframework+pycharm框架搭建

1.安装python3 框架组成&#xff1a;自动化基础库&#xff08;robotframework&#xff09;图形界面&#xff08;robotframework-ride&#xff09; 2.安装RF框架 安装robotframework。pip install robotframework -i https://pypi.doubanio.com/simple 安装robotframework-r…

delphi if多个条件_判断(if)语句

判断&#xff08;if&#xff09;语句目标开发中的应用场景if 语句体验if 语句进阶综合应用01. 开发中的应用场景生活中的判断几乎是无所不在的&#xff0c;我们每天都在做各种各样的选择&#xff0c;如果这样&#xff1f;如果那样&#xff1f;……程序中的判断if 今天发工资: ​…

excel sheet限制_Python 处理Excel内的数据(案例介绍*3)

点击上方“蓝字”关注我们了解更多精彩案例一介绍现在有一批电商产品跟当日销量的数据&#xff0c;如下&#xff0c;总共有上万笔的数据&#xff0c;现在需要统计每个品牌当日的销售量&#xff0c;比如美宝莲今天总共卖出了多少的商品&#xff0c;另外需要统计每个品牌下面的每…

robotFramework-ride使用2-分支与循环

1条件运算 Run keyword if 如果条件为True&#xff0c;则执行关键字 ELSE IF ELSE IF使用案例1&#xff1a; 2循环 在使用append to list前&#xff0c;添加Collections库 列表元素添加与删除 列表元素求和-循环 :FOR ${i} IN 1 2 3 4 5 :FOR ${i} IN RANGE 1 5FOR里面…

项目案例:在线拍卖系统_冀拓公司在张家口开展尾矿库在线监测监控系统 建设项目...

近日&#xff0c;受张家口弘锦矿业有限责任公司委托&#xff0c;冀拓公司在弘锦矿业大东沟尾矿库开展了安全风险智能在线监测监控系统施工安装服务。由于该库为“头顶库”&#xff0c;按照省应急管理厅要求“头顶库”必须在6月底之前完成联网&#xff0c;在6月24日接到项目任务…

Linux 两台服务器之间传输文件和文件夹

今天处理一个项目要迁移的问题&#xff0c;突然发现这么多图片怎么移过去&#xff0c;可能第一时间想到的是先从这台服务器下载下来&#xff0c;然后再上传到另外一台服务器上面去&#xff0c;这个方法确实是可行&#xff0c;但是实在是太费时间了&#xff0c;今天我就教大家怎…

arm汇编指令集_1. 从0开始学ARM安装Keil MDK uVision集成开发环境

关于ARM的一些基本概念&#xff0c;大家可以参考我之前的文章&#xff1a;《0.到底什么是Cortex、ARMv8、arm架构、ARM指令集、soc&#xff1f;一文帮你梳理基础概念【科普】》0. 如何学习arm&#xff1f;ARM的知识点很多很杂&#xff0c;很多同学都遇到过类似的问题&#xff0…

32位存储环境下整数范围为什么是[-2^31,2^31-1]?

一、概念&#xff1a;存储单位 1.“位”是数据存储的最小单位。在计算机中的二进制数系统中&#xff0c;位&#xff0c;简记为bit,也称为比特&#xff0c;每个0或1就是一个位。 2.“字节”是计算机信息技术用于计量存储容量的一种计量单位&#xff0c;通常情况下&#xff0c;…

mysql5.7配置用户名密码_MySQL57安装图解

原文&#xff1a;MySQL57安装图解_ma524654165的博客-CSDN博客作者&#xff1a; ma524654165MySQL57安装图解、、、0-需要准备的安装包1在百度下载MySQl开始安装下载的MySQL勾选同意点击Next4选择Server only5点击execute继续6点击next7直接next8配置服务器的端口-默认9设置一个…

vc6.0添加注释快捷键

第一步&#xff1a;工具 第二步&#xff1a;点击定制&#xff08;Customize&#xff09;-键盘-All commands-选择CommentOut&#xff0c;在新建快捷键处填写Ctrl/。

python 释放内存_学了4年C++后,我转向了Python

作者 | asya f 编译 | Lisa C 已经学不动了&#xff0c;现在换 Python 还来得及吗&#xff1f;一位四年工作经验的 C 程序员亲述转型历程&#xff0c;这不仅仅是语言上的转变&#xff0c;而是代码思维甚至工作环境的转变。 通常&#xff0c;程序员会认为 Python 编码比较简单&a…