【微信公众平台】扫码登陆

news2024/11/24 20:59:29

文章目录

    • 前置准备
      • 测试号接口配置
    • 带参数二维码登陆
      • 获取access token
      • 获取Ticket
      • 拼装二维码Url
      • 编写接口返回二维码
      • 接收扫描带参数二维码事件
      • 编写登陆轮训接口
      • 测试页面
    • 网页授权二维码登陆
      • 生成ticket
      • 生成授权地址
      • 获取QR码
        • 静态文件支持
        • 编写获取QR码的接口
      • 接收重定向参数
      • 轮训登陆接口
      • 测试页面

前置准备

80/443端口能被联网访问:内网穿透、云服务器(测试号无需域名,ip即可)

申请测试号:微信公众平台 (qq.com)

测试号接口配置

开始开发 / 接入指南 (qq.com)

在这里插入图片描述

Token随便写入,用于判断请求是否来自微信服务器。

URl绑定需要编写一个接口用来接收微信的信息:

  • 接收微信GET请求传来四个参数 (后续事件推送也会POST到这个接口)

  • 返回参数中的Echostr参数

(gin框架)

wxgroup := r.Group("/wechat")
{
	wxgroup.Any("/message",controller.WxMessage)
}
r.Run(":80") // 80 端口启动服务
func WxMessage(ctx *gin.Context) {
	vp := &wxgo.VerifyParams{
		Signature: ctx.Query("signature"),
		Echostr:   ctx.Query("echostr"),
		Timestamp: ctx.Query("timestamp"),
		Nonce:     ctx.Query("nonce"),
	}
    // 判断请求是否来自微信服务器。
	if flag, _ := wechat.Wx.VerifySignature(*vp); flag {
		log.Println(vp)
        // 返回echostr字符串
		ctx.String(http.StatusOK, vp.Echostr)
	}
}

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

参数描述
signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp时间戳
nonce随机数
echostr随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

检验signature的Golang示例代码:

// 签名验证
func (w *Wechat) VerifySignature(vp VerifyParams) (res bool, err error) {
	// 获取 token 字段, 为接口配置设置的token参数
	token, err := w.Cfg.GetToken()
	if err != nil {
		return false, err
	}
	// 构造匹配字段
	strs := []string{vp.Timestamp, vp.Nonce, token}
	// 按字典序排列后拼接成一个字符串
	sort.Strings(strs)
	str := strings.Join(strs, "")
	// 对拼接后的字符串进行 SHA1 加密
	hash := sha1.New()
	hash.Write([]byte(str))
	hashed := fmt.Sprintf("%x", hash.Sum(nil))
	// 加密结果与 signature 比较
	if hashed != vp.Signature {
		return false, errors.New("error: Signature mismatch")
	}
	return true, nil
}

将接口地址http://your_ip:80/wechat/message写入配置信息中的URL即可配置成功

带参数二维码登陆

账号管理 / 生成带参数的二维码 (qq.com)

流程:

  • 获取access token
  • 通过access token与一些请求参数获取二维码ticket
  • 通过ticket换取二维码url,返回给前端
  • 用户扫描二维码后事件信息推送到消息接口、将ticket放入redis
  • 前端用ticket轮训是否已经登陆

获取access token

开始开发 / 获取 Access token (qq.com)

  • 在测试号信息下获取:appID、appsecret

  • 用http client发送请求获取access token

接口调用请求说明

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

参数说明

参数是否必须说明
grant_type获取access_token填写client_credential
appid第三方用户唯一凭证
secret第三方用户唯一凭证密钥,即appsecret

返回说明

正常情况下,微信会返回下述JSON数据包给公众号:

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

参数说明

参数说明
access_token获取到的凭证
expires_in凭证有效时间,单位:秒

错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

