微服务JWT的介绍与使用

news2024/11/15 17:39:33

1. 无状态登录

1.1 微服务的状态

​ 微服务集群中的每个服务,对外提供的都是Rest风格的接口,而Rest风格的一个最重要的规范就是:服务的无状态性。

​ 什么是无状态?

  1. 服务端不保存任何客户端请求者信息
  2. 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份

​ 无状态,在微服务开放中,优势是?

  1. 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
  2. 服务端的是否集群对客户端透明
  3. 服务端可以任意的迁移和伸缩
  4. 减小服务端存储压力

1.2 无状态登录实现原理

在这里插入图片描述

服务器端生产唯一标识(注意:最终需要进行校验)

方案1:UUID,数据单一,不能包含种类过多的信息。

方案2:JWT 生成唯一标识,数据可以自定义。【使用】

为了保证JWT生成数据安全性,采用RSA加密。

浏览器存储和自动携带数据

方案1:使用cookie,有很多局限性(大小,个数)。

方案2:请求参数,get请求URL有长度限制,每一个路径都需要处理比较麻烦。

方案3:浏览器localStorage/sessionStorage存储,通过ajax的请求头携带。【使用】

在这里插入图片描述

1.3 RSA加密

1.3.1 概述

​ RSA公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可逆的”密码体制。

​ 在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK [2]。

​ RSA加密:非对称加密。

​ 同时生产一对秘钥:公钥和私钥。

​ 公钥秘钥:用于加密

​ 私钥秘钥:用于解密

​ 既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密,私钥负责解密;同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名,公钥负责验证。

使用RSA加密保证token数据在传输过程中不会被篡改。

1.3.2 工具类

package com.czxy.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
 * Created by liangtong.
 */
public class RsaUtils {

    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws Exception
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);

        //创建父文件夹
        if(!dest.getParentFile().exists()){
            dest.getParentFile().mkdirs();
        }
        //创建需要的文件
        if (!dest.exists()) {
            dest.createNewFile();
        }

        Files.write(dest.toPath(), bytes);
    }
}

1.3.3 生产公钥和私钥

package com.czxy.utils;

import org.junit.Test;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * Created by liangtong.
 */
public class TestRsa {

    private static final String pubKeyPath = "D:\\rsa\\rsa.pub";

    private static final String priKeyPath = "D:\\rsa\\rsa.pri";

    @Test
    public void testRas() throws Exception {
        //生产公钥和私钥
        RsaUtils.generateKey(pubKeyPath, priKeyPath, "234");
    }

