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

登录凭证------

为什么需要登录凭证?

web开发中,我们使用的协议http是无状态协议,http每次请求都是一个单独的请求,和之前的请求没有关系,服务器就不知道上一步你做了什么操作,我们需要一个办法证明我没登录过

制作登录凭证

制作登录凭证的过程主要涉及到用户身份验证和信息的存储与验证。以下是制作登录凭证的基本步骤:

  1. 设计登录界面:首先,你需要设计一个用户登录的界面,这个界面通常包含输入用户名和密码的字段,以及一个提交按钮。
  2. 用户输入信息:用户在登录界面输入用户名和密码,然后点击提交按钮。
  3. 后台验证:服务器或后台系统接收到用户提交的用户名和密码后,会进行验证。验证过程通常包括检查用户名和密码是否匹配数据库中存储的信息。
  4. 生成登录凭证:如果用户名和密码验证通过,服务器会生成一个登录凭证。这个凭证通常是一个包含用户信息和认证信息的令牌(Token),它可以是服务器生成的Session ID,也可以是JWT(JSON Web Token)等形式的令牌。
  5. 返回登录凭证:服务器将生成的登录凭证返回给客户端,客户端通常会将其存储在本地,如浏览器的Cookie或LocalStorage中。
  6. 后续请求验证:在用户后续的请求中,客户端会附带这个登录凭证,服务器会根据这个凭证来验证用户的身份和权限。

在这个过程中,确保数据传输的安全性是非常重要的。通常,用户名和密码的传输会使用HTTPS等加密协议进行保护,以防止信息泄露。同时,对于登录凭证的存储和使用,也需要采取适当的安全措施,以防止被恶意利用。

请注意,具体的实现方式会根据你使用的技术栈和框架有所不同。例如,如果你使用的是Spring Security等安全框架,它通常会提供一套完整的用户认证和授权机制,包括登录凭证的生成和管理。在实际开发中,建议参考你所使用的技术栈和框架的官方文档或相关教程,以获取更具体和详细的指导。

1.cookie(一般服务器来设置,现在使用不多)

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><h2>客户端的网站</h2><button>设置cookie</button>
</body>
<script>/** * 在浏览器通过js设置cookie(很少用)* 没设置max-age(过期时间,/毫秒),是内存cookie* 设置max-age是硬盘cookie*/const btnE1=document.querySelector('button')btnE1.onclick=function(){document.cookie='age=12'document.cookie='name=why;max-age=30'}
</script></html>

服务端设置node

cookie  

const Koa = require('koa');
const Router = require('@koa/router');const app = new Koa();
const router = new Router({ prefix: '/users' });
router.get('/login', (ctx, next) => {ctx.cookies.set('username', 'zs', {maxAge: 24 * 60 * 60 * 1000,})ctx.body = 'users page';
});
router.get('/list', (ctx, next) => {const value = ctx.cookies.get('username');console.log(value)if (value === 'zs') {ctx.body = `userslist--${value}`;} else {ctx.body = '没有权限,请先登录';}});
app.use(router.routes())
app.use(router.allowedMethods());
app.listen(3004, () => {console.log('服务器启动成功')
}
)

cookie-sessionID

相当于session的升级,加密,双重加密

const Koa = require('koa');
const Router = require('@koa/router');
const koasession = require('koa-session');
const app = new Koa();
const router = new Router({ prefix: '/users' });
// ---------
const session = koasession({key: 'SESSION_ID',maxAge: 60 * 60 * 1000,signed: true, //加密
}, app)
app.keys = ['some secret hurr'];  //加盐操作,随便写点值,相当于双重认证
app.use(session);
// ---------
router.get('/login', (ctx, next) => {ctx.session.yan = 'ikub'ctx.body = '登录成功';
});
router.get('/list', (ctx, next) => {const value = ctx.session.yanconsole.log(value)
});
app.use(router.routes())
app.use(router.allowedMethods());
app.listen(3004, () => {console.log('服务器启动成功')
}
)

为什么cookie cookie-sessionID不常用了?