{"errcode":40013,"errmsg":"invalid appid"}
// 获取普通access token
func (w *Wechat) GetATReq() error {
	// 获取appID与appsecret
	appid, err := w.Cfg.GetAppid()
	if err != nil {
		return err
	}
	appsecret, err := w.Cfg.GetAppsecret()
	if err != nil {
		return err
	}
	// 构造请求地址
    // ReqUrl.ATUrl: "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
	url := fmt.Sprintf(ReqUrl.ATUrl, appid, appsecret)
	// 发送 GET 请求获取响应
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	// 如果响应结果包含错误错误码,返回错误信息
	if strings.Contains(string(body), "errcode") {
		return fmt.Errorf("wechat response error: %s", string(body))
	}
	// 解析字符串
	err = json.Unmarshal(body, &w.LatestAT)
	if err != nil {
		return errors.New("json Unmarshal fail")
	}
	// 设置成功获取时间
	w.LatestAT.Time = time.Now()
	return nil
}
// wechat 用于保存at与配置信息
type Wechat struct {
	Cfg      *WechatCfg
	LatestAT *AT
}
// access token
type AT struct {
	AccessToken string `json:"access_token"`
	ExpiresTime int    `json:"expires_in"`
	Time        time.Time // access token获取时间
}
// 配置
type WechatCfg struct {
	Token       string
	Appid       string
	Appsecret   string
	ExpiresTime int
}
// 获取 accesstoken
func (w *Wechat) GetAccessToken() (at string, err error) {
	w.RefreshAT()
	return w.LatestAT.AccessToken, nil
}
// 刷新 LatestAT
func (w *Wechat) RefreshAT() error {
	// 先判断上次获取的是否超时
	duration := time.Since(w.LatestAT.Time)
	durationInSeconds := int(duration.Seconds())
	if durationInSeconds < (w.LatestAT.ExpiresTime - 600) {
		return nil
	}
    // 发送请求获取access token
	err := w.GetATReq()
	return err
}

获取Ticket

创建二维码ticket

每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

临时二维码请求说明

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”}}}

参数说明