    @Test
    public void testGetRas() throws Exception {
        //获得公钥和私钥
        PublicKey publicKey = RasUtils.getPublicKey(pubKeyPath);
        PrivateKey privateKey = RasUtils.getPrivateKey(priKeyPath);

        System.out.println(publicKey.toString());
        System.out.println(privateKey.toString());
    }

1.4 JWT

1.4.1 概述

​ JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io

1.4.2 添加坐标

<properties>
    <jwt.jjwt.version>0.9.0</jwt.jjwt.version>
    <jwt.joda.version>2.9.7</jwt.joda.version>
    <lombok.version>1.16.20</lombok.version>
    <beanutils.version>1.9.3</beanutils.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>${beanutils.version}</version>
    </dependency>

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>${jwt.jjwt.version}</version>
    </dependency>

    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>${jwt.joda.version}</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>

</dependencies>

1.4.3 时间处理工具:DateTime

//当前时间
DateTime.now().toDate().toLocaleString()
//当前时间加5分钟
DateTime.now().plusMinutes(5).toDate().toLocaleString()
//当前时间减5分钟
DateTime.now().minusMinutes(5).toDate().toLocaleString()

1.4.4 测试JWT

生成Token
@Test
public void testGenerateToken() throws Exception {
    String str = Jwts.builder()
            .claim("test","测试数据")
            .setExpiration(DateTime.now().plusMinutes(60).toDate())
            .signWith(SignatureAlgorithm.RS256,RsaUtils.getPrivateKey(priKeyPath))
            .compact();
    System.out.println(str);
}
解析Token
@Test
public void testParseToken() throws Exception {
    String token = "";
    Claims claims = Jwts.parser().setSigningKey(RsaUtils.getPublicKey(pubKeyPath)).
						parseClaimsJws(token).getBody();
    String text = claims.get("test",String.class);
    System.out.println(text);
}

1.4.5 工具类:JwtUtils

package com.czxy.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.DateTime;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * Created by liangtong.
 */
public class JwtUtils {
    /**
     *  私钥加密token
     * @param data 需要加密的数据(载荷内容)
     * @param expireMinutes 过期时间,单位:分钟
     * @param privateKey 私钥
     * @return
     */
    public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey) throws Exception {
        //1 获得jwt构建对象
        JwtBuilder jwtBuilder = Jwts.builder();
        //2 设置数据
        if( data == null ) {
            throw new RuntimeException("数据不能为空");
        }
        BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获得属性名
            String name = propertyDescriptor.getName();
            // 获得属性值
            Object value = propertyDescriptor.getReadMethod().invoke(data);
            if(value != null) {
                jwtBuilder.claim(name,value);
            }
        }
        //3 设置过期时间
        jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
        //4 设置加密
        jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
        //5 构建
        return jwtBuilder.compact();
    }

    /**
     * 通过公钥解析token
     * @param token 需要解析的数据
     * @param publicKey 公钥
     * @param beanClass 封装的JavaBean
     * @return
     * @throws Exception
     */
    public static <T> T  getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception {
        //1 获得解析后内容
        Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
        //2 将内容封装到对象JavaBean
        T bean = beanClass.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获得属性名
            String name = propertyDescriptor.getName();
            // 通过属性名,获得对应解析的数据
            Object value = body.get(name);
            if(value != null) {
                // 将获得的数据封装到对应的JavaBean中
                BeanUtils.setProperty(bean,name,value);
            }
        }
        return bean;
    }
}

1.4.6 生产token和校验token

编写测试对象UserInfo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {

    private Long id;
    private String username;
}
编写测试类
package com.czxy.utils;

import com.czxy.entity.UserInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import org.junit.Test;

/**
 * Created by liangtong.
 */
public class TestJWT {
    private static final String pubKeyPath = "D:\\rsa\\rsa.pub";

    private static final String priKeyPath = "D:\\rsa\\rsa.pri";


    @Test
    public void testToken() throws Exception {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(10L);
        userInfo.setUsername("用户名");
        String token = JwtUtils.generateToken(userInfo, 30, RsaUtils.getPrivateKey(priKeyPath));
        System.out.println(token);
    }

    @Test
    public void testParserToken() throws Exception {
        String token = "eyJhbGciOiJSUzI1NiJ9.eyJjbGFzcyI6ImNvbS5jenh5LmVudGl0eS5Vc2VySW5mbyIsImlkIjoxMCwidXNlcm5hbWUiOiLnlKjmiLflkI0iLCJleHAiOjE1NzU5MTYyMDl9.W3Q3Iz1vGq1nf7RQW3eAzkMvkME9P5_5zoDcFQXX0eke07lA2PLuZGCYcB6-DI0i7UrahFOmB0OFQodrK_3CZkrh-sI_802twkGatRaI0ifetRLV_1XHVl_Lymh6SaDdBB1OT3-EQCAppjoHFb9Tyq1EGyQZ5xoU-vLp7fzNQLQ";
        UserInfo userInfo = JwtUtils.getObjectFromToken(token, RsaUtils.getPublicKey(pubKeyPath), UserInfo.class);
        System.out.println(userInfo);
    }
}

1.4.7 扩展:JWT token组成

JWT的token包含三部分数据:头部、载荷、签名。

名称描述组成部分
头部(Header)通常头部有两部分信息1. 声明类型,这里是JW2. 加密算法,自定义
载荷(Payload)就是有效数据1. 用户身份信息2. 注册声明
签名(Signature)整个数据的认证信息一般根据前两步的数据,再加上服务的的密钥(secret),通过加密算法生成。用于验证整个数据完整和可靠性
生成的数据格式:

在这里插入图片描述

1.5 Zuul整合JWT

1.5.1 自定义配置内容

