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

为你的项目加上微信登录(个人开发)

当我们开发个人项目的时候,为了用户登录的便捷性,经常会给我们的项目加上一些除了注册之外的方式,其中最常见的就是微信登录,但作为个人开发者,是无法使用微信的授权登录的,但是通过微信公众号可以获得同样的结果。

看完这篇文章你能得到什么?

  1. 引入微信公众号开发:微信公众号开发比较容易,只要入门后面根据开发文档操作即可
  2. 为你的项目加上微信登陆功能
  3. 学会如何阅读文档:文章中标识了这个模块具体在哪个章节,可以先去尝试自己学习开发
  4. 掌握一些开发的思想

实现思路

  1. 微信公众号为我们提供了带信息的二维码的方式,通过这个二维码我们可以让用户去关注我们的公众号
  2. 微信为我们提供了一个可以获取到用户头像等基本信息的方法
  3. 用户点击链接授权后我们为他注册账号然后返回登录密钥
  4. 我们将这个密钥作为键存入我们的 redis,设置过期时间
  5. 将用户的 openId 作为值,当用户输入正确的密钥的时候
  6. 后端就能拿到这个密钥对应的 openId
  7. 记录用户的登录态完成登录功能

前期准备

注册公众号

我们首先需要去注册一个属于我们的公众号,微信公众平台:https://mp.weixin.qq.com/,image.png
注意注册类型一定要选择订阅号,服务号需要企业身份,而且选择后是无法更改的(一个邮箱只能绑定一种账号),选择的时候务必注意,跟着走完注册流程后我们就获得了自己的公众号。
为了开发的便捷性,我们使用开发者工具中提供的公众平台的测试号来尝试实验,测试号省去了一些繁琐的部分,我们开发完成后将代码上线后再去改回使用我们的公众号即可。

内网穿透工具

公众号开发是需要向我们后端发送请求的,我们不可能再服务器上进行代码开发,为了本地开发的便捷,这里使用内网穿透工具来让微信服务器可以访问我们本地的端口。
这里我们选择一个免费的内网穿透工具(提供的免费服务供给我们开发足够了),
netapp:https://natapp.cn/,根据系统的提示下载即可,配置参考官方给的图文即可https://natapp.cn/article/natapp_newbie,唯一需要注意的点就是这个 config.ini 文件的编写,image.png
我们将这两个文件放在同一个目录下直接启动即可image.png,启动后的界面是这样image.png

开发者文档

文章后面括号中会标识这个操作对应的开发者文档中的哪个模块,方便大家进行学习和查阅,我们可以再开发者工具中很容易的找到开发者文档:
image.png

开始开发

接入微信公众平台开发

首先我们打开我们的微信测试号,填写接口配置信息,微信服务器发送请求的时候会向我们填写的 url 发送请求,有了上面的内网穿透工具,微信服务器已经可以发送请求到我们的内网。
将想要微信服务器访问到的组件粘贴到接口配置信息中,这里我们使用 /wx 来接收所有微信发送的请求。

@RequestMapping("/wx")
@Slf4j
@RestController
@CrossOrigin(origins = "http://localhost:8000", allowCredentials = "true")
public class WxController {/*** 微信 url 验证接口*/@GetMapping("/")public String wxCheck(@RequestParam("signature") String signature,@RequestParam("timestamp") Integer timestamp,@RequestParam("nonce") String nonce,@RequestParam("echostr") String echostr) {log.info("开始验证 url signature={},timestamp={},nonce={},echostr={}", signature,timestamp, nonce, echostr);//TODO:验证是微信发送的return echostr;
}

image.png
这时候微信会向我们填写的地址发送一个 GET 请求,同时会附带上面写的参数,我们接收到这个信息后需要将随机数作为返回值返回给微信服务器才能通过验证**(开始开发 / 接入指南)**,微信发送给我们的参数为:
image.png
通过开发文档提供的方式可以验证这个消息是否来自微信,这里先不提供具体的实现方法。

开发带参数的二维码

想要实现扫码登录,需要一个能导航到我们公众号的二维码,二维码开发的具体信息在**(账号管理 / 生成带参数的二维码)**,简单来说就是我们拿我们的 accessToken 和我们希望二维码中携带的信息去向微信服务器换取一个 ticket,再通过这个 ticket 去获得我们的二维码图片地址。

获取 AccessToken

access_toke n是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。

关于获取 AccessToken 的具体流程在**(开始开发 / 获取 Access token)**,我们发送 GET 请求到这个地址

https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

注意将参数改为自己的,就能获取到类似于这样的 JSON 字符串

{"access_token":"ACCESS_TOKEN","expires_in":7200}

image.png
示例实现方法
我在项目中是封装了一个 WxUtil 作为工具类来存放这些方法,发送请求是通过 HuTool 的 HttpUtil,这里就不再赘述使用方法,可以当作参考:

	/*** 获取 accessToken*/
public static String getAccessToken() throws JsonProcessingException {String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +"&appid=" +"&secret=";HttpResponse execute = HttpUtil.createGet(url).execute();String body = execute.body();ObjectMapper objectMapper = new ObjectMapper();// 将 JSON 字符串转化为 MapMap<String, Object> stringObjectMap = objectMapper.readValue(body,new TypeReference<>(){});return (String)stringObjectMap.get("access_token");
}

这里的序列化工具采用的是 Jackson

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.8</version>
</dependency>
获取带参数的二维码

接下来让我们再次回到主线上来,继续开发带参数的二维码,参考官方文档的说明:

临时二维码请求说明
http请求方式: POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN POST数据格式:json POST数据例子:{“expire_seconds”: 604800, “action_name”: “QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下POST数据创建字符串形式的二维码参数:{“expire_seconds”: 604800, “action_name”: “QR_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}}
永久二维码请求说明
http请求方式: POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN POST数据格式:json POST数据例子:{“action_name”: “QR_LIMIT_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下POST数据创建字符串形式的二维码参数: {“action_name”: “QR_LIMIT_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}}

这里我们获取的是临时的二维码,用户如果长时间不扫码则二维码会失效,我们需要构造一个 Post 请求将微信文档中需要参数传递过去,下面给出我的示例代码:

 public static String getTemporaryQRCode(Boolean isTemplate, Boolean isSTR) throws JsonProcessingException {HashMap<String, Object> map = new HashMap<>();//获取临时二维码//该二维码有效时间,以秒为单位。//最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。map.put("expire_seconds", 120);if (isTemplate) {//二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,// QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值if (isSTR) {map.put("action_name", "QR_STR_SCENE");}else {map.put("action_name", "QR_SCENE");}} else {if (isSTR) {map.put("action_name", "QR_LIMIT_STR_SCENE");} else {map.put("action_name", "QR_LIMIT_SCENE");}}// 构建 actionInfo,内嵌HashMap<String, Object> actionInfo = new HashMap<>();HashMap<String, Object> scene = new HashMap<>();scene.put("scene_id", "1");actionInfo.put("scene", scene);map.put("action_info", actionInfo);//发送请求来获取 ticketString ticket = getTicket(map);// 通过 ticket 来获取到二维码的地址String encode = URLEncoder.encode(ticket);String codeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + encode;return codeUrl;}

这段代码中,我需要传入两个参数来判断请求的是否是临时的二维码,场景值 ID 是否是字符串,通过判断语句来修改 POST 参数的内容,这里可以采用直接编写 JSON 字符串的方式实现,但如果想通过 Map 来实现的话需要注意请求的参数中有一个内嵌的 JSON 对象。
通过这个方法我们就能获取到我们二维码的 URL,这个二维码能干什么呢?

接收带参数的二维码

我们将这些信息封装到二维码后,用户扫码关注我们的公众号的时候就会将这些信息同时提交给我们的服务器**(基础消息能力 / 接收事件推送)**,当用户扫码后可以有两种事件推送,已经关注公众号和未关注但通过我们提供的二维码扫码关注两种事件。

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[SCAN]]></Event><EventKey><![CDATA[SCENE_VALUE]]></EventKey><Ticket><![CDATA[TICKET]]></Ticket>
</xml> <xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[subscribe]]></Event><EventKey><![CDATA[qrscene_123123]]></EventKey><Ticket><![CDATA[TICKET]]></Ticket>
</xml>

