04-获取认证的用户身份信息

存储用户信息的方式

获取用户信息的流程

用户提交账号和密码后,DaoAuthenticationProvider调用UserDetailsService接口实现类的loadUserByUsername()方法,该方法可以接收请求参数username的值,然后根据该值查询用户信息,最后将账号,密码,权限封装到UserDetails对象中并返回给Spring security框架

public interface UserDetailsService {
    // 根据用户名获取用户信息的方法,不同的实现类对应不同的实现方式
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
// UserDetails是用户信息接口
public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

在这里插入图片描述

基于内存的方式存储用户信息

InMemoryUserDetailsManagerUserDetailsService接口的实现类,可以将用户信息以硬编码的方式存储在内存中并封装到UserDetails对象中

package com.xuecheng.auth.config;
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public UserDetailsService userDetailsService() {
        // 1. 配置用户信息服务
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        // 2. 创建用户信息这里暂时写死,后面需要从数据库中动态查询, Kyle的权限是p1,Lucy的权限是p2
        // User是Spring Security提供的工具类
        manager.createUser(User.withUsername("Kyle").password("123").authorities("p1").build());
        manager.createUser(User.withUsername("Lucy").password("456").authorities("p2").build());
        return manager;
    }
    // 设置密码比对的规则
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 采用明文的方式直接比对
        return NoOpPasswordEncoder.getInstance();
    }

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/r/**")// 表示"/r/**"开头的请求需要认证
            .authenticated()
            .anyRequest().permitAll()
            .and()
            .formLogin()
            .successForwardUrl("/login-success");
        // 配置退出登录页面,认证成功后访问/logout可退出登录
        http.logout().logoutUrl("/logout");
    }
}

基于数据库存储用户信息和密码编码格式

实际开发中认证服务所需要的用户信息都是存储在数据库中,所以我们要从数据库中查询用户信息

第一步:屏蔽掉原先的InMemoryUserDetailsManager, 自定义一个UserDetailsService接口的实现类UserDetailsImpl并重写loadUserByUsername()方法,该方法可以接收URL中携带的请求参数username的值

  • 我们重写方法的业务逻辑是根据用户的账号名从数据库中查询对应的用户信息,然后将账号,密码,用户权限信息封装到UserDetails类型的对象中并返回给Spring Security框架,不设置权限会报Cannot pass a null GrantedAuthority collection
@Service
public class UserDetailsImpl implements UserDetailsService {
    @Autowired
    XcUserMapper xcUserMapper;
    /**
    *
    * @param name     用户输入的登录账号
    * @return         UserDetails
    * @throws         UsernameNotFoundException
    */
    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
        // 根据username去XcUser表中查询对应的用户信息
        XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, name));
        // 返回NULL表示用户不存在,SpringSecurity会帮我们处理即抛出异常表示用户不存在
        if (user == null) {
            return null;
        }
        // 设置用户权限
        // String[] authorities=  {"test"};
        
        // 取出数据库中用户的密码
        String password = user.getPassword();
        // 将用户名和密码以及用户权限(权限必须要设置)封装成一个UserDetails对象,然后返回即可
        return User.withUsername(user.getUsername()).password(password).authorities("test").build();
    }
}

第二部:Spring Security框架将URL中的请求参数password的值即用户的登陆密码和UserDetails对象中从数据库中查询到的密码进行比对

  • 数据库中的密码是加密过的,但用户登陆时输入的密码是明文形式,所以我们要修改WebSecurityConfig配置类中的密码格式器PasswordEncoder指定的密码校验方式
密码格式描述
NoOpPasswordEncoder表示通过明文的方式比较密码
BCryptPasswordEncoder表示先将用户输入的密码编码为BCrypt格式后才与数据库中的密码进行比对
@Bean
public PasswordEncoder passwordEncoder() {     
	// return NoOpPasswordEncoder.getInstance();
    return new BCryptPasswordEncoder();
}

BCryptPasswordEncoder原理: 虽然同样的原始密码可以对应不同的Hash值,但是比对密码时如果调用它提供的matches方法即使Hash值不同但校验是正确的

  • 先将数据库表中用户密码的存储方式改为BCrype格式, 申请令牌时输入的密码需要的还是明文形式
public static void main(String[] args) {
    String password = "123456";
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    for (int i = 0; i < 5; i++) {
        // 每个计算出的Hash值都不一样
        String encodePsw = encoder.encode(password);
        // 虽然Hash值不一样,但是校验是可以通过的
        System.out.println("转换后密码:" + encodePsw + "比对情况:" + encoder.matches(password, encodePsw));
    }
}

