Spring-Security前后端分离权限认证
前后端分离
一般来说,我们用SpringSecurity默认的话是前后端整在一起的,比如thymeleaf或者Freemarker,SpringSecurity还自带login登录页,还让你配置登出页,错误页。
但是现在前后端分离才是正道,前后端分离的话,那就需要将返回的页面换成Json格式交给前端处理了
SpringSecurity默认的是采用Session来判断请求的用户是否登录的,但是不方便分布式的扩展,虽然SpringSecurity也支持采用SpringSession来管理分布式下的用户状态,不过现在分布式的还是无状态的Jwt比较主流。 所以怎么让SpringSecurity变成前后端分离,可以采用Jwt来做认证
什么是jwt
Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC7519).该token被设计为紧凑且==安全==的,特别适用于==分布式站点的单点登录(SSO)场景==。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
官网: JSON Web Token Introduction - jwt.io
jwt的结构
以 . 分割 三部分
Header
Header 部分是一个JSON对象,描述JWT的元数据,通常是下面的样子。
{"alg": "HS256","typ": "JWT"}
上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256 (写成 HS256) ;typ属性表示这个令牌(token)的类型(type), JWT令牌统一写为JWT。
最后,将上面的JSON对象使用Base64URL算法转成字符串。
Payload(载荷)
Payload 部分也是一个JSON对象,==用来存放实际需要传递的数据==。JWT规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (lssued At):签发时间
jti (JWT ID):编号
除了官方字段,==你还可以在这个部分定义私有字段==,下面就是一个例子。
{"sub": "1234567890","name" : "John Doe",“userid”:2"admin": true}
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把==秘密信息==放在这个部分。这个JSON 对象也要使用Base64URL 算法转成字符串。
Signature
Signature部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个==密钥(secret)==。这个密钥只有==服务器才知道==,不能泄露给用户。然后,使用Header里面指定的==签名算法(默认是 HMAC SHA256)==,按照下面的公式产生签名。
HMACSHA256(base64UrlEncode(header) + ".”"+base64UrlEncode(payload),secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
1.项目添加hutool依赖
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version></dependency>
http://t.csdnimg.cn/TA0Xx基于文章中连接数据库的实例基础上进行的前后端分离设计
2.搭建好一个vue项目
所需的导入包
3.修改配置文件 main.js
全局导入引入
import Vue from 'vue' import App from './App.vue' import router from './router'Vue.config.productionTip = falseimport ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI); import axios from 'axios' // 后端项目的时候 http://localhost:8080 // axios设置一个默认的路径 // 创建实例时配置默认值 const instance = axios.create({// 访问路径的时候假的一个基础的路径baseURL: 'http://localhost:8080/',// withCredentials: true });
请求拦截器与响应拦截器
// 请求拦截器 // instance.interceptors.request.use( config=> {// config 前端 访问后端的时候 参数// 如果sessionStorage里面于token 携带着token 过去if(sessionStorage.getItem("token")){// token的值 放到请求头里面let token = sessionStorage.getItem("token");config.headers['token']=token;}// config.headers['Authorization']="yyl"return config; }, error=> {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error); });// 添加响应拦截器 instance.interceptors.response.use( response=> {console.log(response)// 状态码 500if(response.data.code!=200){alert("chucuole")console.log(response.data);router.push({path:"/login"});return;}return response; }, error=> {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error); });Vue.prototype.$axios = instance; // 引入组件
挂载点
new Vue({router,render: h => h(App) }).$mount('#app')
4.搭建一个.vue页面,并在 router 目录下的 index.js 文件配置好路由
<template><div class="login-container"><el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="login-form"><el-form-item label="用户名" prop="username"><el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input></el-form-item><el-form-item label="确认密码" prop="password"><el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input></el-form-item><el-form-item><el-button type="primary" @click="submitForm('ruleForm')">提交</el-button><el-button @click="resetForm('ruleForm')">重置</el-button></el-form-item></el-form></div>
</template>
搭建的页面包含基本的登录表单,在新建一个页面用于成功的页面展示,如 图中跳转的main.vue
methods: {submitForm(formName) {this.$refs[formName].validate((valid) => {if (valid) {alert('submit!');// 请求 userlogin userlogin//post i请求 json 数据 后端接受的时候 @RequestBodythis.$axios.post("userlogin",qs.stringify(this.ruleForm)).then(r=>{// 获取token的值console.log(r.data.t);// 存起来sessionStorage.setItem("token",r.data.t)// 成功之后 跳转 /mainthis.$router.push("/main");//console.log(r.data);})} else {console.log('error submit!!');return false;}});},}
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'Vue.use(VueRouter)const routes = [{path: '/',name: 'home',component: HomeView},{path: '/login',name: 'login',component: () => import(/* webpackChunkName: "about" */ '../views/login.vue')},{path: '/about',name: 'about',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')},{path: '/main',name: 'main',component: () => import(/* webpackChunkName: "about" */ '../views/main.vue')},
]
// 针对ElementUI导航栏中重复导航报错问题
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {return originalPush.call(this, location).catch(err => err)
}
这里配置了导航重复导航的问题,我们在响应拦截器配置了code非200的跳转登录的情况,为了避免登录失败导致跳转登录页面,重复导航的问题
5.后端加入跨域的配置文件
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;@Configuration
public class CrossConfig {@Beanpublic CorsFilter corsFilter() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();final CorsConfiguration corsConfiguration = new CorsConfiguration();//corsConfiguration.setAllowCredentials(true); // 允许 携带cookie 的信息corsConfiguration.addAllowedHeader("*"); // 允许所有的头corsConfiguration.addAllowedOrigin("*");// 允许所有的请求源corsConfiguration.addAllowedMethod("*"); // 所欲的方法 get post delete putsource.registerCorsConfiguration("/**", corsConfiguration); // 所有的路径都允许跨域return new CorsFilter(source);}}
6.统一返回数据实体
@Data
@AllArgsConstructor //
@NoArgsConstructor //
public class Result<T> {/*** code编码*/private Integer code = 200;/*** 消息*/private String msg = "操作成功";/*** 具体的数据*/private T t;/*** 成功的静态方法*/public static <T> Result success(T t){return new Result<>(200,"操作成功",t);}public static <T> Result <T> fail(){return new Result<>(500,"操作失败",null);}public static <T> Result <T> forbidden(){return new Result<>(403,"权限不允许",null);}
}
7.对实现了UserDetailsService接口的service层进行了修改
@Service
public class MyUserDetailService implements UserDetailsService {@Resourceprivate TabUserMapper userMapper;@Resourceprivate TabUserRoleMapper userRoleMapper;@Resourceprivate TabRoleMapper roleMapper;@Resourceprivate TabMenuMapper menuMapper;// 根据用户的名字 加载用户的信息@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// username 代表前端传递过来的名字
// 根据名字去数据库查询一下有没有这个用户的信息QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("username",username);TabUser tabUser = userMapper.selectOne(queryWrapper);if(tabUser != null) {
// 有值 查询用户对应的角色的idQueryWrapper queryWrapper1 = new QueryWrapper();queryWrapper1.eq("uid",tabUser.getId());List<TabUserRole> tabUserRoles = userRoleMapper.selectList(queryWrapper1);List<Integer> rids = tabUserRoles.stream().map(tabUserRole -> tabUserRole.getRid()).collect(Collectors.toList());
// 根据角色的id 查询rcodeList<TabRole> tabRoles = roleMapper.selectBatchIds(rids);
// 角色的修信息 角色管理 修改角色的名字List<SimpleGrantedAuthority> collect = tabRoles.stream().map(tabRole -> new SimpleGrantedAuthority("ROLE_" + tabRole.getRcode())).collect(Collectors.toList());
// 根据角色的id 查询菜单的mcodeList<TabMenu> menus = menuMapper.selectCodeByRids(rids);List<SimpleGrantedAuthority> resources = menus.stream().map(tabMenu -> new SimpleGrantedAuthority(tabMenu.getMcode())).collect(Collectors.toList());
// 将角色的所有信息,和资源信息合并在一起List<SimpleGrantedAuthority> allresource = Stream.concat(collect.stream(), resources.stream()).collect(Collectors.toList());return new User(username, tabUser.getPassword(), allresource);}return null;}
}
8.数据链路层,对前后端的认证进行判断与返回的JSON数据
@Component
public class JwtFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {/*解析token1.获取token -> 存在 -> 解析不存在返回 null 没有认证2.效验token真的还是假的 真-> 过 -> 用户的信息存放到安全框架的上下文路径里面假-> 返回一个Json 数据 没有认证* */String[] whitename = {"/userlogin"};String token = request.getHeader("token");
// token存在if(StringUtils.isNotBlank(token)) {
// 存在 解析boolean verify = JWTUtil.verify(token, "hp".getBytes());if(verify) {
// 效验合格
// 获取用户的名字 和密码的信息JWT jwt = JWTUtil.parseToken(token);String username = (String) jwt.getPayload("username");List<String> resources = (List<String>) jwt.getPayload("resources");
// 资源的信息List<SimpleGrantedAuthority> collect = resources.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());
// 保存用户的信息UsernamePasswordAuthenticationToken usertoken = new UsernamePasswordAuthenticationToken(username, null, collect);
// 存起来用户的信息SecurityContextHolder.getContext().setAuthentication(usertoken);
// 放行filterChain.doFilter(request,response);}else {Result result = new Result(401, "没有登录", null);printJsonData(response,result);}}else {
// 查看是否在白名单 如果在 就放行String requestURL = request.getRequestURI();if(ArrayUtils.contains(whitename,requestURL)) {filterChain.doFilter(request,response);}else {Result result = new Result(401, "没有登录", null);printJsonData(response,result);}}}public void printJsonData(HttpServletResponse response, Result result) {try {response.setContentType("application/json;charset=utf8"); //json格式 编码是中文ObjectMapper objectMapper = new ObjectMapper();String s = objectMapper.writeValueAsString(result);// 使用objectMapper将result转化为json字符串PrintWriter writer = response.getWriter();writer.print(s);writer.flush();writer.close();}catch (Exception e) {e.printStackTrace();}}
}
9.对config文件进行修改(前后端分离情况)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Resourceprivate JwtFilter jwtFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {// 配置 登录form 表单
// 路劲前面必须加 /http.formLogin().loginProcessingUrl("/userlogin").successHandler((request, response, authentication) -> {System.out.println("authentication"+authentication);
// 资源的信息Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();List<String> allresources = authorities.stream().map(s -> s.getAuthority()).collect(Collectors.toList());System.out.println("allresources"+allresources);
// 认证成功
// 生成tokenMap map =new HashMap<>();map.put("username",authentication.getName()); // 认证成功之后 用户的名字map.put("resources",allresources);
// 资源的信息设置签发时间
// Calendar instance = Calendar.getInstance(); //获取当前的时间
// Date time = instance.getTime();过期的时间设置为2小时之后
// instance.add(Calendar.HOUR,2); //两个小时之后
// Date time1 = instance.getTime();
// map.put(JWTPayload.EXPIRES_AT,time1);
// map.put(JWTPayload.ISSUED_AT,time);
// map.put(JWTPayload.NOT_BEFORE,time);String token = JWTUtil.createToken(map, "hp".getBytes());System.out.println(token);Result result = new Result(200,"登录成功",token);printJsonData(response,result);}) //前后端分离的时候 认证成功 走的方法.failureHandler((request, response, exception) -> {Result result = new Result(500, "失败", null);printJsonData(response,result);}); //认证失败 走的方法http.authorizeRequests().antMatchers("/userlogin").permitAll(); //代表放行 "/userlogin"http.authorizeRequests().anyRequest().authenticated();
// 权限不允许的时候http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {Result result = new Result(403, "权限不允许", null);printJsonData(response,result);});http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
// csrf 方便html文件 能够通过http.csrf().disable();http.cors(); // 可以跨域}@Resourceprivate UserDetailsService userDetailsService;@Beanpublic PasswordEncoder getPassword() {return new BCryptPasswordEncoder();}// 自定义用户的信息@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(getPassword());}public void printJsonData(HttpServletResponse response, Result result) {try {response.setContentType("application/json;charset=utf8");ObjectMapper objectMapper = new ObjectMapper();String s = objectMapper.writeValueAsString(result);PrintWriter writer = response.getWriter();writer.print(s);writer.flush();writer.close();}catch (Exception e) {e.printStackTrace();}}
}
10.配置完成
相关文章:

Spring-Security前后端分离权限认证
前后端分离 一般来说,我们用SpringSecurity默认的话是前后端整在一起的,比如thymeleaf或者Freemarker,SpringSecurity还自带login登录页,还让你配置登出页,错误页。 但是现在前后端分离才是正道,前后端分离的话,那就…...

Django中Cookie和Session的使用
目录 一、Cookie的使用 1、什么是Cookie? 2、Cookie的优点 3、Cookie的缺点 4、Django中Cookie的使用 二、Session的使用 1、什么是Session? 2、Session的优点 3、Session的缺点 4、Django中Session的使用 三、Cookie和Session的对比 总结 D…...
云原生周刊:KubeSphere 3.4.1 发布 | 2023.11.13
开源项目推荐 Inspektor Gadget Inspektor Gadget 是一组用于调试和检查 Kubernetes 资源与应用程序的工具(或小工具)。它在 Kubernetes 集群中管理 eBPF 程序的打包、部署和执行,包括许多基于 BCC 工具的程序,以及一些专为在 I…...

逐帧动画demo
用这一张图实现一个在跑的猎豹的动画 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X…...
Mongodb 中,与索引相关的监控指标
Mongodb为集合添加索引,能够提高查询的效率,减少查询过程中检索文档的数量,改变文档检索的方式。 索引,采用集合中的一部分数据,构建了B tree,支持mongodb的高效检索。除$indexStats命令外,mong…...

图论14-最短路径-Dijkstra算法+Bellman-Ford算法+Floyed算法
文章目录 0 代码仓库1 Dijkstra算法2 Dijkstra算法的实现2.1 设置距离数组2.2 找到当前路径的最小值 curdis,及对应的该顶点cur2.3 更新权重2.4 其他接口2.4.1 判断某个顶点的连通性2.4.2 求源点s到某个顶点的最短路径 3使用优先队列优化-Dijkstra算法3.1 设计内部类…...

OpenCV 实现透视变换
一:OpenCV透视变换的概念 仿射变换(affine transform)与透视变换(perspective transform)在图像还原、图像局部变化处理方面有重要意义。通常,在2D平面中,仿射变换的应用较多,而在3D平面中,透视变换又有了自己的一席之…...

ChinaSoft 论坛巡礼|开源软件供应链论坛
2023年CCF中国软件大会(CCF ChinaSoft 2023)由CCF主办,CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办,将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…...
VUE 组合式API
响应式 data 选项式API_响应式 <template><h3>选项式API</h3><p>{{ message }}</p> </template> <script> export default {data(){return{message:"选项式API 绑定数据"}} } </script>组合式API_响应式 <…...

尝试使用php给pdf添加水印
在开发中增加pdf水印的功能是很常见的,经过实验发现这中间还是会有很多问题的。第一种模式,采用生成图片的方式把需要添加的内容保存成图片,再将图片加到pdf中间,这种方法略麻烦一些,不过可以解决中文乱码的问题&#…...

ubuntu上安装edge浏览器
1下载edge浏览器 官网下载 edge浏览器的linux版本可在上面的官网中寻找。 我选择的是Linux(.deb)。 2 安装 可在终端的edge安装包所在的路径下输入下面命令安装。 sudo dpkg -i edge安装包的名称.deb3 安装可能存在的问题 1dpkg:依赖关系问题使得edge-stable的配置工作不…...
动态切换 Spring Boot 打包配置:使用 Maven Profiles 管理 JAR 和 WAR
引言 在多环境开发中,我们经常需要根据部署环境来改变 Spring Boot 应用的打包方式。本文将探讨如何使用 Maven Profiles 结合依赖排除来动态地切换 JAR 和 WAR 打包配置。 1. 修改 pom.xml 以支持 WAR 包 转换 Spring Boot 应用从 JAR 到 WAR 时,首先…...

微信小程序使用阿里巴巴矢量图标
一,介绍 微信小程序使用图标有两种方式,一种是在线获取,一种是下载到本地使用, 第一种在线获取的有个缺点就是图标是灰色的,不能显示彩色图标,而且第一种是每次请求资源的,虽然很快࿰…...

使用JAVA pdf转word
使用spire.pdf 非常简单。 查看 https://mvnrepository.com/artifact/e-iceblue/spire.pdf 注意,这个包在 e-iceblue 下。 下面开始撸代码 先来pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://mav…...

成都瀚网科技有限公司抖音带货的正规
成都瀚网科技有限公司,一家在科技领域有着深厚积累的公司,近年来也开始涉足电子商务领域,特别是在抖音等短视频平台上进行带货活动。在这个充满机遇与挑战的时代,该公司以其独特的商业模式和运营策略,正在赢得消费者的…...

windows服务器热备、负载均衡配置
安装网络负载平衡 需要加入的服务器上全部需要安装网络负载平衡管理器 图形化安装:使用服务器管理器安装 在服务器管理器中,使用“添加角色和功能”向导添加网络负载均衡功能。 完成向导后,将安装 NLB,并且不需要重启计算机。 …...
samba服务器搭建 挂载远程目录 常用配置参数介绍
samba 直接复用linux的用户,但是Linux 用户的密码和 smbpasswd 设置的密码是分开的。 Linux 用户的密码是存储在 Linux 系统的用户数据库中,通常是 /etc/shadow 文件中以加密形式存储的。Samba 用户的密码是存储在专门的 Samba 密码数据库中 smbpasswd…...

Ansible命令使用
ansible ansible的命令 ansible命令模块Pingcommand 模块shell 模块copy 模块file 模块fetch 模块cron 模块yum 模块service 模块user 模块group 模块script 模块setup 模块get_url模块stat模块unarchive模块unarchive模块 ansible的命令 /usr/bin/ansible Ansibe AD-Hoc 临…...

element 周选择器el-date-picker
2023.11.13今天我学习了在使用element 周选择器的时候,我们会发现默认的时间选择为星期日到下一个星期一,如图: 我们需要改成显示星期一到星期天,只需要加一行代码:picker-options <el-date-pickertype"week&…...

No200.精选前端面试题,享受每天的挑战和学习
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...

大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程
基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...
HTML中各种标签的作用
一、HTML文件主要标签结构及说明 1. <!DOCTYPE html> 作用:声明文档类型,告知浏览器这是 HTML5 文档。 必须:是。 2. <html lang“zh”>. </html> 作用:包裹整个网页内容,lang"z…...

新版NANO下载烧录过程
一、序言 搭建 Jetson 系列产品烧录系统的环境需要在电脑主机上安装 Ubuntu 系统。此处使用 18.04 LTS。 二、环境搭建 1、安装库 $ sudo apt-get install qemu-user-static$ sudo apt-get install python 搭建环境的过程需要这个应用库来将某些 NVIDIA 软件组件安装到 Je…...