参数说明
expire_seconds该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。
action_name二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
action_info二维码详细信息
scene_id场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1–100000)
scene_str场景值ID(字符串形式的ID),字符串类型,长度限制为1到64
func (w *Wechat) GetQRTicketReq(codetype string, sceneId int) (string, error) {
	// 获取 access token
	at, err := w.GetAccessToken()
	if err != nil {
		return "", err
	}
	// 拼接请求地址
	url := fmt.Sprintf(ReqUrl.TicketUrl, at)
	// 构造请求数据
	data := &QRCodeReq{
		ExpireSeconds: w.Cfg.GetExpiresTime(),
		ActionName:    codetype, // QR码 类型
		ActionInfo: ActionInfo{
			Scene: Scene{
				SceneId: sceneId,
			},
		},
	}
	// 发送 post 请求获取响应
	client := &http.Client{}
	Jsondata, err := json.Marshal(&data)
	if err != nil {
		return "", err
	}
	reader := bytes.NewReader(Jsondata)
	req, err := http.NewRequest("POST", url, reader)
	if err != nil {
		return "", err
	}
	// 设置请求头 json格式
	req.Header.Set("Content-Type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	// 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	var respData = QRCodeRes{}
	// 解析字符串
	err = json.Unmarshal(body, &respData)
	if err != nil {
		return "", errors.New("json unmarsha fail")
	}
	return respData.Ticket, nil
}

拼装二维码Url

通过ticket换取二维码

获取二维码ticket后,开发者可用ticket换取二维码图片。请注意,本接口无须登录态即可调用。

请求说明

HTTP GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 提醒:TICKET记得进行UrlEncode

返回说明

ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。

HTTP头(示例)如下: Accept-Ranges:bytes Cache-control:max-age=604800 Connection:keep-alive Content-Length:28026 Content-Type:image/jpg Date:Wed, 16 Oct 2013 06:37:10 GMT Expires:Wed, 23 Oct 2013 14:37:10 +0800 Server:nginx/1.4.1

错误情况下(如ticket非法)返回HTTP错误码404。

func (w *Wechat) GetQrImageUrl(ticket string) string {
	ticket = url.QueryEscape(ticket) // 进行UrlEncode
    // ReqUrl.QRImgUrl: "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s"
	url := fmt.Sprintf(ReqUrl.QRImgUrl,ticket)
	return url
}

编写接口返回二维码

wxgroup.GET("/getloginqr",controller.GetGZQrUrl)
// 获取公众号登陆二维码
func GetGZQrUrl(ctx *gin.Context) {
    // 获取ticket
	ticket, _ := wechat.Wx.GetQRTicketReq("QR_STR_SCENE", 123)
    // 拼装二维码url
	qrUrl := wechat.Wx.GetQrImageUrl(ticket)
	ctx.JSON(http.StatusOK, gin.H{
		"ticket": ticket,
		"qrUrl":  qrUrl,
	})
}

接收扫描带参数二维码事件

基础消息能力 / 接收事件推送 (qq.com)基础消息能力 / 接收事件推送 (qq.com)

用户扫描带场景值二维码时,可能推送以下两种事件:

  1. 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
  2. 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。

推送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>

参数说明:

参数描述
ToUserName开发者微信号
FromUserName发送方账号(一个OpenID)
CreateTime消息创建时间 (整型)
MsgType消息类型,event
Event事件类型,subscribe(关注) / SCAN(之前已关注)
EventKey事件KEY值,qrscene_为前缀,后面为二维码的参数值
Ticket二维码的ticket,可用来换取二维码图片

在最开始时的消息接口添加消息处理逻辑,接收到扫描带参数二维码事件推送后将ticket和openid放入redis

func WxMessage(ctx *gin.Context) {
	vp := &wxgo.VerifyParams{
		Signature: ctx.Query("signature"),
		Echostr:   ctx.Query("echostr"),
		Timestamp: ctx.Query("timestamp"),
		Nonce:     ctx.Query("nonce"),
	}
	if flag, _ := wechat.Wx.VerifySignature(*vp); flag {
		log.Println(vp)
		ctx.String(http.StatusOK, vp.Echostr)
	}
    // 添加消息处理逻辑
	uEvent := &wxgo.UserEvent{}
	ctx.ShouldBindXML(&uEvent)
	fmt.Println(uEvent)
	if uEvent.Ticket != "" && (uEvent.Event == "SCAN" || uEvent.Event == "subscribe") {
		openid := uEvent.FromUserName
		log.Printf("ticket:%s, openid:%s", uEvent.Ticket, openid)
		// 将ticket和openid存入redis
		err := redis.RedisClient.Set(uEvent.Ticket, openid, 60*time.Second).Err()
		if err != nil {
			log.Println(err.Error())
		}
	}
}

// 用户事件
type UserEvent struct {
	ToUserName   string `xml:"ToUserName"`   // 开发者微信号
	FromUserName string `xml:"FromUserName"` // 发送方账号(一个OpenID)
	CreateTime   int    `xml:"CreateTime"`   // 消息创建时间(整型)
	MsgType      string `xml:"MsgType"`      // 消息类型,event,
	Event        string `xml:"Event"`        // 事件类型,subscribe(关注), SCAN(已关注)
	EventKey     string `xml:"EventKey"`     // 事件KEY值,qrscene_为前缀,后面为二维码的参数值
	Ticket       string `xml:"Ticket"`       // 二维码的ticket,可用来换取二维码图片
}

编写登陆轮训接口

前端需要传递参数ticket询问是否登陆

// 登陆轮训接口
func CheckLogin(ctx *gin.Context) {
	ticket := ctx.Query("ticket")
    // 从redis中读取ticket
	openid, err := redis.RedisClient.Get(ticket).Result()
	if err != nil {
		ctx.JSON(http.StatusOK, gin.H{
			"login": false,
		})
	} else {
		ctx.JSON(http.StatusOK, gin.H{
			"login":  true,
			"openid": openid,
		})
	}
}
wxgroup.GET("/checklogin",controller.CheckLogin)

测试页面

修改两处请求地址

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页面</title>
    <style>
        #qr-code {
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>

    <div id="qr-code-container">
        <img id="qr-code" src="#" alt="QR Code">
    </div>
    <h1 id="login-status"></h1>
    <h3 id="login-user"></h3>
    <script>
        // 请求后端获取二维码图片地址
        function getQRCode() {
            // 修改请求地址
            fetch('http://your_ip/wechat/getloginqr') // 假设'/get_qr_code'是后端提供的接口地址
            .then(response => response.json())
            .then(data => {
                console.log(data.qrUrl)
                document.getElementById('qr-code').src = data.qrUrl;
                // 开始轮询检查登录状态
                // 根据问号分割URL,获取问号后面的部分
                var queryString = data.qrUrl.split('?')[1];
                // 根据等号分割查询字符串,获取ticket参数的值
                var ticket = queryString.split('=')[1];
                console.log(ticket);
                checkLoginStatus(ticket);
            })
            .catch(error => console.error('Error:', error));
        }

        // 轮询检查登录状态
        function checkLoginStatus(ticket) {
            // 修改请求地址
            url = 'http://your_ip/wechat/checklogin?ticket='+ ticket
            console.log(url)
            var checkLoginInterval = setInterval(() => {
                fetch(url) // 假设'/check_login_status'是后端提供的接口地址
                .then(response => response.json())
                .then(data => {
                    console.log(data)
                    if (data.login) {
                        document.getElementById('login-status').innerText = '登录成功';
                        document.getElementById('login-user').innerText = 'Openid: ' + data.openid; 
                        clearInterval(checkLoginInterval); // 登录成功后停止轮询
                    } 
                })
                .catch(error => console.error('Error:', error));
            }, 2000); // 每隔2秒轮询一次
        }

        // 页面加载完成后立即获取二维码
        window.onload = function() {
            getQRCode();
        };
    </script>
</body>
</html>

网页授权二维码登陆

微信网页开发 / 网页授权 (qq.com)

微信公众测试号不支持PC端网页登陆,但是可以模拟带参数二维码的登陆流程来实现类似的功能

流程:

  • 生成ticket作为授权地址的state参数
  • 编写接口作为授权地址都redirect_uri参数
  • 用授权地址生成QR码,并将QR码与ticket返回给前端
  • 用户扫描QR码将携带code和state参数重定向到接口
  • 接口接收code和state中的ticket参数,用code向微信服务器获取用户的access token和open id
  • 将ticket和openid存入redis
  • 前端用ticket轮训是否登陆

生成ticket

生成随机字符串

func GenerateRandomTicket(length int) string {
	rand.Seed(time.Now().UnixNano())
	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	b := make([]rune, length)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

生成授权地址

在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(已认证服务号,默认拥有scope参数中的snsapi_base和snsapi_userinfo 权限),引导关注者打开如下页面:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

参数说明

参数是否必须说明
appid公众号的唯一标识
redirect_uri授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
response_type返回类型,请填写code
scope应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
state重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect无论直接打开还是做页面302重定向时候,必须带此参数
forcePopup强制此次授权需要用户弹窗确认;默认为false;需要注意的是,若用户命中了特殊场景下的静默授权逻辑,则此参数不生效
// 生成ticket
ticket := wxgo.GenerateRandomTicket(20)
// 生成授权地址
redirect_url := "http://39.101.78.10/wechat/accessusercode" // 微信授权后重定向地址,用于接收用户code
scope := "snsapi_base" //授权权限
oauthUrl := wechat.Wx.GetOauth2CodeUrl(redirect_url, scope, ticket) // 授权地址
// Oauth2CodeUrl: "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"
func (w *Wechat) GetOauth2CodeUrl(redirectUrl string, scope string, state string) string {
	encodeUrl := url.QueryEscape(redirectUrl)
	url := fmt.Sprintf(ReqUrl.Oauth2CodeUrl, w.Cfg.Appid, encodeUrl, scope, state)
	return url
}

获取QR码

静态文件支持

qr码保存到服务器中,需要联网访问到

r.Static("/static","resource")
编写获取QR码的接口
wxgroup.GET("/getauthqr",controller.GetAuthQrUrl)
// 获取网页授权登陆二维码
func GetAuthQrUrl(ctx *gin.Context) {
	// 生成ticket
	ticket := wxgo.GenerateRandomTicket(20)
	// 生成授权地址
	redirect_url := "http://your_ip/wechat/accessusercode" // 微信授权后重定向地址,用于接收用户code
	scope := "snsapi_base"                                  //授权权限
	oauthUrl := wechat.Wx.GetOauth2CodeUrl(redirect_url, scope, ticket)
	// 将授权地址生成QR码
	savePath := "./resource/image"
	err := wxgo.GenerateQrCode(oauthUrl, savePath, ticket)
	if err != nil {
		log.Fatal(err.Error())
		return
	}
	qrUrl := fmt.Sprintf("http://your_ip/static/image/%s.png", ticket)
	ctx.JSON(http.StatusOK, gin.H{
		"ticket": ticket,
		"qrUrl":  qrUrl,
	})
}

GenerateQrCode:

func GenerateQrCode(url string, savedir string, fname string) error {
    // "github.com/skip2/go-qrcode" 包
	qrcode, err := qrcode.New(url, qrcode.Highest)
	if err != nil {
		return err
	}
	qrcode.DisableBorder = true
	//保存成文件
	savepath := fmt.Sprintf("%s/%s.png", savedir, fname)
	err = qrcode.WriteFile(256, savepath)
	return err
}

接收重定向参数

如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

code说明:code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

// 网页授权接收code
func AccessUserCode(ctx *gin.Context) {
	code := ctx.Query("code")
	ticke := ctx.Query("state")
	// 用code获取用户access token
	uat, _ := wechat.Wx.GetUserATReq(code)
	// 用access token获取用户信息
	uInfo, _ := wechat.Wx.GetUserInfoReq(uat)
	log.Printf(uInfo.NickName, uInfo.Headimgurl, uInfo.City)
	// 将ticket和openid放入redis
	redis.RedisClient.Set(ticke, uInfo.OpenId, 60*time.Second)
	// 重定向到成功/失败页面
	ctx.Redirect(http.StatusTemporaryRedirect, "http://your_ip/static/html/loginsucceed.html")
}

轮训登陆接口

复用带参数二维码的登陆轮训接口

测试页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页面</title>
    <style>
        #qr-code {
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>

    <div id="qr-code-container">
        <img id="qr-code" src="#" alt="QR Code">
    </div>
    <h1 id="login-status"></h1>
    <h3 id="login-user"></h3>
    <script>
        // 请求后端获取二维码图片地址
        function getQRCode() {
        	// 修改ip
            fetch('http://your_ip/wechat/getauthqr') 
            .then(response => response.json())
            .then(data => {
                console.log(data.qrUrl)
                document.getElementById('qr-code').src = data.qrUrl;
                // 开始轮询检查登录状态
                var ticket = data.ticket;
                console.log(ticket);
                checkLoginStatus(ticket);
            })
            .catch(error => console.error('Error:', error));
        }

        // 轮询检查登录状态
        function checkLoginStatus(ticket) {
            // 修改ip
            url = 'http://your_ip/wechat/checklogin?ticket='+ ticket
            console.log(url)
            var checkLoginInterval = setInterval(() => {
                fetch(url) // 假设'/check_login_status'是后端提供的接口地址
                .then(response => response.json())
                .then(data => {
                    console.log(data)
                    if (data.login) {
                        document.getElementById('login-status').innerText = '登录成功';
                        document.getElementById('login-user').innerText = 'Openid: ' + data.openid; 
                        clearInterval(checkLoginInterval); // 登录成功后停止轮询
                    } 
                })
                .catch(error => console.error('Error:', error));
            }, 2000); // 每隔2秒轮询一次
        }

        // 页面加载完成后立即获取二维码
        window.onload = function() {
            getQRCode();
        };
    </script>
</body>
</html>

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

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

相关文章

正确解决:关于Lattic Diamond和Radiant License冲突问题(无法破解问题)

一、问题 今天工作&#xff0c;搞16nm Avant E系列FPGA&#xff0c;需要用到莱迪思的Radiant 2023.2软件&#xff08;按这个博主的安装流程Lattice Radiant 2023.1 软件安装教程&#xff09;。 安装好之后&#xff0c;设置环境变量&#xff0c;导入License.dat就是破解不了&…

从零开始学习Linux(3)----权限

1.Linux权限的概念 Linux用户&#xff1a;1.root&#xff0c;超级管理员 2.非root&#xff0c;XXX&#xff0c;普通用户 命令&#xff1a;su[用户名] 功能&#xff1a;切换用户。 su -&#xff1a;是指以root的身份重新登录一次。 普通用户切换root需要输入密码&#xff0c;…

【Java开发指南 | 第二篇】标识符、Java关键字及注释

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 标识符Java关键字Java注释 标识符 Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。 所有的标识符都应该以字母&#xff08;A-Z 或者 a-z&#xff09;,美元符&#xff08;$&#xff0…

登录解析(后端)

调试登录接口 进入实现类可以有 验证码校验 登录前置校验 用户验证 验证码校验 通过uuid获取redis 中存储的验证码信息&#xff0c;获取后对用户填写的验证码数据进行校验比对 用户验证 1.进入控制器的 /login 方法 2.进入security账号鉴权功能&#xff0c;经过jar内的流…

5.2 mybatis之autoMappingBehavior作用

文章目录 1. NONE关闭自动映射2. PARTIAL非嵌套结果映射3. FULL全自动映射 众所周知mybatis中标签< resultMap >是用来处理数据库库字段与java对象属性映射的。通常java对象属性&#xff08;驼峰格式&#xff09;与数据库表字段&#xff08;下划线形式&#xff09;是一 一…

python3.poc。sqlmapTamperPocsuite

目的&#xff0c;掌握工具的api接口&#xff0c;框架工具二次开发 ---sqlmap的api接口&#xff1a;https://www.freebuf.com/articles/web/204875.html 应用&#xff1a;配合前期信息收集的到可能存在注入点的地方&#xff0c;批量化的去扫描 #开发当前项目过程&#xff1a…

【YOLOv5】使用yolov5训练模型时报错合集

文章目录 前言问题1 -- VsCode终端无法进入Anaconda创建的虚拟环境【问题描述】【问题分析】【解决方式】方法一方法二 问题2 -- 怎么在VsCode中为项目配置Anaconda创建的虚拟环境【问题描述】【解决方式】 问题3 -- yolov5训练模型时报错RuntimeError: result type Float cant…

c语言基础总结

1. c语言概述 c语言是计算机编程语言的一种&#xff0c;编程语言用于人和机器交流。 1.1 c语言特点 简洁 ​ c语言的语法简单&#xff0c;语句清晰明了&#xff0c;使得程序易于阅读和理解 高效 ​ c语言的执行效率高&#xff0c;可以用于开发需要高性能的应用程序 可移…

C语言中的控制语句(分支语句 if、switch、三目运算符)

程序执行的三大流程 顺序 : 从上向下&#xff0c; 顺序执行代码分支 : 根据条件判断&#xff0c; 决定执行代码的分支循环 : 让特定代码重复的执行 分支语句 条件语句用来根据不同的条件来执行不同的语句&#xff0c;C语言中常用的条件语句包括if语句和switch语句。 if 语句…

比特币叙事大转向

作者&#xff1a;David Lawant 编译&#xff1a;秦晋 要理比特币解减半动态&#xff0c;最关键的图表是下面这张&#xff0c;而不是价格图表。它显示了自 2012 年以来&#xff0c;矿业总收入与比特币现货交易量的比例&#xff0c;并标注了三个减半日期。 虽然矿工仍然是比特币生…

语音识别ASR背后的原理

现在人机语音交互已经成为我们日常生活的一部分&#xff0c;语音交互更自然&#xff0c;大大的提高了效率。 一、什么是语音识别 文字绝对算是人类最伟大的发明之一&#xff0c;正是因为有了文字&#xff0c;人类的文明成果才得以延续。但是文字只是记录方式&#xff0c;人类…

【C++】C++11右值引用

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.什么是左值&&…

npm最新淘宝镜像站已经更新registry(2024-04-19)

1、npm替换地址 旧的 https://registry.npm.taobao.org 已替换为 https://registry.npmmirror.com 淘宝镜像的淘宝官方提供的方法&#xff08;最新的源配置&#xff09; npm config set registry https://registry.npmmirror.com 镜像站网址&#xff1a; npmm…

【Node.js】 fs模块全解析

&#x1f525;【Node.js】 fs模块全解析 &#x1f4e2; 引言 在Node.js开发中&#xff0c;fs模块犹如一把万能钥匙&#xff0c;解锁着整个文件系统的操作。从读取文件、写入文件、检查状态到目录管理&#xff0c;无所不能。接下来&#xff0c;我们将逐一揭开fs模块中最常用的那…

RAID技术

RIAD 什么是RAID 磁盘阵列:利用虚拟化存储技术把多个硬盘组合起来&#xff0c;成为一个或多个硬盘阵列组&#xff0c;目的为提升性能或数据冗余&#xff0c;或是两者同时提升。 简单来说RAID是把多个硬盘组合成为一个逻辑硬盘&#xff0c;因此&#xff0c;操作系统只会把它当作…

若依框架后台管理系统_修改后台管理密码

若依框架后台管理系统_修改后台管理密码 1. 找见加密函数&#xff1a; /*** 生成BCryptPasswordEncoder密码** param password 密码* return 加密字符串*/public static String encryptPassword(String password){BCryptPasswordEncoder passwordEncoder new BCryptPasswordE…

Kubernetes Pod的配置管理 ConfigMap和Secret

目录 前言 一、为什么需要配置管理 二、使用ConfigMap管理Pod的配置信息 2.1 创建ConfigMap&#xff08;4种方式&#xff09; 2.1.1 指定ConfigMap的参数创建 2.1.2 指定配置文件创建ConfigMap 2.1.3 通过一个文件内的多个键值对创建ConfigMap 2.1.4 yaml文件创建Config…

gpt能生成ppt吗

gpt能生成ppt吗 GPT是一个高度通用的工具&#xff0c;适用于多种场景和领域&#xff0c;制作ppt只是它强大功能的冰山一角&#xff0c;具体包括&#xff1a; 信息查询与解释&#xff1a; 提供科学、技术、历史、文化等领域的详细解释和背景信息。 解答疑问&#xff0c;帮助…

达梦数据库一体机树立金融解决方案标杆

达梦数据库一体机自问世以来&#xff0c;获得众多行业用户的高度关注&#xff0c;并率先在金融行业吹响冲锋号角&#xff0c;实现多个重大项目的落地应用。近日&#xff0c;珠海华润银行股份有限公司基于达梦数据库一体机 I 系列的《数据库一体机银行多业务系统集中部署解决方案…

【Java 多线程】Synchronized

Synchronized Synchronized 是Java的一个关键字&#xff0c;它能够将代码块或方法锁起来&#xff0c;是一种互斥锁&#xff0c;一次只能允许一个线程进入被锁住的代码块 如果 Synchronized 修饰的是实例方法&#xff0c;则对应的锁是对象实例如果 Synchronized 修饰的是静态方…