浏览器-基本认证(Basic Authentication)-摘要认证(digest authentication)=spring boot实现demo

2023-05-16

平时开发的 java web 网站登录,都是通过表单提交登录信息。有时一些中间件登录是浏览器弹窗,没有看到表单实现代码。故通过查询,发现两种 HTTP 简单认证: 基本认证( Basic Authentication )、摘要认证( digest authentication )等,本次通过 java实现 spring boot 基本和摘要认证。
基本认证

  1. 客户端发送 HTTP 给服务器;
  2. 发送 Request 中 header 没含Authorization, 服务器返回401 Unauthozied ,并在Response 的 header中WWW-Authenticate添加信息(Basic realm="自定义内容");
  3. 客户端把用户名和密码(用户名+冒号+密码)用 BASE64 编码后,放在 Request 中 header Authorization 发送给服务器, 认证成功
  4. 服务器将 header 中Authorization的用户名密码取出验证, 如果验证通过,将根据请求,返回资源

摘要认证

  1. 客户端发送 HTTP 给服务器;
  2. 发送 Request 中 header 没含Authorization, 服务器产生(Digest 、认证的域realm="自定义内容"、认证的校验方式qop、随机数nonce、随机字符opaque、认证算法algorithm)字符串拼接一起,放在WWW-Authenticate响应头,一起发送给客户端;
  3. 客户端发现401响应,表示需认证,则弹出让用户输入用户名和密码的认证窗口,客户端选择算法,计算出密码和其他数据的摘要(response),放到Authorization的请求头中发送服务器,如客户端要对服务器也进行认证,这时可发送客户端随机数cnonce
  4. 服务接受摘要,选择算法,获取存储的用户名密码,计算摘要跟客户端传输的摘要进行比较,验证是否匹配

参考

HTTP基本认证(Basic Authentication)的JAVA示例
摘要认证及实现HTTP digest authentication
JAVA 调用 RFC2617摘要认证协议的接口
curl命令使用digest方式验证用户
http请求头和响应头
Java猿社区—Http digest authentication 请求代码最全示例
RFC 2617 - HTTP Authentication: Basic and Digest Access Authenti
HTTP Authentication: Basic and Digest Access Authentication
C# 摘要认证(digest authentication) IETF RFC 2617
http协议基本认证 Authorization
中文rfc2617-001
Java 基于 IETF RFC 2617 身份认证

代码

pom

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.3.1.RELEASE</spring-boot.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-core</artifactId>
        <version>5.3.8</version>
    </dependency>
	<dependency>
	    <groupId>cn.hutool</groupId>
	    <artifactId>hutool-crypto</artifactId>
	    <version>5.3.8</version>
	</dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

接口代码


import cn.hutool.core.lang.UUID;
import org.apache.tomcat.util.security.MD5Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * 测试认证接口
 *
 * @author z.y.l
 * @version v1.0
 * @date 2020-09-10
 */