Cookie和Session都是在Web开发中常用的技术,用于在用户访问网站时跟踪和存储用户信息。它们虽然都用于此目的,但它们在实现方式、存储位置、安全性等方面存在显著差异。

  1. 实现方式和存储位置:
  • Cookie是服务器在本地机器上存储的小段文本,并随每一个请求发送至同一服务器。它主要保存在客户端,即用户的浏览器上。
  • Session则是保存在服务器端的,是一种用来存放用户数据的类HashTable结构。当浏览器首次发送请求时,服务器会生成一个HashTable和一个Session ID,并通过响应发送给浏览器。之后的请求中,浏览器会将这个Session ID包含在请求中,以便服务器识别并恢复对应的用户会话。
  1. 存储内容:
  • Cookie只能存储ASCII字符串,需要通过编码方式才能存储为Unicode字符或者二进制数据。
  • Session则能够存储任何类型的数据,包括string、integer、list、map等。
  1. 安全性:
  • Cookie对客户端是可见的,因此存在被分析、窃取或进行cookie欺骗的风险,这使得它在安全性方面相对较弱。
  • Session存储在服务器上,对客户端是透明的,因此不存在敏感信息泄漏的风险,安全性相对较高。
  1. 有效期:
  • 通过设置cookie的属性,可以实现cookie的长期有效。
  • 它的过期时间通常是由服务器设置的,并且可以是一个较长的固定时间段,比如几小时或几天,这取决于服务器端的配置。
  1. 服务器压力:
  • 由于cookie保存在客户端,不占用服务器资源,因此对服务器的压力较小。
  • Session保存在服务器端,每个用户的会话信息都需要在服务器上维护,因此当用户量较大时,可能会对服务器造成较大压力。

总的来说,Cookie和Session在Web开发中各有其用途。Cookie主要用于在客户端保存一些用户信息,如登录状态等,而Session则主要用于在服务器端保存用户的会话信息。它们之间的选择通常取决于具体的应用场景和需求。

--

当结合使用Cookie和Session时,关闭浏览器页面并不一定会销毁Session。实际上,Session的销毁与浏览器的关闭行为并没有直接的联系,而是取决于Session的过期时间设置以及服务器的Session管理策略。

以下是关于Session销毁的一些关键点:

  1. 过期时间:Session通常会有一个过期时间,这是服务器设置的。当Session达到这个过期时间后,即使浏览器仍然打开,Session也会被自动销毁。过期时间的设置可以在服务器端进行配置,常见的设置方式包括基于固定时间间隔的过期(如30分钟)或基于用户活动的过期(如用户最后一次操作后的某个时间)。

  2. 服务器管理:服务器负责管理和跟踪所有的Session。当服务器检测到某个Session已经过期或者由于其他原因(如资源限制、服务器重启等)需要销毁时,它会主动销毁这个Session。

  3. 浏览器关闭:关闭浏览器只是断开了客户端与服务器之间的连接,它并不会直接销毁服务器端的Session。也就是说,即使你关闭了浏览器,服务器仍然会保留对应的Session信息,直到Session过期或被服务器主动销毁。

  4. 手动销毁:在某些情况下,你可能需要在服务器端手动销毁某个Session,比如用户明确退出登录或者管理员强制结束某个会话。这通常可以通过调用服务器端的Session管理API来实现。

  5. Cookie与Session的关系:Cookie在这里扮演的是传递Session ID的角色。即使你关闭了浏览器,只要Cookie中的Session ID没有过期并且没有被清除,下次你打开浏览器并访问同一网站时,浏览器仍然会发送包含这个Session ID的Cookie给服务器,从而恢复之前的会话。

因此,结合使用Cookie和Session时,关闭浏览器页面并不会直接导致Session的销毁。要销毁Session,需要依赖于服务器端的Session管理策略和过期时间的设置。

jwt生成token

token