这两个事件仅有 Event 参数不同,我们可以通过获取这个参数来判断用户是否是第一次关注公众号

处理信息构建授权地址

我们在二维码的参数中设置一个标识登录的参数,当我们接收到这个参数的时候就执行登录的逻辑,同时为这个用户构建一个登录的密钥,将密钥和用户的 openId 作为一个键值对存储在 Redis 数据库中,同时将密钥返回给用户。
需要获取当用户的具体信息的时候,就需要学习公众号开发的具体授权**(微信网页开发 / 网页授权)**

用户同意授权,获取到用户的 code

请求地址
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
参考示例
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx807d86fb6b3d4fd2&redirect_uri=http%3A%2F%2Fdevelopers.weixin.qq.com&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

这个地址的逻辑是用户访问这个地址后会请求授权,用户同意授权后会跳转到我们指定的回调界面,我这里希望在回调界面中为用户提供他们的登录密钥。
设置回调接口
image.png
找到测试公众号界面中的这个修改按钮,来指定回调的地址的域名,注意不要带 http,微信会检测我们的回调地址是否在这个域名下,如果在就无法实现跳转功能。
image.png

返回授权地址

当用户扫码进入我们的公众号,且验证事件信息后发现是登录请求的时候,就向他提供我们的授权地址
代码示例
下面提供我实现的方式,仅供参考

/*** 接收消息并自动回复的功能*/
@PostMapping("/")
public String receiveMessage(HttpServletRequest request) throws IOException, DocumentException {ServletInputStream inputStream = request.getInputStream();// 将传来的信息封装到 map 中HashMap<String, String> map  = new HashMap<>();SAXReader saxReader = new SAXReader();Document read = saxReader.read(inputStream);Element rootElement = read.getRootElement();List<Element> elements = rootElement.elements();for (Element element : elements) {map.put(element.getName(), element.getStringValue());}log.info("开始返回信息,请求参数{}", map);// 拿到用户的 OpenIdString openId = map.get("FromUserName");// 验证是扫码登录且事件值是1  || (map.get("Event").equals("subscribe"))//TODO:给事件值封装一个常量类if ((map.get("Event").equals("subscribe") || map.get("Event").equals("SCAN"))) {//String token = wxService.WxLogin(openId);String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +"appid=" +"&redirect_uri=http://pab2mw.natappfree.cc/wx/wxCallBack" +"&response_type=code" +"&scope=snsapi_userinfo" +"&state=STATE#wechat_redirect";return WxUtil.getReplyMessage(map, "请跳转到授权网址:" + url);}return "";

先从 request 中接收到提供的参数,再去验证这个请求是否为登录请求,再将我们前面构造的回调地址传回去,这里需要学习被动回复用户信息**(基础消息能力 / 被动回复用户信息)**,这里我只解析这个回复文本消息的能力
被动回复用户信息
我们需要在 return 中返回一个 XML 格式的字符串

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[你好]]></Content>
</xml>

这是给出的字符串的示例,其中 ToUserName 和 FromUSerName 都可以通过请求的 XML 拿取,我们只需要指定创建时间和回复的具体消息即可。

public static String getReplyMessage(Map<String, String> map, String message) throws JsonProcessingException {// 设置基础的信息TestMessage testMessage = new TestMessage();testMessage.setFromUserName(map.get("ToUserName"));testMessage.setToUserName(map.get("FromUserName"));testMessage.setMsgType("text");testMessage.setCreateTime(System.currentTimeMillis() / 1000);//设置需要返回给用户的信息testMessage.setContent(message);// 利用 JackSon 实现序列化return JsonUtil.getXmlString(testMessage);
}