@Controller
@RequestMapping("/auth")
public class AuthController {
    private final Logger logger = LoggerFactory.getLogger(AuthController.class);
    private final static String AUTH_B = "Basic ",AUTH_D = "Digest ";
    @GetMapping ("")
    @ResponseBody
    public String root(){
        return "Hello Spring Boot Auth Demo.By zyl.";
    }
    @GetMapping ("basic/info")
    @ResponseBody
    public String basicInfo(){
        return "Hello Basic Authentication.By zyl.";
    }
    @RequestMapping("basic")
    @ResponseBody
    public String basic(HttpServletRequest request, HttpServletResponse response){
        //基本认证(Basic Authentication)
        String auth = request.getHeader("Authorization");
        if( null == auth || auth.isEmpty() || !auth.startsWith(AUTH_B)){
            logger.info("测试浏览器原生弹窗登录认证-未登录");
            response.addHeader("WWW-Authenticate","Basic realm=\".\"");
            response.setStatus(401);
            //缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存
            response.setHeader("Cache-Control", "no-store");
            //0, 代表着过去的日期,即该资源已经过期
            response.setDateHeader("Expires", 0);
        }else {
            logger.info("测试浏览器原生弹窗登录认证-验证登录");
            logger.info("auth, {}",auth);
            auth = new String(Base64.getDecoder().decode(auth.substring(AUTH_B.length())));
            logger.info("auth-decode, {}",auth);
            String[] split = auth.split(":");
            String n = split[0],p = split[1];
            logger.info("auth-decode, name: {} , p: {}",n,p);
        }
        return "Hello Spring Boot Demo.by auth.";
    }
    @RequestMapping("digest")
    @ResponseBody
    public String digest(HttpServletRequest request, HttpServletResponse response){
        //摘要认证 digest authentication
        /*
        摘要认证及实现HTTP digest authentication https://www.jianshu.com/p/cf5a900d4ef7
        */
        String auth = request.getHeader("Authorization");
        if( null == auth || auth.isEmpty() || !auth.startsWith(AUTH_D)){
            logger.info("测试浏览器原生弹窗登录认证-摘要认证-未登录");
            Map<String, String> map = new HashMap<>(5);
            //值是一个简单的字符串
            map.put("realm","realm");
            //保护质量,是认证的(校验)方式.auth表示认证;auth-int表示完整性保护认证
            map.put("qop","auth,auth-int");
            //是随机数, 可以用GUID
            map.put("nonce",UUID.fastUUID().toString(true));
            //是个随机字符串,它只是透传而已,即客户端还会原样返回过来。
            map.put("opaque",UUID.fastUUID().toString(true));
            //是个字符串,用来指示用来产生分类及校验和的算法对。如果该域没指定,则认为是“MD5“算法。
            map.put("algorithm","MD5");
            StringBuilder sb = new StringBuilder(AUTH_D);
            map.forEach((k,v) -> sb.append(k).append('=').append('"').append(v).append('"').append(','));
            response.addHeader("WWW-Authenticate",sb.toString());
            logger.info("WWW-Authenticate: {}",sb.toString());
            response.setStatus(401);
        }else {
            logger.info("测试浏览器原生弹窗登录认证-摘要认证-验证登录");
            logger.info("auth, {}",auth);
            String[] split = auth.split(", ");
            String nc = "",cnonce="",nonce="",qop="",algorithm="";
            for (String sp : split) {
                logger.info("auth-ap, {}",sp);
                if(sp.startsWith("nc")){
                    nc = sp.substring("nc=".length());
                }else if(sp.startsWith("cnonce")){
                    cnonce = sp.substring("cnonce=".length());
                }else if(sp.startsWith("nonce")){
                    nonce = sp.substring("nonce=".length());
                }else if(sp.startsWith("qop")){
                    qop = sp.substring("qop=".length());
                }else if(sp.startsWith("algorithm")){
                    algorithm = sp.substring("algorithm=".length());
                }
            }
            String name = "asd", realm = "realm",pass = "123456";
            String a1 = name+":"+realm+":"+pass;
            MD5 md_5 = MD5.create();
            String ha1 = md_5.digestHex(a1,"UTF-8");
            logger.info("MD5 name:realm:pass > {} > {}",a1,ha1);

            boolean sess = "MD5-sess".equalsIgnoreCase(algorithm);
            if(sess){
                ha1 = ha1 + ":" + nonce + ":" + cnonce;
                logger.info("MD5-sess md5(name:realm:pass):nonce:cnonce > {} > {}",a1,ha1);
            }

            String a2 = "get:/hello/digest";
            String ha2 = md_5.digestHex(a2,"UTF-8");
            logger.info(" method:uri > {} > {}",a2,ha2);

            if("auth-int".equals(qop)){
                logger.info("如果 qop 值为“auth-int”,那么 HA2 为:HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody)),实现代码未找到");
            }
            if("auth".equalsIgnoreCase(qop) || "auth-int".equalsIgnoreCase(qop)){
                String md = ha1+":"+nonce+":"+nc+":"+cnonce+":"+qop+":"+ha2;
                String md5 = md_5.digestHex(md,"UTF-8");
                logger.info("response ha1:nonce:nc:cnonce:qop:ha2 > {} > {}",md,md5);
            }else{
                String md = ha1+":"+nonce+":"+ha2;
                String md5 = md_5.digestHex(md,"UTF-8");
                logger.info("response ha1:nonce:ha2 > {} > {}",md,md5);
            }
        }
        return "Hello Spring Boot Demo.by digest.";
    }
    @GetMapping ("digest/info")
    @ResponseBody
    public String digestInfo(){
        return "Hello Digest Authentication.By zyl.";
    }
}