// 转换后密码:$2a$10$6hbvtCtgcISvbBHJ.UnhPO1io7StF.ySPkmAvzO/efvNmHVVJZOeK比对情况:true
// 转换后密码:$2a$10$ufYW9qXSAk0N201B/wCR7uGrzygawnwXtyL2vKpDLAOCOkF33sGnK比对情况:true
// 转换后密码:$2a$10$DEaVxYHakIE/kDvAU4eC7OZ7c9kqKBJedClVxDPnYH.zwuZvCRnzm比对情况:true
// 转换后密码:$2a$10$s2qgaKGgULYQ7tce2u6TIeHopap4HqfyghJYu1vdDZ2WcNk70ykFe比对情况:true
// 转换后密码:$2a$10$XQaQJIfXyd/UvMHC..uBNuDXNVrZHnEGn.tW0oSB6WVjdsZLFpkGq比对情况:true

由于我们修改了框架密码的校验方式,所以还需要将认证服务批准接入的客户端的密钥也更改为BCrypt的编码格式

// 配置客户端的详情信息
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()// 使用in-memory存储
            .withClient("XcWebApp")// 客户端的Id
            .secret(new BCryptPasswordEncoder().encode("XcWebApp"))//客户端密钥转为BCrypt格式
            .resourceIds("xuecheng-plus")//资源列表
            .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")// 允许的授权类型
            .scopes("all")// 允许的授权范围
            .autoApprove(false)//false跳转到授权页面                  
            .redirectUris("http://localhost/");//客户端接收授权码的重定向地址
}

第三步:重启认证服务使用HttpClient通过密码模式申请令牌,要保证登陆的明文密码在数据库中有对应的BCrypt格式的加密密码

###### 密码模式
POST localhost:53070/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=Kyle&password=111111

第四步:查看服务端响应的结果

// 输入正确的账号密码即可成功申请令牌
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJLeWxlIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTY3ODQ1MTUxNiwiYXV0aG9yaXRpZXMiOlsidGVzdCJdLCJqdGkiOiJkOWUwYjU0ZS03Zjg4LTQ2NjAtYjFlZS04ZWQzYjYzZmQwNjMiLCJjbGllbnRfaWQiOiJYY1dlYkFwcCJ9.NaS3hmpDtX3zkXvnZWoo9ROEWgYeA6GoxBzy_lOxzvA",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJLeWxlIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImQ5ZTBiNTRlLTdmODgtNDY2MC1iMWVlLThlZDNiNjNmZDA2MyIsImV4cCI6MTY3ODcwMzUxNiwiYXV0aG9yaXRpZXMiOlsidGVzdCJdLCJqdGkiOiI5YjE4OTUwOS1iYmEzLTRkMTctYTNkNC05OWQwZGI5NjU0MDAiLCJjbGllbnRfaWQiOiJYY1dlYkFwcCJ9.548w5CdQiIU5_k1qRBjzM-PMBqy-XX3zr17tQS6g6CM",
"expires_in": 7199,
"scope": "all",
"jti": "d9e0b54e-7f88-4660-b1ee-8ed3b63fd063"
}

// 输入错误的密码,申请令牌失败
{
"error": "invalid_grant",
"error_description": "用户名或密码错误"
}

扩展用户身份信息的方式(UserDetails)

用户表中除了存储用户的账号密码还有其他信息,但是UserDetails接口中只有username、password字段,这样JWT令牌中也只会写入用户的账号和密码信息

  • 在认证阶段DaoAuthenticationProvider会调用我们自定义的UserDetailsService接口实现类的loadUserByUsername()方法查询数据库表中用户全部的信息,但是UserDetails中只能存储账号和密码
public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

由于JWT令牌中用户身份信息来源于UserDetails, 所以我们只能把UserDetails中包含的用户信息写入JWT令牌中,想要扩展用户的身份信息有两种方式

  • 修改源码: 对框架提供的UserDetails的属性进行扩展,使之包括更多的自定义属性
  • 存储Json(推荐): 我们还可以将用户的全部信息转为Json数据存储到UserDetails的username属性(需要脱敏),并不一定要只存储用户账号
