Spring Authorization Server1.0 介绍与使用

news2024/11/17 21:53:44

一、版本使用

   1、Java:17或者更高的版本。

   2、springboot 3.0

   3、Spring Authorization Server 1.0版本。

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>1.0.0</version>
</dependency>

二、OAuth2涉及的参与者

  • RO (resource owner): 资源所有者,对资源具有授权能力的人。也就是登录用户。
  • RS (resource server): 资源服务器,它存储资源,并处理对资源的访问请求。
  • Client: 第三方应用,它获得RO的授权后便可以去访问RO的资源。
  • AS (authorization server): 授权服务器,它认证RO的身份,为RO提供授权审批流程,并最终颁发授权令牌(Access Token)。

注意:为了便于协议的描述,这里只是在逻辑上把AS与RS区分开来;在物理上,AS与RS的功能可以由同一个服务器来提供服务。

三、授权服务器(authorization server)

   授权服务器授权的类型有三种:授权码模式、客户端模式、刷新模式。

1、授权码模式(authorization_code) 

 步骤:

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

参数说明:

A步骤中,客户端申请认证的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

示例:

http://localhost:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=message.read&redirect_uri=http://127.0.0.1:8080/authorized

通过示例请求出现一个登录界面:

输入你配置的账号及密码,若是账号密码没有问题,则会返回授权码既是路径中出现code="ssssssss"。若是出现输入账号密码都没有问题,却返回账号密码相关的问题(No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken) 

此时需要查看你密码加密问题,后面配置编码器的时候会提一下。

若是路径没有返回code,则去数据库(oauth2_authorization)看看有木有出现授权码(authorization_code_value),若有直接复制。

接着根据授权码获取token,如:

 此处需要注意:需要增加客户端的配置信息(clientId、clientSecret)

 最后获取:

2、客户端模式(client_credentials)

 客户端模式指客户端以自己的名义,而不是以用户的名义,向服务提供商进行认证。

 接着输入客户端配置信息:

最后得到:

注意:客户端模式无刷新token操作。

 3、刷新模式(refresh_token)

接着添加客户端配置信息:

最后得到:

 4、授权服务器配置

AuthorizationServerConfig配置:
package com.allen.demoserver.config;

import com.allen.component.redis.repository.RedisRepository;
import com.allen.demoserver.service.RedisToJdbcService;
import com.allen.demoserver.service.impl.RedisOAuth2AuthorizationConsentService;
import com.allen.demoserver.service.impl.RedisOAuth2AuthorizationService;
import com.allen.demoserver.service.impl.RedisToJdbcServiceImpl;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0
        http
                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -> exceptions
                        .authenticationEntryPoint(
                                new LoginUrlAuthenticationEntryPoint("/login"))
                )
                // Accept access tokens for User Info and/or Client Registration
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

        return http.build();
    }

    /**
     * oauth2 用于第三方认证,RegisteredClientRepository 主要用于管理第三方(每个第三方就是一个客户端)
     *
     * @return
     */
    @Bean
    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("demo-client")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/demo-client-oidc")
                .redirectUri("http://127.0.0.1:8080/authorized")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope("message.read")
                .scope("message.write")
                .clientSettings(ClientSettings.builder()
                        //client端请求 jwt解密算法
                        .jwkSetUrl("http://localhost:9090/oauth2/jwks")
                        .requireAuthorizationConsent(true)
                        .build())
                .tokenSettings(TokenSettings.builder()
                        //使用透明方式,默认是 OAuth2TokenFormat SELF_CONTAINED,生成 JWT 加密方式
                        // SELF_CONTAINED 生成 JWT 加密方式
//				.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
//				.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
                        .authorizationCodeTimeToLive(Duration.ofHours(1))
                        .accessTokenTimeToLive(Duration.ofHours(2))
                        .refreshTokenTimeToLive(Duration.ofHours(3))
                        .reuseRefreshTokens(false)
                        .build())
                .build();

        // Save registered client in db as if in-memory
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        if (null == registeredClientRepository.findByClientId("demo-client")){
            registeredClientRepository.save(registeredClient);
        }

        return registeredClientRepository;
    }