测试

请求:http://localhost:8080/auth
在这里插入图片描述

基本认证

浏览器访问

请求:http://localhost:8080/auth/basic,浏览器提示登录,请求报文显示了后端响应内容
在这里插入图片描述
输入账号密码登陆后,请求头会携带认证信息
在这里插入图片描述
日志
在这里插入图片描述

[AuthController.java:62] =.= 测试浏览器原生弹窗登录认证-验证登录
[AuthController.java:63] =.= auth, Basic YWRtaW46MTIzNDU2
[AuthController.java:65] =.= auth-decode, admin:123456
[AuthController.java:68] =.= auth-decode, name: admin , p: 123456

请求:http://localhost:8080/auth/basic/info,就会携带认证信息
在这里插入图片描述

Linux使用curl访问

在Linux服务器通过curl访问认证接口

curl -u test http://192.168.xxx.xxx:8080/auth/basic
Enter host password for user 'test':
Hello Spring Boot Demo.by auth.

在这里插入图片描述
接口打印日志
在这里插入图片描述

[AuthController.java:54] =.= 测试浏览器原生弹窗登录认证-验证登录
[AuthController.java:55] =.= auth, Basic dGVzdDo2NTQzMjE=
[AuthController.java:57] =.= auth-decode, test:654321
[AuthController.java:60] =.= auth-decode, name: test , p: 654321

摘要认证

浏览器访问

请求:http://localhost:8080/auth/digest,浏览器提示登录
在这里插入图片描述
输入账号密码登陆后,请求头会携带认证信息
在这里插入图片描述
在这里插入图片描述

[AuthController.java:91] =.= 测试浏览器原生弹窗登录认证-摘要认证-验证登录
[AuthController.java:92] =.= auth, Digest username="asd", realm="realm", nonce="d8df2efaecf74dd8a736147794eb2aff", uri="/auth/digest", algorithm=MD5, response="bfcdf3e596dace72c035de009f96458b", opaque="b4bccdce4a6b4ba1a42acf3d357c33bc", qop=auth, nc=00000004, cnonce="b2596a7472eee0df"
[AuthController.java:96] =.= auth-ap, Digest username="asd"
[AuthController.java:96] =.= auth-ap, realm="realm"
[AuthController.java:96] =.= auth-ap, nonce="d8df2efaecf74dd8a736147794eb2aff"
[AuthController.java:96] =.= auth-ap, uri="/auth/digest"
[AuthController.java:96] =.= auth-ap, algorithm=MD5
[AuthController.java:96] =.= auth-ap, response="bfcdf3e596dace72c035de009f96458b"
[AuthController.java:96] =.= auth-ap, opaque="b4bccdce4a6b4ba1a42acf3d357c33bc"
[AuthController.java:96] =.= auth-ap, qop=auth
[AuthController.java:96] =.= auth-ap, nc=00000004
[AuthController.java:96] =.= auth-ap, cnonce="b2596a7472eee0df"
[AuthController.java:113] =.= MD5 name:realm:pass > asd:realm:123456 > 41fd06d7b3a49c6fc0d44d338b3b2fb8
[AuthController.java:123] =.=  method:uri > get:/hello/digest > a422e85fbdc5fb6cb7d362a4207ff531
[AuthController.java:131] =.= response ha1:nonce:nc:cnonce:qop:ha2 > 41fd06d7b3a49c6fc0d44d338b3b2fb8:"d8df2efaecf74dd8a736147794eb2aff":00000004:"b2596a7472eee0df":auth:a422e85fbdc5fb6cb7d362a4207ff531 > f5faaf2b18a4ed555cdec05a02454398