const Koa = require('koa');
const Router = require('@koa/router');
const jwt = require('jsonwebtoken');
const app = new Koa();
const router = new Router({ prefix: '/users' });
const secret = 'secretxxx';
router.get('/login', (ctx, next) => {//颁发token/*** payload: string | object | Buffer, * secretOrPrivateKey: jwt.Secret, * options?: jwt.SignOptions | undefined): string (+4 overloads)*/const token = jwt.sign({ id: 1 },secret,{ expiresIn: '1h' });ctx.body = {token,message: '成功'};
});
router.get('/list', (ctx, next) => {const authorization = ctx.headers.authorization;const token = authorization.replace('Bearer ', '');console.log(token)//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzEwMTI1MTE4LCJleHAiOjE3MTAxMjg3MTh9.s3nYx08tvwTk086cNkZK-0_KQ-ytBBfothOMlOOuclw//验证tokentry {const result = jwt.verify(token, secret);console.log(result)//{ id: 1, iat: 1710125118, exp: 1710128718 }//验证成功去数据库查数据返回数据ctx.body = {message: '成功',code: 0,list: [{ name: 'zs', age: 18 },{ name: 'ls', age: 20 }]}} catch (error) {ctx.body = {message: 'token过期||无效的token',code: 1,}}});
app.use(router.routes())
app.use(router.allowedMethods());
app.listen(3004, () => {console.log('服务器启动成功')
}
)

算法:

HS256 =>针对对称加密

const Koa = require('koa');
const Router = require('@koa/router');
const jwt = require('jsonwebtoken');
const app = new Koa();
const router = new Router({ prefix: '/users' });
const secret = 'secretxxx';
router.get('/login', (ctx, next) => {//颁发token/*** payload: string | object | Buffer, * secretOrPrivateKey: jwt.Secret, * options?: jwt.SignOptions | undefined): string (+4 overloads)*/const token = jwt.sign({ id: 1 },secret,{ expiresIn: '1h' });ctx.body = {token,message: '成功'};
});
router.get('/list', (ctx, next) => {const authorization = ctx.headers.authorization;const token = authorization.replace('Bearer ', '');console.log(token)//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzEwMTI1MTE4LCJleHAiOjE3MTAxMjg3MTh9.s3nYx08tvwTk086cNkZK-0_KQ-ytBBfothOMlOOuclw//验证tokentry {const result = jwt.verify(token, secret);console.log(result)//{ id: 1, iat: 1710125118, exp: 1710128718 }//验证成功去数据库查数据返回数据ctx.body = {message: '成功',code: 0,list: [{ name: 'zs', age: 18 },{ name: 'ls', age: 20 }]}} catch (error) {ctx.body = {message: 'token过期||无效的token',code: 1,}}});
app.use(router.routes())
app.use(router.allowedMethods());
app.listen(3004, () => {console.log('服务器启动成功')
}
)

RS256=>针对非对称加密

使用git bash 终端

要生成RSA私钥和公钥,你可以使用OpenSSL这个强大的工具。以下是如何使用OpenSSL生成一个私钥和对应的公钥的步骤:

  1. 生成私钥

首先,你需要生成一个私钥。在命令行中,使用以下命令:

 

bash复制代码

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

这个命令会生成一个2048位的RSA私钥,并将其保存在private_key.pem文件中。

  1. 从私钥中提取公钥

有了私钥之后,你可以从中提取出对应的公钥。使用以下命令:

 

bash复制代码

openssl rsa -pubout -in private_key.pem -out public_key.pem

这个命令会从private_key.pem文件中读取私钥,并提取出公钥,然后将公钥保存到public_key.pem文件中。

现在,你拥有了一个私钥文件private_key.pem和一个公钥文件public_key.pem

注意

  • 私钥是保密的,只有你自己或你信任的系统应该能够访问它。不要将私钥泄露给任何人。
  • 公钥可以公开分享,用于验证由私钥签名的数据。
  • 在生产环境中,你应该使用安全的方式来存储和管理私钥,例如使用硬件安全模块(HSM)或密钥管理服务(KMS)。

如果你正在开发一个需要用到这些密钥的应用程序,你需要在你的代码中安全地加载这些密钥文件。确保你的应用程序不会将这些文件的内容暴露给未经授权的用户或系统

const Koa = require('koa');
const Router = require('@koa/router');
const jwt = require('jsonwebtoken');
const fs = require('fs');
const app = new Koa();
const router = new Router({ prefix: '/users' });
// 颁发私钥,验证私钥
const privateKey = fs.readFileSync('./keys/private_key.pem');
const publicKey = fs.readFileSync('./keys/public_key.pem');
router.get('/login', (ctx, next) => {//颁发token/*** payload: string | object | Buffer, * secretOrPrivateKey: jwt.Secret, * options?: jwt.SignOptions | undefined): string (+4 overloads)*/const token = jwt.sign({ id: 1 }, privateKey,{expiresIn: '1h',algorithm: 'RS256' //签名算法,默认为HS256});ctx.body = {token,message: '成功'};
});
router.get('/list', (ctx, next) => {const authorization = ctx.headers.authorization;const token = authorization.replace('Bearer ', '');console.log(token)//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzEwMTI1MTE4LCJleHAiOjE3MTAxMjg3MTh9.s3nYx08tvwTk086cNkZK-0_KQ-ytBBfothOMlOOuclw//验证tokentry {const result = jwt.verify(token, publicKey, {algorithms: ['RS256'] //签名算法,默认为HS256});console.log(result)//{ id: 1, iat: 1710125118, exp: 1710128718 }//验证成功去数据库查数据返回数据ctx.body = {message: '成功',code: 0,list: [{ name: 'zs', age: 18 },{ name: 'ls', age: 20 }]}} catch (error) {ctx.body = {message: 'token过期||无效的token',code: 1,}}});
app.use(router.routes())
app.use(router.allowedMethods());
app.listen(3004, () => {console.log('服务器启动成功')
}
)

前端获取token

后端将 token 返回给前端通常是通过 HTTP 响应来完成的。在用户成功登录或其他需要身份验证的操作后,后端服务器会生成一个 token,并将其包含在 HTTP 响应的头部或正文中返回给前端。下面是一个基本的流程说明:

1. 发送请求到后端

前端应用(例如,一个使用 JavaScript、React、Vue.js 或 Angular 构建的单页应用)会向后端发送一个请求。这个请求通常是用户登录请求,其中包含了用户的凭据(如用户名和密码)。

2. 后端验证凭据并生成 Token

后端服务器接收到请求后,会验证提供的凭据。如果凭据有效,服务器会生成一个 token。这个 token 通常是一个加密的字符串,包含了用户的身份信息以及有效期等信息。常见的 token 格式是 JWT(JSON Web Token)。

3. 后端将 Token 返回给前端

后端有多种方式将生成的 token 返回给前端:

  • HTTP 响应头:将 token 设置在 HTTP 响应的某个自定义头部中,例如 Authorization 或 X-Auth-Token
  • HTTP 响应体:将 token 作为 JSON 对象的一部分返回在响应的正文中。这通常用于 RESTful API 的响应。
  • Cookie:在某些情况下,后端可能将 token 设置为 Cookie 并返回给前端。这种方法在使用传统的服务器端渲染或需要跨域共享 token 时可能更为合适。

示例:使用 HTTP 响应头返回 Token

假设后端使用 Express.js(一个 Node.js 框架)来构建 API,以下是一个简单的示例,展示如何在登录成功后将 token 返回给前端:

const express = require('express');  
const jwt = require('jsonwebtoken'); // 假设使用 jwt-simple 或 jsonwebtoken 库来生成 token  
const app = express();  // 模拟用户验证函数  
function authenticateUser(username, password) {  // 在这里进行实际的用户验证逻辑  // 如果验证成功,返回用户信息;否则返回 null 或抛出错误  return { id: 1, username }; // 示例用户信息  
}  app.post('/login', (req, res) => {  const { username, password } = req.body;  const user = authenticateUser(username, password);  if (user) {  // 生成 token,假设 secret 是你的密钥  const token = jwt.sign(user, 'your_secret_key', { expiresIn: '1h' });  // 将 token 设置在响应头中返回给前端  res.setHeader('Authorization', `Bearer ${token}`);  res.send({ message: 'Login successful' });  } else {  res.status(401).send({ message: 'Invalid credentials' });  }  
});  app.listen(3000, () => {  console.log('Server is running on port 3000');  
});

在这个例子中,当用户通过 POST 请求 /login 路由并提供有效的用户名和密码时,服务器会生成一个 JWT token,并将其作为 Authorization 响应头的一部分返回给前端。前端可以解析这个响应头,提取 token,并将其存储在本地(如 localStorage),以便在后续的请求中使用。

前端处理响应并存储 Token

前端接收到响应后,需要解析响应头或响应体,提取出 token,并将其存储在适当的位置。以下是一个使用 JavaScript Fetch API 和 async/await 的简单示例:

async function login(username, password) {  try {  const response = await fetch('/login', {  method: 'POST',  headers: {  'Content-Type': 'application/json',  },  body: JSON.stringify({ username, password }),  });  if (!response.ok) {  throw new Error('Network response was not ok');  }  // 从响应头中提取 token  const token = response.headers.get('Authorization');  if (token) {  // 存储 token,这里假设使用 localStorage  localStorage.setItem('token', token);  console.log('Token stored successfully');  } else {  throw new Error('Token not found in response');  }  } catch (error) {  console.error('Error during login:', error);  }  
}

在这个示例中,login 函数发送一个登录请求到后端,并从响应头中提取 token。然后,它将 token 存储在 localStorage 中,以便后续使用。请注意,这只是一个基本示例,实际应用中可能需要处理更多的边缘情况和安全性问题。

后续使用token

在前端获取到后端返回的 token 之后,后续的使用通常涉及以下几个关键步骤:

1. 存储 Token

将 token 存储在前端的安全位置,以便在后续的请求中使用。常见的存储位置包括:

  • localStorage:用于长期存储,即使在浏览器关闭和重新打开后也能保留数据。
  • sessionStorage:仅在当前浏览器会话中有效,关闭浏览器窗口后数据会丢失。
  • Cookies:存储在浏览器中,并随每个请求发送到服务器。但请注意,Cookies 可能受到跨域请求的限制,且大小有限。

示例:使用 localStorage 存储 Token

 

javascript复制代码

// 假设你已经从响应头中获取了 token
const token = response.headers.get('Authorization');
// 将 token 存储在 localStorage 中
localStorage.setItem('user-token', token);

2. 在请求中附带 Token

在发送需要身份验证的请求时,前端需要在请求的头部附带 token。这通常是通过设置 HTTP 请求头的 Authorization 字段来实现的。

示例:使用 Fetch API 附带 Token

 

javascript复制代码

async function fetchProtectedResource() {
const token = localStorage.getItem('user-token'); // 从 localStorage 获取 token
if (!token) {
throw new Error('No token found');
}
const response = await fetch('/protected-resource', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`, // 在请求头中附带 token
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data;
}

在这个示例中,fetchProtectedResource 函数从 localStorage 中获取 token,并将其作为 Authorization 头部的一部分附加到 GET 请求中。

3. 处理 Token 过期或无效的情况

后端可能会在 token 过期或无效时返回一个错误响应。前端需要能够捕获这些错误,并相应地处理,例如提示用户重新登录或自动刷新 token(如果后端支持)。

示例:处理 Token 过期错误

 

javascript复制代码

async function fetchProtectedResource() {
try {
// ... 省略之前的代码 ...
if (!response.ok) {
if (response.status === 401 || response.status === 403) {
// Token 可能过期或无效,处理这些情况
handleTokenExpirationOrInvalidation();
} else {
throw new Error('Network response was not ok');
}
}
// ... 省略处理响应的代码 ...
} catch (error) {
console.error('Error fetching protected resource:', error);
}
}
function handleTokenExpirationOrInvalidation() {
// 这里可以清除存储的 token,重定向到登录页面,或尝试刷新 token
localStorage.removeItem('user-token');
window.location.href = '/login'; // 重定向到登录页面
}

在这个示例中,如果后端返回 401 或 403 状态码,前端会调用 handleTokenExpirationOrInvalidation 函数来处理 token 过期或无效的情况。

4. 在路由守卫中检查 Token

如果你使用的是前端路由(例如 Vue Router、React Router 等),可以在路由守卫中检查用户是否已登录(即是否存在有效的 token)。如果 token 不存在或无效,可以将用户重定向到登录页面。

示例:在 Vue Router 中使用路由守卫检查 Token

 

javascript复制代码

import router from './router'; // 引入路由配置
import store from './store'; // 引入 Vuex 存储(如果有使用)
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('user-token'); // 从 localStorage 获取 token
if (to.path !== '/login' && !token) {
// 如果要访问的路由不是登录页面且不存在 token,则重定向到登录页面
next('/login');
} else {
// 否则继续导航到目标路由
next();
}
});

在这个示例中,router.beforeEach 是一个全局的路由守卫,它会在路由变化之前被调用。它检查目标路由(to)是否是登录页面,以及是否存在有效的 token。如果不满足条件,就将用户重定向到登录页面。

请注意,这些步骤是基本的指导,实际应用中可能需要根据具体需求和框架进行调整和扩展。此外,还需要注意处理安全性问题,如使用 HTTPS 保护数据传输、

相关文章:

登录凭证------

为什么需要登录凭证&#xff1f; web开发中&#xff0c;我们使用的协议http是无状态协议&#xff0c;http每次请求都是一个单独的请求&#xff0c;和之前的请求没有关系&#xff0c;服务器就不知道上一步你做了什么操作&#xff0c;我们需要一个办法证明我没登录过 制作登录凭…...

matplotlib系统学习记录

日期&#xff1a;2024.03.12 内容&#xff1a;将matplotlib的常用方法做一个记录&#xff0c;方便后续查找。 基本使用 # demo01 from matplotlib import pyplot as plt # 设置图片大小,也就是画布大小 fig plt.figure(figsize(20,8),dpi80)#图片大小&#xff0c;清晰度# 准…...

【DL】ML系统学习笔记 1

【DL】ML系统学习笔记 1 1. 机器学习定义2. 机器学习三大任务3. 机器学习定义回归举例4. Gradient Descent 优化5. Gradient Descent 优化步骤6. 回归步骤小姐7. Linear models8. 核心步骤流程9. 模型优化9. 深度学习引出1. 机器学习定义 Machine Learning Looking for Functio…...

ffmpeg视频处理常用命令

1.ffmpeg主要参数 -f fmt&#xff08;输入/输出&#xff09; 强制输入或输出文件格式。 格式通常是自动检测输入文件&#xff0c; 并从输出文件的文件扩展名中猜测出来&#xff0c;所以在大多数情况下这个选项是不需要的。-i url&#xff08;输入&#xff09; 输入文件的网址-…...

前端npm和yarn更换国内淘宝镜像

NPM 查询当前镜像 npm get registry 设置为淘宝镜像 npm config set registry https://registry.npm.taobao.org/ (旧地址) npm config set registry https://registry.npmmirror.com/ (最新地址) 设置为官方镜像 npm config set registry https://registry.n…...

华为配置OSPF的Stub区域示例

配置OSPF的Stub区域示例 组网图形 图1 配置OSPF Stub区域组网图 Stub区域简介配置注意事项组网需求配置思路操作步骤配置文件 Stub区域简介 Stub区域的ABR不传播它们接收到的自治系统外部路由&#xff0c;在Stub区域中路由器的路由表规模以及路由信息传递的数量都会大大减少…...

学会Web UI框架--Bootstrap,快速搭建出漂亮的前端界面

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属的专栏&#xff1a;前端泛海 景天的主页&#xff1a;景天科技苑 文章目录 Bootstrap1.Bootstrap介绍2.简单使用3.布局容器4.Bootstrap实现轮播…...

C语言学习大纲

笔者看了下某二本的C语言考研大纲&#xff0c;供平常学习参考&#xff0c;主要考察知识点: C语言概述 &#xff08;1&#xff09; 了解程序设计语言的语法 &#xff08;2&#xff09; 掌握C语言的特点 &#xff08;3&#xff09; 掌握问题求解的过程数据描述 &#xff08;1&am…...

Unity URP 如何写基础的曲面细分着色器

左边是默认Cube在网格模式下经过曲面细分的结果&#xff0c;右边是原状态。 曲面细分着色器在顶点着色器、几何着色器之后&#xff0c;像素着色器之前。 它的作用时根据配置信息生成额外的顶点以切割原本的面片。 关于这部分有一个详细的英文教程&#xff0c;感兴趣可以看一…...

android pdf框架-8,图片缓存

解码会产生很多图片,滑过后不要显示,如果直接回收,会浪费不少资源. 在没有缓存的情况下,会看到gc还是比较频繁的. 有了缓存后,明显gc少了. 目录 常用的缓存 自定义缓存 显示相关的内存缓存 解码缓存池 内存缓存实现: 解码缓存池实现: 常用的缓存 lrucache,这是最常用…...

UE5.2 SmartObject使用实践

SmartObject是UE5新出的一项针对AI的功能&#xff0c;可为开发者提供如公园长椅、货摊等交互对象的统一外观封装&#xff0c;如UE的CitySample&#xff08;黑客帝国Demo&#xff09;中就运用到了SmartObject。 但SmartObject实践起来较为繁琐&#xff0c;主要依赖于AI及行为树…...

奇舞周刊第521期:实现vue3响应式系统核心-MVP 模型

奇舞推荐 ■ ■ ■ 实现vue3响应式系统核心-MVP 模型 手把手带你实现一个 vue3 响应式系统&#xff0c;代码并没有按照源码的方式去进行组织&#xff0c;目的是学习、实现 vue3 响应式系统的核心&#xff0c;用最少的代码去实现最核心的能力&#xff0c;减少我们的学习负担&…...

Mybatis-plus手写SQL如何使用条件构造器和分页

Mybatis-plus手写SQL如何使用条件构造器和分页插件 前言&#xff1a;在使用mybatis-plus过程中&#xff0c;使用条件构造器和分页插件非常效率的提升开发速度&#xff0c;但有些业务需要使用连表查询&#xff0c;此时还想使用条件构造器和使用分页时应该如何操作呢&#xff1f…...

Vue的table组件合并行方法

/*** param {Array} data - 原始数据集合* param {string} addParamer - 这个是自定义的参数&#xff0c;向每个对象中添加一个参数 按照这个参数的个数进行合并* param {} args - 剩余参数 这个是合并规则 &#xff0c;比如按照时间合并 那就传入对象中的时间参数date&#xf…...

5. C语言字符串处理常用方法

在 C 语言中,字符串是以字符数组的形式表示的,以空字符 \0 结尾。C 语言提供了一系列的字符串处理函数,可以用于字符串的操作、查找、比较等。以下是一些常用的 C 语言字符串处理函数: 1. 字符串定义与初始化 #include <stdio.h> #include <string.h>int mai…...

ts--(入门到离职系列)

TS 与 JS 的区别 TypeScript[4] 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集&#xff0c;而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。-- 官方文档 说人话就是 TS 拓展了 JS 的一些功能&#xff0c;解决了 JS 的一些缺点&#…...

java后端常见问题

java面向对象的特征&#xff0c;如何应用 面向对象编程是利用类和对象的编程思想万物皆成类&#xff0c;对象是类的具体体现&#xff0c;面向对象的三大基础特性&#xff0c;继承封装多态。、封装&#xff1a;封装隐藏了类的具体内部实现机制&#xff0c;可以在不影响使用的情…...

windows系统玩游戏找不到d3dx9_43.dll缺失,无法启动此程序的解决方法

今日&#xff0c;我们要深入讨论d3dx9_43.dll文件的重要性及其缺失问题。最近&#xff0c;我也遇到了这个文件丢失的困扰&#xff0c;因此想借此机会与大家分享如何解决d3dx9_43.dll缺失的问题。 一.电脑d3dx9_43.dll丢失会提示什么&#xff1f; 关于电脑提示d3dx9_43.dll丢失…...

MATLAB中mapminmax函数用法

目录 语法 说明 示例 使用 mapminmax 函数格式化矩阵 mapminmax函数的功能是通过将行最小值和最大值映射到 [-1 1] 来处理矩阵。 语法 [Y,PS] mapminmax(X,YMIN,YMAX) [Y,PS] mapminmax(X,FP) Y mapminmax(apply,X,PS) X mapminmax(reverse,Y,PS) dx_dy mapminmax(d…...

数据结构导航 -- 38篇

数据结构实现代码 线性表 顺序表代码-C-CSDN博客 单链表代码-C-CSDN博客 双链表代码-C-CSDN博客 有序表概述-CSDN博客 栈 栈代码数组实现-C-CSDN博客 栈代码链表实现&#xff08;链栈&#xff09;-C-CSDN博客 队列 队列与环形队列顺序存储代码数组实现-C-CSDN博客 队…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...