@Service
public class UserDetailsImpl implements UserDetailsService {
    @Autowired
    XcUserMapper xcUserMapper;
    /**
    *
    * @param name     用户输入的登录账号
    * @return         UserDetails
    * @throws         UsernameNotFoundException
    */
    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
        // 根据username去XcUser表中查询对应的用户信息
        XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, name));
        // 返回NULL表示用户不存在,SpringSecurity会帮我们处理即抛出异常表示用户不存在
        if (user == null) {
            return null;
        }
        // 设置用户权限
        // String[] authorities=  {"test"};
        // 取出数据库中用户的密码
        String password = user.getPassword();
        
        // 将查询到的用户信息映射的XcUser对象转为Json,注意对用户的敏感信息要进行脱敏
        user.setPassword(null);
        String userString = JSON.toJSONString(user);
        // 将用户名和密码以及用户权限(权限必须要设置)封装成一个UserDetails对象,然后返回即可
        return User.withUsername(userString).password(password).authorities("test").build();
    }
}

重启认证服务,使用密码模式重新获取令牌

// 密码模式
POST localhost:53070/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=Kyle&password=111111

访问校验接口查看JWT令牌中的内容,此时令牌的user_name属性中包含了查询到的用户全部信息(密码为null)

// 访问校验接口
POST localhost:53070/auth/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJ7XCJjb21wYW55SWRcIjpcIjEyMzIxNDE0MjVcIixcImNyZWF0ZVRpbWVcIjpcIjIwMjItMDktMjhUMDg6MzI6MDNcIixcImlkXCI6XCI1MlwiLFwibmFtZVwiOlwiS2lraVwiLFwicGFzc3dvcmRcIjpcIiQyYSQxMCQwcHQ3V2xmVGJuUERUY1d0cC8uMk11NUNUWHZvaG5OUWhSNjI4cXE0Um9LU2MwZEdBZEVnbVwiLFwic2V4XCI6XCIxXCIsXCJzdGF0dXNcIjpcIlwiLFwidXNlcm5hbWVcIjpcIkt5bGVcIixcInV0eXBlXCI6XCIxMDEwMDJcIn0iLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNjc4NDUyMzU0LCJhdXRob3JpdGllcyI6WyJ0ZXN0Il0sImp0aSI6Ijc2MDc0MDI4LTBiM2MtNDQ4Mi1hN2Y0LTc1NDI3ZTA2OTFjMSIsImNsaWVudF9pZCI6IlhjV2ViQXBwIn0._GKfGE2s5k0n6VC4_RKQrzdzydWY-WtX3Q_Hc4DxQ1g

{
  "aud": [
    "xuecheng-plus"
  ],
  "user_name": "{\"companyId\":\"1232141425\",\"createTime\":\"2022-09-28T08:32:03\",\"id\":\"52\",\"name\":\"Kiki\",\"password\":\"$2a$10$0pt7WlfTbnPDTcWtp/.2Mu5CTXvohnNQhR628qq4RoKSc0dGAdEgm\",\"sex\":\"1\",\"status\":\"\",\"username\":\"Kyle\",\"utype\":\"101002\"}",
  "scope": [
    "all"
  ],
  "active": true,
  "exp": 1678452354,
  "authorities": [
    "test"
  ],
  "jti": "76074028-0b3c-4482-a7f4-75427e0691c1",
  "client_id": "XcWebApp"
}

资源服务获取用户身份信息

现在认证服务生成JWT令牌的user_name属性已经存储用户JSON格式的信息,在资源服务中就可以取出该JSON格式的内容转换为用户对象去使用

第一步: 在content-api工程中写一个工具类SecurityUtil用来在各个微服务中获取到当前登录用户信息的对象