请求:http://localhost:8080/auth/digest/info,就会携带认证信息
在这里插入图片描述

Linux使用curl访问

在Linux服务器通过curl访问认证接口

curl -i http://192.168.xxx.xxx:8080/auth/digest -X GET --digest --user asd:12345
HTTP/1.1 401 
WWW-Authenticate: Digest realm="realm",qop="auth,auth-int",opaque="0f7db485912f42d5b3a22dbf0ecb771e",nonce="723bb303214c479496ab83afc54eedc3",algorithm="MD5",
Content-Type: text/plain;charset=UTF-8
Content-Length: 33
Date: Sun, 09 Oct 2022 08:34:42 GMT

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 33
Date: Sun, 09 Oct 2022 08:34:42 GMT

Hello Spring Boot Demo.by digest.

在这里插入图片描述
接口打印日志,两次请求
在这里插入图片描述

[AuthController.java:73] =.= 测试浏览器原生弹窗登录认证-摘要认证-未登录
[AuthController.java:88] =.= WWW-Authenticate: Digest realm="realm",qop="auth,auth-int",opaque="0f7db485912f42d5b3a22dbf0ecb771e",nonce="723bb303214c479496ab83afc54eedc3",algorithm="MD5",
[AuthController.java:91] =.= 测试浏览器原生弹窗登录认证-摘要认证-验证登录
[AuthController.java:92] =.= auth, Digest username="asd", realm="realm", nonce="723bb303214c479496ab83afc54eedc3", uri="/auth/digest", cnonce="ICAgICAgICAgICAgICAgICAgICAgICAgIDIxMDU1MTU=", nc=00000001, qop=auth, response="21d1f834c6c12b9baf6f87ffdaf76edf", opaque="0f7db485912f42d5b3a22dbf0ecb771e", algorithm="MD5"
[AuthController.java:96] =.= auth-ap, Digest username="asd"
[AuthController.java:96] =.= auth-ap, realm="realm"
[AuthController.java:96] =.= auth-ap, nonce="723bb303214c479496ab83afc54eedc3"
[AuthController.java:96] =.= auth-ap, uri="/auth/digest"
[AuthController.java:96] =.= auth-ap, cnonce="ICAgICAgICAgICAgICAgICAgICAgICAgIDIxMDU1MTU="
[AuthController.java:96] =.= auth-ap, nc=00000001
[AuthController.java:96] =.= auth-ap, qop=auth
[AuthController.java:96] =.= auth-ap, response="21d1f834c6c12b9baf6f87ffdaf76edf"
[AuthController.java:96] =.= auth-ap, opaque="0f7db485912f42d5b3a22dbf0ecb771e"
[AuthController.java:96] =.= auth-ap, algorithm="MD5"
[AuthController.java:113] =.= MD5 name:realm:pass > asd:realm:123456 > 41fd06d7b3a49c6fc0d44d338b3b2fb8
[AuthController.java:123] =.=  method:uri > get:/hello/digest > a422e85fbdc5fb6cb7d362a4207ff531
[AuthController.java:131] =.= response ha1:nonce:nc:cnonce:qop:ha2 > 41fd06d7b3a49c6fc0d44d338b3b2fb8:"723bb303214c479496ab83afc54eedc3":00000001:"ICAgICAgICAgICAgICAgICAgICAgICAgIDIxMDU1MTU=":auth:a422e85fbdc5fb6cb7d362a4207ff531 > 04b7f44c993d847534f97489c80f08b6