//    @Bean
//    public RedisToJdbcService redisToJdbcService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
//        return new RedisToJdbcServiceImpl(jdbcTemplate, registeredClientRepository);
//    }
//    @Bean
//    public OAuth2AuthorizationService authorizationService(RegisteredClientRepository registeredClientRepository,RedisRepository redisRepository
//            ,RedisToJdbcService redisToJdbcService) {
//        return new RedisOAuth2AuthorizationService(registeredClientRepository,redisRepository,redisToJdbcService);
//    }
    @Bean
    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
    }
//    @Bean
//    public OAuth2AuthorizationConsentService authorizationConsentService(RedisRepository redisRepository, RedisToJdbcService redisToJdbcService) {
//        return new RedisOAuth2AuthorizationConsentService(redisRepository,redisToJdbcService);
//    }


    /**
     * 用于给access_token签名使用。
     *
     * @return
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    /**
     * 配置Authorization Server实例
     * @return
     */
    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }


}
配置说明:
authorizationServerSecurityFilterChain :关于Protocol Endpoints.(协议端点)配置registeredClientRepository:关于客户端的一些配置
authorizationService:关于OAuth2Authorization 授权信息的处理
authorizationConsentService:关于OAuth2AuthorizationConsent信息的处理(入库)jwkSource()、generateRsaKey()、jwtDecoder:关于token生成规则的处理authorizationServerSettings:关于AuthorizationServerSettings【授权服务器】的配置,含路径及接口。

注意:authorizationService、authorizationConsentService 目前框架提供InMemoryOAuth2AuthorizationService(内存)、JdbcOAuth2AuthorizationService(数据库)。若需要实现Redis存储,需要自己去实现OAuth2AuthorizationService接口。关于redis的实现,可以项目看源码

DefaultSecurityConfig配置:
package com.allen.demoserver.config;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;


/**
 * @author xuguocai
 * @date 2022/12/13 21:55
 **/
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {

	/**
	 * 这个也是个Spring Security的过滤器链,用于Spring Security的身份认证。
	 * @param http
	 * @return
	 * @throws Exception
	 */
	@Bean
	@Order(2)
	public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
		throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			// Form login handles the redirect to the login page from the
			// authorization server filter chain
			.formLogin(Customizer.withDefaults());

		return http.build();
	}

	/**
	 * 配置用户信息,或者配置用户数据来源,主要用于用户的检索。
	 * @return
	 */

	@Bean
	UserDetailsService users() {
		UserDetails user = User.withDefaultPasswordEncoder()
				.username("user1")
				.password("123456")
				.roles("USER")
				.build();
		return new InMemoryUserDetailsManager(user);
	}

	public static void main(String[] args) {
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
		String encode = bCryptPasswordEncoder.encode("123456");
		System.out.println(encode);
	}
}

配置说明:

defaultSecurityFilterChain:关于过滤器链的验证配置。
users():关于UserDetailsService 的实现,此处密码基于源码默认PasswordEncoder处理。

 

 注意:基于代码学习,可以使用默认的编码器,既是如上配置方式。若是接口的实现,需要注意引入的编码器对密码的加密。若是配置的编码器出现问题,会影响授权码获取code。

package com.allen.demoserver.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author xuguocai
 * @date 2022/11/24 17:06
 **/
@Slf4j
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private UserClient userClient;

    // 需要注入 new BCryptPasswordEncoder(),既是自己定义这个bean
	@Autowired
	private PasswordEncoder passwordEncoder;

	/**
	 * 基于用户名获取数据库中的用户信息
	 * @param username 这个username来自客户端
	 * @return
	 * @throws UsernameNotFoundException
	 */
	@Override
	public UserDetails loadUserByUsername(String username)
		throws UsernameNotFoundException {
		log.info("loadUserByUsername   {}",username);
		//基于feign方式获取远程数据并封装
		//1.基于用户名获取用户信息
		UserVO data = userClient.selectUserByUserName(username);
		if(data==null) {
			throw new UsernameNotFoundException("用户不存在");
		}

		//2.基于用于id查询用户权限
		String password = passwordEncoder.encode(data.getPassword());
//		log.info("数据:{}",data);
		//3.对查询结果进行封装并返回
		return new User(username, password,
			AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"));
		//返回给认证中心,认证中心会基于用户输入的密码以及数据库的密码做一个比对
	}

}