修改application.yml 添加内容
#自定义内容
sc:
  jwt:
    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥(自定义内容)
    pubKeyPath: D:/rsa/rsa.pub # 公钥地址
    priKeyPath: D:/rsa/rsa.pri # 私钥地址
    expire: 360 # 过期时间,单位分钟
  filter:
    allowPaths:
      - /checkusername
      - /checkmobile
      - /sms
      - /register
      - /login
      - /verifycode
      - /categorys
      - /news
      - /brands
      - /sku/esData
      - /specifications
      - /search
      - /goods
      - /comments
      - /pay/callback

1.5.2 JWT配置类

package com.czxy.changgou3.config;

import com.czxy.utils.RasUtils;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * Created by liangtong.
 */
@Data
@ConfigurationProperties(prefix = "sc.jwt")
@Component
public class JwtProperties {

    private String secret; // 密钥

    private String pubKeyPath;// 公钥

    private String priKeyPath;// 私钥

    private int expire;// token过期时间

    private PublicKey publicKey; // 公钥

    private PrivateKey privateKey; // 私钥

    private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);

    @PostConstruct
    public void init(){
        try {
            File pubFile = new File(this.pubKeyPath);
            File priFile = new File(this.priKeyPath);
            if( !pubFile.exists() || !priFile.exists()){
                RasUtils.generateKey( this.pubKeyPath ,this.priKeyPath , this.secret);
            }
            this.publicKey = RasUtils.getPublicKey( this.pubKeyPath );
            this.privateKey = RasUtils.getPrivateKey( this.priKeyPath );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

1.5.3 过滤路径配置类

package com.czxy.changgou3.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
 * Created by liangtong.
 */
@Data
@ConfigurationProperties(prefix="sc.filter")
public class FilterProperties {

    //允许访问路径集合
    private List<String> allowPaths;
}

1.5.4 过滤器

package com.czxy.changgou3.filter;

import com.czxy.changgou3.config.FilterProperties;
import com.czxy.changgou3.config.JwtProperties;
import com.czxy.changgou3.pojo.User;
import com.czxy.utils.JwtUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * Created by liangtong.
 */
@Component
//2.1 加载JWT配置类
@EnableConfigurationProperties({JwtProperties.class , FilterProperties.class} )
public class LoginFilter  extends ZuulFilter {

    //2.2 注入jwt配置类实例
    @Resource
    private JwtProperties jwtProperties;

    @Resource
    private FilterProperties filterProperties;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 5;
    }

    @Override
    public boolean shouldFilter() {     //03.当前过滤器是否执行,true执行,false不执行
        //3.1 获得用户请求路径
        // 3.1.1 获得上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        // 3.1.2 获得request
        HttpServletRequest request = currentContext.getRequest();
        // 3.1.2 获得请求路径  , /v3/cgwebservice/login
        String requestURI = request.getRequestURI();

        //3.2 如果路径是 /auth-service/login ,当前拦截不执行
        for (String path  : filterProperties.getAllowPaths()) {
            //判断结尾
            if(requestURI.contains(path)){
                return false;
            }
        }

        //3.3 其他都执行 run()方法
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //1 获得token
        //1.1 获得上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        //1.2 获得request对象
        HttpServletRequest request = currentContext.getRequest();
        //1.3 获得指定请求头的值
        String token = request.getHeader("Authorization");


        //2 校验token -- 使用JWT工具类进行解析
        // 2.3 使用工具类,通过公钥获得对应信息
        try {
            JwtUtils.getObjectFromToken(token , jwtProperties.getPublicKey() , User.class);
        } catch (Exception e) {
            // 2.4 如果有异常--没有登录(没有权限)
            currentContext.addOriginResponseHeader("content-type","text/html;charset=UTF-8");
            currentContext.addZuulResponseHeader("content-type","text/html;charset=UTF-8");
            currentContext.setResponseStatusCode( 403 );        //响应的状态码:403
            currentContext.setResponseBody("token失效或无效");
            currentContext.setSendZuulResponse( false );        //没有响应内容
        }

        // 2.5 如果没有异常,登录了--放行
        return null;
    }
}

1.6 下游服务获得token

在这里插入图片描述

zuul默认配置
zuul:
  sensitive-headers: Cookie,Set-Cookie,Authorization

在这里插入图片描述

修改zuul配置,及删除配置

在这里插入图片描述

其他服务中,可以通过request获得内容

在这里插入图片描述

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

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

相关文章

140基于matlab的求解转子系统前三个临界转速和主振型的传递矩阵法转子系统的不平衡响应

基于matlab的求解转子系统前三个临界转速和主振型的传递矩阵法转子系统的不平衡响应。参数涉及等截面参数、材料参数、轮盘参数。程序已调通&#xff0c;可直接运行。 140 matlab临界转速及振型 (xiaohongshu.com)

pod 报错Failed to connect to github.com port 443

pod 报错Failed to connect to github.com port 443 1、排查代理问题1.1、查找网络代理1.2、修改 Git 的代理 2、排查DNS解析问题2.1、查找 ip地址2.2、修改 host 文件 1、排查代理问题 1.1、查找网络代理 打开 设置 --> 网络与Internet --> 查找代理 1.2、修改 Git …

在IDEA中使用快捷键让XML注释更加规范

Setting -> Editor -> Code Style -> XML 取消勾选 Line comment at first column 这样我们在使用ctrl / 快速注释时&#xff0c;就可以让注释符号紧贴注释内容&#xff0c;不出现空格。

k8s-helm

Helm: 什么是helm,在没有这个heml之前&#xff0c;deployment service ingress的作用就是通过打包的方式&#xff0c;把deployment service ingress这些打包在一块&#xff0c;一键式的部署服务&#xff0c;类似于yum 官方提供的一个类似于安全仓库的功能&#xff0c;可以实现…

C++ 20 Module

头文件包含一直是C/C的传统&#xff0c;它使代码声明与实现分离&#xff0c;但它有一个非常大的问题就是会被重复编译&#xff0c;拖累编译速度。 通常一个标准头文件iostream展开后可能达几十万甚至上百万行。笔者使用下面的示例进行测试&#xff0c;新建一个main.cc&#xf…

自动文章生成软件:自用工具推荐与使用技巧

在信息爆炸的时代&#xff0c;内容创作已成为企业和个人提升品牌影响力、增加流量和提升知名度的重要手段。然而&#xff0c;手动撰写大量文章既费时又费力。有没有一种方法可以快速、高效地生成文章呢&#xff1f;答案是肯定的&#xff0c;今天我们就来分享一款自用的AI批量生…

(十一)Head first design patterns状态模式(c++)

状态模式 如何去描述状态机&#xff1f; 假设你需要实例化一台电梯&#xff0c;并模仿出电梯的四个状态&#xff1a;开启、关闭、运行、停止。也许你会这么写 class ILift{ public:virtual void open(){}virtual void close(){}virtual void run(){}virtual void stop(){} }…

windows用mingw(g++)编译opencv,opencv_contrib,并install安装

windows下用mingw编译opencv貌似不支持cuda&#xff0c;选cuda会报错&#xff0c;我无法解决&#xff0c;所以没选cuda&#xff0c;下面两种编译方式支持。 如要用msvc编译opencv&#xff0c;参考我另外一篇文章 https://blog.csdn.net/weixin_44733606/article/details/1357…

gRPC-gateway使用介绍

gRPC-gateway 参考资料&#xff1a;gRPC-Gateway使用指南 服务中&#xff0c;使用了gRPC gateway&#xff08;代理&#xff09;来将外部的http请求映射为内部rpc调用。 proto文件示例&#xff1a; // 导入google/api/annotations.proto import "google/api/annotations…

《WebKit 技术内幕》学习之九(4): JavaScript引擎

4 实践——高效的JavaScript代码 4.1 编程方式 关于如何使用JavaScript语言来编写高效的代码&#xff0c;有很多铺天盖地的经验分享&#xff0c;以及很多特别好的建议&#xff0c;读者可以搜索相关的词条&#xff0c;就能获得一些你可能需要的结果。同时&#xff0c;本节希望…

在全志H616核桃派上实现USB摄像头的OpenCV颜色检测

在给核桃派开发板用OpenCV读取图像并显示到pyqt5的窗口上并加入颜色检测功能&#xff0c;尝试将图像中所有蓝色的东西都用一个框标记出来。 颜色检测核心api 按照惯例&#xff0c;先要介绍一下opencv中常用的hsv像素格式。颜色还是那个颜色&#xff0c;只是描述颜色用的参数变…

图神经网络X项目|基于图神经网络的电商行为的预测(5%)

文章目录 Jupyter Notebook 学习人工智能的好帮手数据集数据集下载数据集调用数据集应用技巧——获取不重复的编号数据集应用技巧——随机采样数据集应用技巧——抽取前N项进行模拟测试 数据集构建技巧一——查看数据集构建进度 Jupyter Notebook 学习人工智能的好帮手 【Jupy…

opencv010 卷积02(方盒滤波和均值滤波)

今天继续学习滤波器的相关知识&#xff01;这篇比较简单&#xff0c;也短一些&#xff0c;明天写高斯滤波 方盒滤波 boxFilter(scr, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) 方盒滤波的卷积核如下&#xff1a; normalize&#xff08;标准化&#xff0…

从潮汐架构和安第斯大模型,看智能手机的未来演进

好久没聊手机了&#xff0c;今天聊聊手机。 最近这段时间&#xff0c;手机厂商纷纷发布了自家最新的旗舰系列。其中&#xff0c;有一些技术&#xff0c;蛮值得关注的。 大家都知道&#xff0c;手机行业是出了名的“内卷”&#xff0c;厂商之间的竞争非常激烈。但从本质来说&…

STL之unordered_map使用方法

这里写目录标题 STL之unordered_map使用方法1.什么是STL呢2.unordered_map2.1 头文件&#xff1a;2.2 怎么创建&#xff1a;2.3 初始化&#xff1a;2.4 根据key获取对应value值&#xff1a;2.5 遍历&#xff0c;判断key是否存在&#xff1a;2.6 怎么根据迭代器it获取key和value…

浅谈拨测在网络安全中的应用

在当今数字化时代&#xff0c;网络安全成为各个行业和组织关注的焦点。为了保障网络的稳定性和信息的安全&#xff0c;拨测安全性成为一种日益重要的工具。本文将介绍拨测在网络安全中的应用&#xff1a; 1.威胁模拟 通过威胁模拟&#xff0c;拨测安全性可以模拟各种网络攻击&a…

分布式websocket IM聊天系统相关问题问答【第九期】

前言 上期视频讲解了自己关于聊天系统的设计的时候出现了一些不一样的声音。不了解情况的可以看上上期视频。这期主要是讨论。IM聊天系统设计方案多。我的先说明一下自己的技术背景互相之间才能更好的理解。 本期对应视频 目前已经写的文章有。并且有对应视频版本。 git项目地…

小白初探架构模式—常用的设计模式

目录 1.前言 2. 主从架构 2.1 主从架构的优点 2.2 主从架构的应用场景 2.3 主从架构的实现 2.4 主从架构的示例 3. 主从架构设计的延伸 3.1 主备模式 3.2 主从复制 3.3 集群分片 3.4 异地多活 4. 总结 1.前言 作为一个架构设计小白&#xff0c;我们通常用了很多种工具&…

Java和Redis实现一个简单的热搜功能

1. 前言 我们有一个简单的需求&#xff1a; 搜索栏展示当前登陆的个人用户的搜索历史记录&#xff0c;删除个人历史记录。用户在搜索栏输入某字符&#xff0c;则将该字符记录下来 以zset格式存储的redis中&#xff0c;记录该字符被搜索的个数以及当前的时间戳 &#xff08;用…

4_机械臂运动学基础向量空间

在了解机械臂正解推导的过程中&#xff0c;几个问题一直困扰着我&#xff1a; 1、为什么3*3矩阵可以描述姿态&#xff1f;矩阵更进一步的意义是什么&#xff1f;姿态是否有其他的描述方式&#xff0c;如果有是什么&#xff1f; 2、机械臂法兰中心相对于基座的坐标&#xff0c;6…