END

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浏览器-基本认证(Basic Authentication)-摘要认证(digest authentication)=spring boot实现demo 的相关文章

  • serial库常见用法

    安装的时候要输入 pip install pyserial 进行安装 serial是python做串口通讯的库 xff0c 在这里介绍了NX与Windows串口通讯 NX串口通讯windows Suyuoa的博客 CSDN博客 目录 1 使
  • Ubuntu开机自启动一些东西

    有三种方式做开机自启动 目录 1 免除sudo密码 2 Startup 2 desktop 3 service 1 免除sudo密码 做完这一步你的所有sudo命令都不会再让你输密码了 如果你的开机自启动的东西需要sudo xff0c 那么
  • 附录3-动态组件 component与keep-alive setup()写法

    目录 1 基本使用 2 按钮切换组件 3 keep alive 3 1 在切换组件的时候 xff0c 默认情况下被切换的组件就会被销毁 3 1 1 数据情况 3 1 2 生命周期函数情况 3 2 使用 keep alive 3 3 keep
  • 附录11-插件ESLint与prettier

    ESLint是提示错误的 xff0c prettier是帮你解决错误的 目录 1 ESLint 1 1 安装 1 2 配置 1 3 效果 2 prettier 1 ESLint 1 1 安装 1 2 配置 在插件介绍中 xff0c 搜索 e
  • 用 C 语言编写 Windows 服务程序

    NTService cpp Defines the entry point for the console application include lt windows h gt include lt stdio h gt define S
  • 10.过滤器

    过滤器是做文本格式化的 xff0c 只能用在 插值表达式 与 v bind 上 xff0c 在vue3中已经被弃用了 xff0c 这里我简单记录一下并写一下在vue3的替代方法 目录 1 基本使用 1 1 vue2中的写法 1 2 vue3
  • 24.eslint

    eslint是约束代码写法的插件 xff0c 比如组件的命名必须要用驼峰命名这种 eslint官网 检测并修复 JavaScript 代码中的问题 ESLint 插件化的 JavaScript 代码检查工具 目录 1 vue cli的esl
  • CocoaPods的使用——pod install pod install --repo-update pod update pod update --repo-update

    Podfile文件中 xff0c 使用某个库时 不指定版本 xff0c 表示希望使用最新版本 xff0c 如 pod SDWebImage 指定明确版本 xff0c 表示只想要这个版本 xff0c 如 xff1a pod 39 SDWebI
  • SceneDelegate有什么作用?删除有什么影响

    自从Xcode11发布以来 xff0c 当你使用新XCode创建一个新的iOS项目时 xff0c SceneDelegate会被默认创建 xff0c 它到底有什么用呢 xff1f 在iOS 13 xff08 及以后版本 xff09 上 xf
  • UICollectionViewCell自适应宽度

    如图所示效果 xff0c 根据字符长度自适应UICollectionViewCell的大小 xff0c 同时进行左对齐处理 如何实现 继承UICollectionViewFlowLayout创建子类 xff0c 并实现相关的方法 xff0c
  • 微信小程序开发——JS中字符和数组的操作

    字符的操作 span class token keyword var span word span class token operator 61 span span class token string 34 hello world 34
  • 微信小程序开发——字体样式设置

    font style文字样式 normal 正常字体italic 斜体字oblique 倾斜字体 font weight文字粗细 取值范围 100 900normal 相当于400bold 粗体 相当于700bolderlighter fo
  • 异常处理——richview不显示cloud路径的图片

    图片上传后 xff0c 得到的文件路径为cloud xxx png格式 xff0c 使用image组件时 xff0c 正常显示 xff0c 但使用richview富文本组件时 xff0c 未显示出来 此时 xff0c 需要通过使用wx cl
  • 编译错误“Too many arguments to function call....”

    运行编译过程中出现错误 xff0c 并提示 Too many arguments to functions call expected 的信息 xff0c 如图一 遇到这种情况时 xff0c 该如何解决呢 xff1f 解决方法是将属性 En
  • CocoaPods的使用——cocoapods的升级更新

    最近使用cocoa pods时提示版本过低需要升级才能正常使用 但是在升级过程中又出现了问题 首先我是直接使用语句 xff1a sudo gem install cocoapods 进行更新升级的 xff0c 没想到又报错了 接着我在想是不
  • http请求方法(GET、POST、HEAD、OPTIONS、PUT、DELETE、TRACE、CONNECT)

    根据HTTP标准 xff0c HTTP请求可以使用多种请求方法 HTTP的1 0版本中只有三种请求方法 xff1a GET POST 和 HEAD方法 到了1 1版本时 xff0c 新增加了五种请求方法 xff1a OPTIONS PUT
  • MongoDB可视化工具 Studio 3T

    告别终端使用可视化工具Studio 3T对MongoDB进行数据库的操作 简单的使用步骤介绍 1 启动MongoDB服务器 xff08 方法见MongoDB介绍与安装中的介绍 xff09 2 连接MongoDB服务器 3 操作数据库
  • 微信小程序开发——form表单

    WeChat小程序交流 xff08 QQ群 xff1a 769977169 xff09 效果图 代码示例 1 xxx wxml lt form bindsubmit 61 39 submitClick 39 bindreset 61 39
  • 苹果电脑(Mac mini或Macbook或iMac)恢复出厂设置

    苹果电脑 xff08 Mac mini或Macbook或iMac xff09 恢复出厂设置 xff0c 首先要做好如下的准备 xff1a 第一 xff1a 数据的备份 xff1b 第二 xff1a 保证正常的wifi连接 xff1b 第三
  • VSLAM基础(一)————常见特征点提取算法及匹配优化

    过年期间闲来无事 xff0c 就想来把这半年学习的一些视觉知识 xff08 视觉slam相关 xff09 做个梳理 xff0c 就以这篇图像特征点提取与匹配作为开头吧 一 关键点与描述子 关键点 xff1a 图像上某些特殊的 具有代表的点