@Slf4j
public class SecurityUtil {
    public static XcUser getUser() {
        try {
            // 通过SecurityContextHolder获取user_name属性的值即包含用户信息的Json字符串
            Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            if (principal instanceof String) {
                // 将包含用户信息的Json字符串转换为XcUser对象
                String userJson = principal.toString();
                XcUser xcUser = JSON.parseObject(userJson, XcUser.class);
                return xcUser;
            }
        } catch (Exception e) {
            log.error("获取当前登录用户身份信息出错:{}", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }
    // 这里使用内部类是为了不让content工程去依赖auth工程
    @Data
    public static class XcUser implements Serializable {
        private static final long serialVersionUID = 1L;
        private String id;
        private String username;
        private String password;
        private String salt;
        private String name;
        private String nickname;
        private String wxUnionid;
        private String companyId;
        /**
        * 头像
        */
        private String userpic;
        private String utype;
        private LocalDateTime birthday;
        private String sex;
        private String email;
        private String cellphone;
        private String qq;
        /**
        * 用户状态
        */
        private String status;
        private LocalDateTime createTime;
        private LocalDateTime updateTime;
    }
}

第二步: 在内容管理服务中的查询课程信息接口中使用工具类获取当前登陆的用户信息

@ApiOperation("根据课程id查询课程基础信息")
@GetMapping("/course/{courseId}")
public CourseBaseInfoDto getCourseBaseById(@PathVariable Long courseId) {
    SecurityUtil.XcUser user = SecurityUtil.getUser();
    System.out.println("当前用户身份为:" + user);
    return courseBaseInfoService.getCourseBaseInfo(courseId);
}

第三步: 启动认证服务、网关、内容管理服务, 首先访问认证服务生成令牌,然后携带生成的令牌访问内容管理服务的查询课程接口,最后在控制台中查看登陆的用户信息

当前用户身份为:SecurityUtil.XcUser(id=52, username=Kyle, password=null, salt=null, name=Kiki, nickname=null, wxUnionid=null, companyId=1232141425, userpic=null, utype=101002, birthday=null, sex=1, email=null, cellphone=null, qq=null, status=, createTime=2022-09-28T08:32:03, updateTime=null)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux常见基础指令

入门常见基础指令 ls、stat、 pwd 、cd、tree、 whoami、 touch、 mkdir、 rm 、 man、 cp、mv、cat、tac、echo、>、 >>、 < 、more、 less、 head、 tail、date、 cal、 find、 which、alias、whereis、grep、zip与unzip、 tar、bc、uname、xargs... 热键Tab、…

DrGraph原理示教 - OpenCV 4 功能 - 颜色空间

前言 前段时间&#xff0c;甲方提出明确需求&#xff0c;让把软件国产化。稍微研究了一下&#xff0c;那就转QT开发&#xff0c;顺便把以前的功能代码重写一遍。 至于在Ubuntu下折腾QT、OpenCV安装事宜&#xff0c;网上文章很多&#xff0c;照猫画虎即可。 这个过程&#xff0…

金蝶云星空业务对象扩展

文章目录 金蝶云星空业务对象扩展 金蝶云星空业务对象扩展 当前对象已经存在扩展不允许再次扩展。 一般来说&#xff0c;不允许同级多次扩展。因为同级扩展会出现界面元素冲突的情况。 但是通过不同应用扩展部署到环境的&#xff0c;允许一个开发商只能扩展一次标准产品。 不过…

页面布局--Flexbox的自动边距

标题页面布局–Flexbox的自动边距 通过简单的margin:auto&#xff0c;我们就能实现元素的多种对齐方式。 假设我们在盒子模型里有四个元素&#xff1a; 先给容器使用flex布局&#xff1a; .container {display: flex;justify-content: flex-start;align-items: center;gap: 6…

{MySQL}索引事务和JDBC

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、索引1.1索引是什么1.2作用1.3代码 二、事务2.1什么是事务2.2使用 三.JDBC总结 前言 接着上次&#xff0c;继续讲下MySQL 提示&#xff1a;以下是本篇文章正…

第六课:冷战和消费主义、个人计算机革命、图形用户界面(GUI)及3D图形

第六课&#xff1a;冷战和消费主义、个人计算机革命、图形用户界面&#xff08;GUI&#xff09;及3D图形 第二十四章&#xff1a;冷战和消费主义本课概括&#xff1a;政府和消费者推动了计算机的发展 第二十五章&#xff1a;个人计算机革命本集概括&#xff1a;继续讲计算机发展…

机器学习系列11:减少过拟合——L1、L2正则化

如果我们注意到模型在训练集上的表现明显优于模型在测试集上的表现&#xff0c;那么这就是模型过拟合了&#xff0c;也称为 high variance。 产生的过拟合的原因是对于给定的训练集数据来说&#xff0c;模型太复杂了。有几种可以减少过拟合的方法&#xff1a; 收集更多的训练数…

Docker 概述以及整体架构

文章目录 一、Docker概述1.1 什么是 Docker1.2 Docker 如何工作1.3 底层技术 二、Docker架构2.1 Docker 整体架构2.2 Docker daemon2.3 Docker client2.4 Docker registries2.5 Docker objects2.6 Docker Desktop 参考资料 一、Docker概述 1.1 什么是 Docker Docker是一个用于…

快来检测一下你是否真的学会了C语言,保证你看完后收获满满!!

文章目录 每日一言1234567891011121314151617181920结语 每日一言 人生而自由&#xff0c;却无往不在枷锁中。 --社会契约论 1 以下程序段的输出结果是&#xff1f; char s[]"\\141\141abc\t"; printf("%d\n",strlen(s));A. 9 B. 12 C. 13 D. 14 正确答…

程序的编译、链接

目录 前言&#xff1a; 前置知识回顾 宏 宏定义常量 宏定义语句 宏定义函数 条件编译 应用场景 编译过程概览 预编译阶段 编译阶段 汇编阶段 链接阶段 前言&#xff1a; 在ANSI C的任何一种实现中&#xff0c;存在两种不同的环境&#xff0c;第1种是翻译环境&#x…

go module本地包导入

go module本地包导入 本文目录 go module本地包导入启用go mod主项目工作目录本地module目录发布和使用模块 golang 1.11之后加入了go mod来替代GOPATH 官方文档参考&#xff1a;https://golang.google.cn/doc/tutorial/call-module-code 启用go mod 开启 Go modules # 临时开…

一文带你了解大模型的RAG(检索增强生成) | 概念理论介绍+ 代码实操(含源码)

针对大型语言模型效果不好的问题&#xff0c;之前人们主要关注大模型再训练、大模型微调、大模型的Prompt增强&#xff0c;但对于专有、快速更新的数据却并没有较好的解决方法&#xff0c;为此检索增强生成&#xff08;RAG&#xff09;的出现&#xff0c;弥合了LLM常识和专有数…

数据治理:释放数据价值的关键

随着数字化时代的到来&#xff0c;数据已成为组织和企业最重要的资产之一。然而&#xff0c;数据的快速增长和复杂性也给数据管理带来了巨大的挑战。为了确保数据的质量、安全性和合规性&#xff0c;数据治理已成为组织和企业必须面对的重要问题。数据治理是数据要素市场建设的…

自动驾驶学习笔记(二十三)——车辆控制模型

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo开放平台9.0专项技术公开课》免费报名—>传送门 文章目录 前言 运动学模型 动力学模型 总结…

Java进阶(第八期): Java中递归的的使用和递归解决一些算法问题 Java中的异常机制、异常的处理逻辑 自定义异常

文章目录 一、递归1.1 递归的介绍1.2 递归的简单练习1.3 图解递归执行流程&#xff1a;1.4 使用递归完成悲波那契数列1.5 猴子吃桃子问题 二、异常三 、异常的处理逻辑3.1 try catch 捕获异常3.2 throws抛出异常 四、自定义异常 Java进阶&#xff08;第八期&#xff09; 一、递…

如何安装、配置、启动及访问Nacos

准备 JDK 17.0&#xff1a;https://www.bilibili.com/video/BV1ig4y1k7Bq?p2 MySQL 8.0&#xff1a;https://www.bilibili.com/video/BV1QU4y117Vn Navicat Premium&#xff1a;https://www.bilibili.com/video/BV1F94y1A7nC 1、安装Nacos a、地址 网址&#xff1a;http…

ElasticSearch 架构设计

介绍 ElasticSearchMySQLIndexTableDocumentRowFieldColumnMappingSchemaQuery DSLSQLaggregationsgroup by&#xff0c;avg&#xff0c;sumcardinality去重 distinctreindex数据迁移 ElasticSearch 中的一个索引由一个或多个分片组成 每个分片包含多个 segment&#xff08;分…

用 Node.js 写一个爬虫

自己设计一个网站&#xff0c;然后去爬取别人家页面的数据来做一个自己的网站。哈哈哈&#xff0c;如果自己写着玩可能没啥事&#xff0c;但如果用这个网站来获利&#xff0c;你可能就要被寄律师函了&#xff0c;毕竟这有点‘刑’。这篇文章呢&#xff0c;就带大家爬取豆瓣TOP2…

HDMI2.1输入转4Port MIPI/LVDS输出,嵌入式SPI闪存固件存储,VR和AR应用首选国产芯片方案-LT6911GXC

描述 LT6911GXC是一款高性能的HDMI2.1到MIPI或LVDS芯片&#xff0c;用于VR/显示应用。 HDCP RX作为HDCP中继器的上游&#xff0c;可配合其他芯片的HDCPTX实现中继器功能。 对于HDMI2.1输入&#xff0c;LT6911GXC可以配置为3/4通道。自适应均衡使其适合于长电缆应用&#xff0c;…

申请虚拟VISA卡Fomepay教程

fomepay 用下面的注册链接直达 https://gpt.fomepay.com/#/pages/login/index?dS21BA1 或者扫描下面图片的二维码直达注册 注册后尽量随用随充值不建议放大量现金在里面。