这个方法我也封装到了 WxUtil 这个类中,可以作为参考,序列化库使用的 Jackson,导入方式上面已经讲过了。

拉取用户信息

当用户授权成功后,微信会调用我们提供的回调地址,并且将 code 传递过来,比如我们上面指定的 /wxcallback,我们在这个回调接口中实现注册和密钥的返回。
我们拿到用户的 code 就可以向微信请求用户的具体信息了,首先要请求获取一个 accessToken,这和我们上面讲到的 accessToken 不是同一种,是用于网页授权的 accessToken。
我们请求这个地址:

获取code后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

image.png
正确返回的 JSON 对象:

{"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE","is_snapshotuser": 1,"unionid": "UNIONID"
}

通过这个 accessToken 我们就可以获取到用户的信息了
如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。

http:GET(请使用https协议):
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

向这个地址发送请求信息就能得到用户的详细信息:

{   "openid": "OPENID","nickname": NICKNAME,"sex": 1,"province":"PROVINCE","city":"CITY","country":"COUNTRY","headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46","privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

返回的数据是这样的
image.png
下面给出我实现的代码示例,仅供参考

/*** 获取用户详细信息*/
public static Map<String, Object> getUserMes(String pageAccessToken, String openId) throws JsonProcessingException {HttpResponse execute = HttpUtil.createGet("https://api.weixin.qq.com/sns/userinfo?" +"access_token=" + pageAccessToken +"&openid=OPENID" + openId +"&lang=zh_CN").execute();String body = execute.body();return JsonUtil.getParamMap(body);
}

完成注册逻辑返回密钥

Controller 层

    @RequestMapping("/wxCallBack")@ResponseBodypublic String wxCallBack(String code) throws JsonProcessingException {Map<String, Object> pageMap = WxUtil.getPageMap(code);String pageAccessToken = (String) pageMap.get("access_token");String openId = (String) pageMap.get("openid");String token = wxService.WxLogin(pageAccessToken, openId);return "你的登录密钥为" + token;}

Service 层

@Overridepublic String WxLogin(String pageAccessToken, String openId) throws JsonProcessingException {// 先去数据库中查询是否有 openId 如果有则表明已经注册QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("mpOpenId", openId);User one = userService.getOne(queryWrapper);String token = TokenUtil.generateShortToken();String key = "LoginToken" + token;if (one == null) {// 用户还没有注册,调用注册方法Map<String, Object> userMes = WxUtil.getUserMes(pageAccessToken, openId);User user = new User();// 保存用户的 unionId 和 openIduser.setMpOpenId(openId);user.setUnionId((String) userMes.get("unionid"));user.setUserName((String)userMes.get("nickname"));user.setUserAvatar((String)userMes.get("headimgurl"));//TODO:完成 API 调用的 accessKey 的获取boolean save = userService.save(user);if (!save) {throw new BusinessException(400, "系统内部错误");}}// 在 Redis 中存储密钥信息redisService.setExpireString(key, openId, 120, TimeUnit.SECONDS);return token;}

在业务层实现了注册的业务,并且设置了向 Redis 中设置密钥。

用户输入密钥执行登录逻辑

这时候用户就能在授权地址中看到我们的 token,在前端界面输入 token,验证后即可完成登录逻辑。

    @GetMapping("/verifyToken")public BaseResponse<User> verifyToken(String wxToken, HttpServletRequest request) {log.info("接收到的 token 为{}", wxToken);String key = "LoginToken" + wxToken;String openId = redisService.getString(key);if (openId == null) {throw new BusinessException(ErrorCode.OPERATION_ERROR, "密钥错误");}QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("mpOpenId", openId);User one = userService.getOne(wrapper);request.getSession().setAttribute(USER_LOGIN_STATE,one);return ResultUtils.success(one);}

具体根据自己的业务调整,这只是给出一个实例。

小结

这样就完成了微信登录功能的开发,当然还有很多其他的实现方式,这个方式或许不是最好的,也存在一些问题,比如用户恰好输成别人的密钥就会导致登录到别人的账号,可以后期调整,当然用户授权后可以将信息保存到 session 中,前端轮询查看这个 session 是否有登录数据,查询后完成登录跳转。。。
总之实现的方法有很多,希望这篇文章能为大家带来启发。

相关文章:

为你的项目加上微信登录(个人开发)

当我们开发个人项目的时候&#xff0c;为了用户登录的便捷性&#xff0c;经常会给我们的项目加上一些除了注册之外的方式&#xff0c;其中最常见的就是微信登录&#xff0c;但作为个人开发者&#xff0c;是无法使用微信的授权登录的&#xff0c;但是通过微信公众号可以获得同样…...

Pinia的使用技巧

一、安装 npm install pinia 二、main.ts引入 import { createApp } from vue import App from ./App.vue import { createPinia } from piniaconst app createApp(App) app.use(createPinia()) app.mount(#app)三、定义参数 import { defineStore } from piniatype User …...

『亚马逊云科技产品测评』活动征文|AWS 数据库产品类别及其适用场景详细说明

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 前言、AWS 数据库产品类别 01、Amazon Aurora 02、Amazon Docum…...

S32K324 UDS Bootloader开发-下位机篇-Bootload软件(3)

文章目录 前言校验算法34服务响应的字节字节对齐问题跳转问题Boot Delay功能重要配置跳转标志FLASH DRIVER和APP区域CAN ID配置中断使能与禁止CAN TP配置总结前言 上一篇文章介绍了S32K324 UDS Bootlodaer开发中的UDS相关的更改,本文总结一下调试过程中出现的一些问题,及解决…...

如何在 Vim 中剪切、复制和粘贴

目录 前言 如何在 Vim 编辑器中复制文本 如何在 Vim 编辑器中剪切文本 如何在 Vim 编辑器中粘贴文本 如何通过选择文本来剪切和复制文本 通过选择文本复制 在 Vim 中选择文本来剪切文本 前言 在本篇 Vim 快速技巧中&#xff0c;你将学习到剪切和复制粘贴的相关知识。 剪…...

算法基础一

两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 解题思路&#xff1a;这道题最优的做法时间复杂度是O(n)&#xff0c;顺序扫描数组&#xff0c;对每一个元素在…...

6.3 Windows驱动开发:内核枚举IoTimer定时器

内核I/O定时器&#xff08;Kernel I/O Timer&#xff09;是Windows内核中的一个对象&#xff0c;它允许内核或驱动程序设置一个定时器&#xff0c;以便在指定的时间间隔内调用一个回调函数。通常&#xff0c;内核I/O定时器用于周期性地执行某个任务&#xff0c;例如检查驱动程序…...

大数据-之LibrA数据库系统告警处理(ALM-37005 GTM进程异常)

告警解释 当出现如下情况时&#xff0c;产生该告警&#xff1a; GTM实例数据目录中的gtm.conf配置文件不存在或者其中某个配置参数不正确时。GTM实例服务线程无法监听IP&#xff0c;或者无法绑定监听端口。GTM实例进程没有其数据目录读写权限时。 告警属性 告警ID 告警级别…...

一种LED驱动专用控制电路

一、基本概述 TM1620是一种LED&#xff08;发光二极管显示器&#xff09;驱动控制专用IC,内部集成有MCU数字接口、数据锁存 器、LED驱动等电路。本产品质量可靠、稳定性好、抗干扰能力强。主要适用于家电设备(智能热 水器、微波炉、洗衣机、空调、电磁炉)、机顶盒、电子称、…...

Matlab进阶绘图第33期—双曲面图

在《Matlab论文插图绘制模板第56期—曲面图&#xff08;Surf&#xff09;》中&#xff0c;我分享过曲面图的绘制模板。 然而&#xff0c;有的时候&#xff0c;需要在一张图上绘制两个及以上的曲面图&#xff0c;且每个曲面图使用不同的配色方案。 在Matlab中&#xff0c;一张…...

【Linux】23、内存超详细介绍

文章目录 零、资料一、内存映射1.1 TLB1.2 多级页表1.3 大页 二、虚拟内存空间分布2.1 用户空间的段2.2 内存分配和回收2.2.1 小对象2.2.2 释放 三、查看内存使用情况3.1 Buffer 和 Cache3.1.1 proc 文件系统3.1.2 案例3.1.2.1 场景 1&#xff1a;磁盘和文件写案例3.1.2.2 场景…...

官网IDM下载和安装的详细步骤

目录 一、IDM是什么 二、下载安装 三、解决下载超时的问题 四、谷歌浏览器打开IDM插件 谷歌浏览器下载官网&#x1f447; 五、测试 六、资源包获取 一、IDM是什么 IDM&#xff08;internet download manager&#xff09;是一个互联网下载工具插件&#xff0c;常见于用…...

【面经八股】搜广推方向:常见面试题(三)

【面经&八股】搜广推方向:常见面试题(三) 文章目录 【面经&八股】搜广推方向:常见面试题(三)1. 如何解决数据不平衡2. 假设检验的两类错误3. 为什么快排比堆排快4. RMSE、MSE、MAE5. 双塔模型的应用6. XGBoost如果损失函数没有二阶导,该怎么办7. AUC是如何实现的…...

[NOIP2006]明明的随机数

一、题目 登录—专业IT笔试面试备考平台_牛客网 二、代码 set去重&#xff0c;再利用vector进行排序 std::set是一个自带排序功能的容器&#xff0c;它已经按照一定的规则&#xff08;默认是元素的小于比较&#xff09;对元素进行了排序。因此&#xff0c;你不能直接对std::s…...

auth模块

一. auth模块前戏 # 引入:其实我们在创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表 例如:django_sessionauth_user我们知道django在启动之后就可以直接访问admin路由&#xff0c;需要输入用户名和密码&#xff0c;数据参考的就是auth_user表,并且还必须是管…...

H5ke12--3--iframe--编辑邮箱的制作

下面我们来window.iframes[] frames是一个全局变量&#xff0c;它是一个对象数组&#xff0c;其中包含当前窗口中的所有框架&#xff08;如果存在&#xff09;。 在这段代码中&#xff0c;let frameframes[0];是将第一个框架赋值给变量frame。通过frame.document.designMode&q…...

Python面经【3】

零、可迭代对象 可迭代对象是迭代器和生成器的基础&#xff0c;简单来说&#xff0c;可以使用for循环遍历的对象就是可迭代对象&#xff0c;比如常见的list、set和dict。在python中&#xff0c;可迭代对象是指实现了__iter__()方法的对象&#xff0c;当我们使用for循环遍历一个…...

Python集合类型

目录 目标 版本 官方文档 集合分类 实战 创建 循环 常用方法 目标 掌握set和frozenset两种集合的使用方法&#xff0c;包括&#xff1a;创建、交集、并集、差集等操作。 版本 Python 3.12.0 官方文档 Set Types — set, frozensethttps://docs.python.org/3/library/s…...

npm install报错常用解题思路

最近刚接手一个“新”项目&#xff0c;让我很无语。明明是去年起的项目&#xff0c;但是它所用的部分技术栈非常旧&#xff0c;我启动项目&#xff0c;控制台一堆warning报错&#xff0c;然后项目结构也很让我不适应&#xff0c;很多地方都可以用文件夹包一下来方便定位。哎&am…...

conda: error: argument COMMAND: invalid choice

简介 使用conda activate 时&#xff0c;可能会报&#xff1a;conda: error: argument COMMAND: invalid choice: ‘activate’ (choose from ‘clean’, ‘compare’, ‘config’, ‘create’, ‘info’, ‘init’, ‘install’, ‘list’, ‘notices’, ‘package’, ‘remo…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...