随机推荐

  • 【2017CS231n】第十五讲:神经网络模型压缩和加速(硬件、算法层面)

    一 算法 1 剪枝 不是所有的神经网络连接都有用 减少连接 xff0c 但不降低准确性 尝试性 xff1a 剪枝 xff0c 训练不断重复 xff0c 找到对准确性影响最高的那一部分连接 2 参数共享 1 98 2 09 1 92 1 87
  • 程序员面试等通知一般多久?

    最近面试的人比较多 xff0c 毕竟是金三银四嘛 xff0c 竞争也挺大的 xff0c 很多人在面试之后 xff0c 久久没有收到入职通知 xff0c 等待是一件非常痛苦的事情 那么程序员面试等通知一般多久 xff1f 多久没有发通知就等于
  • 程序员简历项目经验怎么写?

    我是一个典型的互联网公司程序员 xff0c 也见过无数的程序员简历 xff0c 包括很多优秀的程序员简历 xff0c 看了可以让人眼前一亮 xff0c 优美简洁的简历模板 xff0c 项目经验工作重点突出 也见过更多的写得不好的简历 程序员
  • OpenRAVE

    机器人的高级功能 机械手臂控制 软件方面 在ros里 有moveit 研究的过程中 扩展到了openrave专业包 Welcome to OpenRAVE Latest Official Release 0 8 2 OpenRAVE pro
  • C++——STL初识

    一 为什么需要STL xff1f 目的 xff1a 为了建立一套重复利用的东西 使用STL可以避免从事大量重复的工作 为建立数据结构的一套标准 xff0c 诞生了STL 二 基本概念 STL是标准模板库 STL从广义上分为 xff1a 容器
  • Showing progress bar in a status bar pane

    This article was contributed by Brad Mann This code creates a progress bar anywhere in the status window and the control
  • 大小端字节序转换

    特点 各主机的字节序可能不同 xff0c 网络协议指定的通讯字节序为大端 只有在多字节数据处理时才需要考虑字节序 运行在同一台计算机的进程互相通信时 xff0c 不需要考虑字节序 二 字节序转换函数 头文件 xff1a include lt
  • 51单片机控制0.96寸OLED(IIC接口)

    1 OLED初步认识 OLED Organic Light Emitting Diode xff0c 即有机发光二极管 OLED由于同时具备自发光 xff0c 不需背光源 xff0c 对比度高 xff0c 厚度薄 xff0c 视角广 xff
  • 51单片机控制LCD1602模块

    51单片机控制LCD1602模块 视频播放 xff1a LCD1602视频 xff08 1 xff09 LCD1602概述 先来看看LCD1602什么意思 xff1f Liquid Crystal Display LCD 表示液晶显示 xf
  • LCD1602模块如何显示自定义字符

    相信大家现在知道了如何通过查字库表的方法来显示表中的任意一个字符 假如现在我想显示某个温度值 xff0c 需要标志出温度符号 C xff0c 现在你去字库表里面查找 xff0c 发现根本找不到这个符号 xff0c 那怎么办 xff1f 下面
  • 51单片机控制温度传感器DS18B20

    xff08 1 xff09 DS18B20初步认识 DS18B20是美国DALLAS半导体公司推出的一种数字化单总线器件 xff0c 属于新一代适配微处理器的改进型智能温度传感器 我们先来看看DS18B20在芯片手册上的标题 DS18B20
  • ESP8266模块手机端和电脑端网络调试助手

    ESP8266模块手机端和电脑端网络调试助手 使用方法比较简单 xff0c 如下界面所示 xff1a 电脑端 xff1a 确定 协议类型 xff0c 34 IP地址 34 和 34 端口 34 xff0c 然后点击连接即可 xff0c 如下
  • Micro Python 入门教程-pyboard V1.1控制LCD1602液晶显示模块

    11 LCD1602液晶显示模块 11 1 初识LCD1602液晶模块 LCD1602什么意思 xff0c LCD表示Liquid Crystal Display xff0c 1602表示一行可以显示16个字符 xff0c 一共有两行 实物
  • 常见无人机自组网路由协议

    常见无人机自组网路由协议 无人机自组网路由协议静态路由协议先验式路由协议反应式路由协议 无人机自组网路由协议 为了适应无人机组网节点高速移动带来的网络拓扑结构快速变化 xff0c 无人机自组网使用的路由协议主要有以下几种 静态路由协议 这种
  • 尚硅谷大数据项目之Flink实时数仓-踩坑记录和笔记记录

    这里写自定义目录标题 1 关于Slf4j注解配置文件logback xml不生效问题2 判断新老用户的时候 xff0c 什么时候会出问题 xff1a 3 为什么维度数据选择存储在Hbase中 xff0c 而不是Redis xff0c Mys
  • 【学习笔记】尚硅谷大数据项目之Flink实时数仓---数据可视化接口实现

    这里写自定义目录标题 第 1 章 数据可视化接口1 1 设计思路1 2 需求梳理1 2 1 最终显示效果图1 2 2 分析可视化大屏1 2 3 接口执行过程 第 2 章 Sugar 数据大屏2 1 产品介绍2 2 使用入口2 3 创建数据大
  • VR发展前景展望

    VR发展前景展望 引子 随着计算机技术的飞速发展 xff0c 虚拟现实也在短时间内经历了萌芽探索到飞速发展完善的转变 由于其独特的沉浸式体验 xff0c VR的前景被大多数人看好 xff0c 更多的VR相关技术也在为让人能更完美的融合到这个
  • Ubuntu18.04下C++分文件编写报错:对‘Class::Func()’未定义的引用解决办法

    源代码结构 如图所示 xff0c 编写一个员工管理系统 xff0c 在WorkerManager h中声明类和函数 xff0c 在WorkerManager cpp中写具体实现在staffManagementSystem cpp中实例化类
  • 人工智能是什么?

    人工智能是什么 xff1f 欢迎大家迈入人工智能的大门1 人工智能的定义2 人工智能的话题3 人工智能的四大技术分支4 人工智能的主要应用领域5 人工智能的三种形态5 1 弱人工智能到强人工智能有多难 xff1f 5 2 弱人工智能的前进方
  • 浏览器-基本认证(Basic Authentication)-摘要认证(digest authentication)=spring boot实现demo

    平时开发的 java web 网站登录 xff0c 都是通过表单提交登录信息 有时一些中间件登录是浏览器弹窗 xff0c 没有看到表单实现代码 故通过查询 xff0c 发现两种 HTTP 简单认证 xff1a 基本认证 Basic Auth