在配置中,需要注入上面的bean:

 此处需要注意编码器的注入,配置不当会影响授权码的账号密码输入,获取不到code值。

 至此,授权服务器配置完毕。需要拓展的根据自己的需求拓展。

四、资源服务器(resource-server)

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>

此处不再描述,可以查看oauth2-resource 项目,关注它的配置类、配置文件,其次再关注获取全局用户方式。

获取全局用户:它是根据jwt token获取,传的token经过过滤器链,既是【securityFilterChain(HttpSecurity http)】配置,最后从token中获取用户信息。

参考地址:

Spring Authorization Server 官网

Spring Authorization Server 官网示例

OAuth 2.0之授权码模式

gc-oauth2 项目源码示例

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

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

相关文章

使用proxy_pool来为爬虫程序自动更换代理IP

文章目录1. 前言2. 教程3. 官网4. 在线demo4.1. 本地部署4.2. 安装4.2.1. Python源码构建安装4.2.1.1. 安装redis数据库4.2.1.1.1. 下载redis源码4.2.1.1.2. 启动redis服务4.2.1.1.3. 安装redis服务4.2.1.1.4. 再次通过命令启动redis服务4.2.1.1.5. 测试redis服务是否可用4.2.1…

node.js+uni计算机毕设项目基于微信小程序的车位共享系统LWPPT(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

C语言程序设计--银行管理系统

主界面 登入界面 #include <stdio.h> #include <malloc.h> #include <conio.h> #include <time.h> #include <windows.h> #define MB_ICONINFORMATION MB_ICONASTERISK //对 错误 struct account_information /…

腾讯云轻量应用服务器使用Matomo 应用镜像搭建网站流量统计系统!

Matomo 是一款开源的网站数据统计软件&#xff0c;可以用于跟踪、分析您的网站的流量&#xff0c;同时充分保障数据安全性、隐私性。该镜像基于 CentOS 7.6 64位操作系统&#xff0c;已预置 Nginx、MariaDB、PHP 软件。本文介绍如何使用 Matomo 快速搭建您的网站流量统计系统。…

【文本检测】2、DBNet++ | 为 DBNet 引入多级特征图聚合模块 ASF

文章目录一、背景二、方法2.1 Adaptive Scale Fusion (ASF) 模块2.2 Binarization2.3 Adaptive Threshold2.4 Deformable Convolution2.5 Label Generation2.6 Optimization三、效果论文&#xff1a;Real-Time Scene Text Detection with Differentiable Binarization and Adap…

Hadoop综合项目——二手房统计分析(可视化篇)

Hadoop综合项目——二手房统计分析&#xff08;可视化篇&#xff09; 文章目录Hadoop综合项目——二手房统计分析&#xff08;可视化篇&#xff09;0、 写在前面1、数据可视化1.1 二手房四大一线城市总价Top51.2 统计各个楼龄段的二手房比例1.3 统计各个城市二手房标签的各类比…

人工智能轨道交通行业周刊-第27期(2022.12.12-12.25)

本期关键词&#xff1a;虚拟中台、智轨、数字员工客服、钢轨光带异常、小目标检测 1 整理涉及公众号名单 1.1 行业类 RT轨道交通中关村轨道交通产业服务平台人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨…

4、前端笔记-JS-数据类型

1、数据类型简介 1.1为什么需要数据类型 不同的数据占用的存储空间不同&#xff0c;为了充分利用存储空间&#xff0c;便于把数据分成所需内存大小不同的数据&#xff0c;定义了不同的数据类型 1.2变量的数据类型 js是弱类型&#xff08;动态语言&#xff09;的语言&#x…

这12类Oracle日期函数,全都给你总结了

在使用Oracle数据库过程中&#xff0c;对日期的使用不可避免&#xff0c;那Oracle中的日期函数有哪些呢&#xff1f;本篇就日期函数进行整理了&#xff0c;不一定全部了解记住&#xff0c;但是要做到心中有数&#xff0c;以后在写脚本的时候就不会绕弯子了。 1、sysdate、curr…

大话设计模型 Task05 状态、适配、单例

目录一、状态模式问题描述问题分析模式定义代码实现二、适配器模式问题描述问题分析模式定义代码实现三、单例模式问题描述问题分析模式定义代码实现一、状态模式 问题描述 假设我们要描述一名员工一天不同时间的工作状态&#xff0c;正常来看是比较简单的&#xff0c;直接从…

卡塔尔世界杯半自动越位识别技术(SAOT)的工作原理

随着卡塔尔世界杯的深入举行&#xff0c;半自动越位识别技术 (Semi-automated offside technology&#xff0c;简称为 SAOT) 这项数字技术正在被越来越多的国内外球迷所熟知。 作为 VAR(Video Assistant Referee&#xff0c;视频助理裁判) 的扩展&#xff0c;SAOT 的引入是为了…

腾讯云轻量应用服务器使用 OpenFaaS 部署云函数!

OpenFaaS 是开源的流行 FaaS&#xff08;Function-as-a-Service&#xff0c;函数即服务&#xff09;框架&#xff0c;OpenFaaS 让开发者聚焦业务代码的编写&#xff0c;无需过多关注语言框架、部署、配置等其他步骤。 轻量应用服务器 Lighthouse 为您提供了 OpenFaaS 应用镜像…

C语言程序设计--个人账簿管理系统

目的在于&#xff1a; 为编码人员提供依据&#xff1b;为修改、维护提供条件&#xff1b;项目负责人将按计划书的要求布置和控制开发工作全过程&#xff1b;项目质量保证组将按此计划书做阶段性和总结性的质量验证和确认。 本说明书的预期读者包括&#xff1a; 项目开发人员&…

车用DC-DC模块 1224V转5V2A过认证大塑料外壳

名称&#xff1a;车用12V转5V2A电源转换器 型号&#xff1a;LM40J5V2A3S 性质&#xff1a;非隔离型的BUCK电源转换器&#xff0c; 特点&#xff1a;采用集成IC设计&#xff0c;具有转换效率高&#xff0c;体积小&#xff0c;稳定可靠的特点&#xff0c;采用灌胶工艺&#xf…

Arcgis使用教程(十三)ARCGIS地图制图之地图输出参数设置详解

目录 1.加载数据 2.页面大小设置 2.1 根据页面大小设置地图 2.2 根据固定比例尺设置页面大小 1.加载数据 在Arcmap中加载中国矢量图层数据&#xff08;中国省级行政区图&#xff0c;国界线两个图层&#xff09;&#xff0c;切换到Layout View窗口&#xff0c;设置地图符号要…

jvm-sandbox-repeater环境搭建

引流回放这个技术现在真的越来越成为了很多公司测试同学必备的一个工具了。当然引流回放的技术有很多&#xff0c;比如下来会提到的jvm-sandbox-repeater。 当然你也可以通过日志分析解析的方式去获取到请求返回的信息等。因为刚听过testerhome开发者大会&#xff0c;好几个主题…

不愧是阿里内部的「排序和数据结构学习笔记」面面俱到、太全了

算法——成为程序员强者的必经之路 算法到底有多重要不必多说&#xff0c;说「算法正在统治世界」也不为过。 在编程行业内&#xff0c;有句话是这么说的「薪资越高、算法越重要」&#xff0c;互联网头部企业花费了大量的时间和金钱&#xff0c;用来研究新算法&#xff0c;不…

Python pandas有几千个库函数,你用过几个?(6)

上一篇链接&#xff1a; Python pandas有好几百个库函数&#xff0c;你都用过吗&#xff08;5&#xff09;_Hann Yang的博客-CSDN博客 DataFrame 类方法&#xff08;211个&#xff0c;其中包含18个子类、2个子模块&#xff09; >>> import pandas as pd >>&g…

详解c++---string的介绍(下)

这里写目录标题前言string的Modifiersoperatorappendpush_backassigninserterasereplaceswappop_backString的operationsc_strcopyfindrfindfind_first_offind_last_offind_first_not_of和find_last_not_of前言 本片文章我们将继续介绍string的使用&#xff0c;点击&#xff1…

从IP V4 向IP V6 过渡(计算机网络-网络层)

目录 从IP V4 向IP V6 过渡 用双协议栈进行从 IPv4 到 IPv6 的过渡 使用隧道技术从 IPv4 到 IPv6 过渡 从IP V4 向IP V6 过渡 向 IPv6 过渡只能采用逐步演进的办法&#xff0c;同时&#xff0c;还必须使新安装的 IPv6 系统能够向后兼容 IPv6 系统必须能够接收和转发 IPv4 分…