authorization server client resource 使用2

news2025/1/13 3:36:53

authorization server && client && resource 使用2

oauth2 整合 jwt

authorization server && client && resource 使用1 中默认的示例就是使用的jwt 生成token(用于),当然这里和我们用户登录的token是有区别的

oauth2 server 和jwt

流程

我们授权服务器端支持授权码模式(用户需要登录)和客户端默认(和用户无关)

步骤

生成

1、OAuth2ClientAuthenticationFilter 对clientid 和clientsecret做一些验证,和jwt无关

2、然后不管是哪种模式在上一步验证通过后会进入OAuth2TokenEndpointFilter中再次进行验证,

​ ProviderManager#authenticate

​ OAuth2ClientCredentialsAuthenticationProvider#authenticate(这里认证成功后会生成token)

​ DelegatingOAuth2TokenGenerator#generate

​ JwtGenerator#generate

解析

BearerTokenAuthenticationFilter#doFilterInternal

ProviderManager#authenticate

JwtAuthenticationProvider#authenticate

JwtAuthenticationProvider#getJwt (这里使用了jwtDecoder)

JwtAuthenticationConverter#convert

配置生效

一样的,搞不明白,

官方oauth结合jwt示例

代码
@Configuration
public class RestConfig {

	@Value("${jwt.public.key}")
	RSAPublicKey key;

	@Value("${jwt.private.key}")
	RSAPrivateKey priv;

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		// @formatter:off
		http
				.authorizeHttpRequests((authorize) -> authorize
						.anyRequest().authenticated()
				)
				.csrf((csrf) -> csrf.ignoringAntMatchers("/token"))
				.httpBasic(Customizer.withDefaults())
				.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
				.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
				.exceptionHandling((exceptions) -> exceptions
						.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
						.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
				);
		// @formatter:on
		return http.build();
	}

	@Bean
	UserDetailsService users() {
		// @formatter:off
		return new InMemoryUserDetailsManager(
			User.withUsername("user")
				.password("{noop}password")
				.authorities("app")
				.build()
		);
		// @formatter:on
	}

	@Bean
	JwtDecoder jwtDecoder() {
		return NimbusJwtDecoder.withPublicKey(this.key).build();
	}

	@Bean
	JwtEncoder jwtEncoder() {
		JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
		JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
		return new NimbusJwtEncoder(jwks);
	}

}

@RestController

public class HelloController {

   @GetMapping("/")
   public String hello(Authentication authentication) {
      return "Hello, " + authentication.getName() + "!";
   }

}
@RestController
public class TokenController {

	@Autowired
	JwtEncoder encoder;

	@PostMapping("/token")
	public String token(Authentication authentication) {
		Instant now = Instant.now();
		long expiry = 36000L;
		// @formatter:off
		String scope = authentication.getAuthorities().stream()
				.map(GrantedAuthority::getAuthority)
				.collect(Collectors.joining(" "));
		JwtClaimsSet claims = JwtClaimsSet.builder()
				.issuer("self")
				.issuedAt(now)
				.expiresAt(now.plusSeconds(expiry))
				.subject(authentication.getName())
				.claim("scope", scope)
				.build();
		// @formatter:on
		return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
	}

}
测试

获取token, 访问需要权限验证的接口,只要带上认证的请求头,Basic base64( 用户名:密码)

获取token之后通过token访问其他接口

突然发现只要知道了你的jwt秘钥,然后就。。。。。

然后就是示例中还直接指定了异常处理,这个似乎就一个返回403和401的作用,具体看相应的实现

自定义JWT配置

数据样例
{
  "sub": "sry",
  "aud": "messaging-client",
  "nbf": 1669812400,
  "scope": [
    "openid",
    "profile",
    "message.read",
    "message.write"
  ],
  "iss": "http://localhost:8080",
  "exp": 1669812700,
  "iat": 1669812400
}
配置类

更多配置方法查看 官方文档,感觉有点多,这儿也有一部分,官方文档2

配置项可以查看 OAuth2ResourceServerConfigurer.JwtConfigurer

配置类更适合扩展,如果没有特殊需求,全部在配置文件中进行配置更简单

公钥和私钥外置

官方提供其他读取配置文件的属性具体参考OAuth2ResourceServerProperties,这里是自定义的读取方式,需要结合自定义配置类

jwt:
  private.key: classpath:app.key
  public.key: classpath:app.pub

也可以生成jks文件

配置类

主要是解码器,编码器,openid(id_token)扩展,token内容扩展,token设置(包含失效时间)等相关配置,当然官网还有很多其他配置,

@Configuration
public class Oauth2JwtConfig {

    @Value("${jwt.public.key}")
    RSAPublicKey pubKey;

    @Value("${jwt.private.key}")
    RSAPrivateKey priKey;


    /**
     * 自定义校验
     * */
    @Bean
    OAuth2TokenValidator<Jwt> audienceValidator() {
        return new JwtClaimValidator<List<String>>(AUD, aud -> aud.contains("messaging"));
    }

    /**
     * 向jwt token 的payload中添加自定义数据
     * */
    @Bean
    public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer(OidcUserInfoService userInfoService) {
        return (context) -> {
            // 授权码登录这里可以获取到用户名称,客户端登录这里可以获取到客户端id
            JwtClaimsSet.Builder claims1 = context.getClaims();
            // 如果是access_token模式,添加对应的返回数据
            if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
                context.getClaims().claims((claims) -> {
                    claims.put("claim-1", "value-1");
                });
            }
            // 如果是id_token模式,这里添加对应的返回数据,一般
            if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {
                OidcUserInfo userInfo = userInfoService.loadUser(
                        context.getPrincipal().getName());
                context.getClaims().claims(claims ->
                        claims.putAll(userInfo.getClaims()));
            }
        };
    }



    @Bean
    public JWKSource<SecurityContext> jwkSource(KeyPair keyPair) {
        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);
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {

        NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
        // 解码的自定义处理器
        jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter());
        return jwtDecoder;
    }


    @Bean
    JwtEncoder jwtEncoder(JWKSource<SecurityContext> jwkSource) {
        return new NimbusJwtEncoder(jwkSource);
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    KeyPair generateRsaKey() {
        // 直接使用配置文件中的公私钥,具体可以使用java自带的
        return new KeyPair(this.pubKey,this.priKey);
    }


    class UsernameSubClaimAdapter implements Converter<Map<String, Object>, Map<String, Object>> {
        private final MappedJwtClaimSetConverter delegate =
                MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());

        public Map<String, Object> convert(Map<String, Object> claims) {
            Map<String, Object> convertedClaims = this.delegate.convert(claims);

            String username = (String) convertedClaims.get("user_name");
            convertedClaims.put("sub1", "你好呀");

            return convertedClaims;
        }
    }

}

扩展id_token的内容,增加userinfo接口的返回数据

@Service
public class OidcUserInfoService {

	private final UserInfoRepository userInfoRepository = new UserInfoRepository();

	public OidcUserInfo loadUser(String username) {
		return new OidcUserInfo(this.userInfoRepository.findByUsername(username));
	}

	static class UserInfoRepository {

		private final Map<String, Map<String, Object>> userInfo = new HashMap<>();

		public UserInfoRepository() {
			this.userInfo.put("sry", createUser("sry"));
			this.userInfo.put("sry", createUser("sry"));
		}

		public Map<String, Object> findByUsername(String username) {
			return this.userInfo.get(username);
		}

		private static Map<String, Object> createUser(String username) {
			return OidcUserInfo.builder()
					.subject(username)
					.name("First Last")
					.givenName("First")
					.familyName("Last")
					.middleName("Middle")
					.nickname("User")
					.preferredUsername(username)
					.profile("https://example.com/" + username)
					.picture("https://example.com/" + username + ".jpg")
					.website("https://example.com")
					.email(username + "@example.com")
					.emailVerified(true)
					.gender("female")
					.birthdate("1970-01-01")
					.zoneinfo("Europe/Paris")
					.locale("en-US")
					.phoneNumber("+1 (604) 555-1234;ext=5678")
					.claim("address", Collections.singletonMap("formatted", "Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance"))
					.updatedAt("1970-01-01T00:00:00Z")
					.build()
					.getClaims();
		}
	}

}

每个注册客户端都可以单独的配置超时时间,参考注释部分,

	@Bean // 4
	public RegisteredClientRepository registeredClientRepository() {
		RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
				.clientId("messaging-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:8090/login/oauth2/code/messaging-client")
				.redirectUri("https://www.baidu.com")
				.scope(OidcScopes.OPENID)
				.scope(OidcScopes.PROFILE)
				.scope("message.read")
				.scope("message.write")
				.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
				// 设置token有效时间,设置刷新token的有效事假,设置刷新refresh_token是否重复使用,更多参考TokenSettings.builder()中的设置,里面还有token格式化的类型
				.tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofMinutes(60))
						.refreshTokenTimeToLive(Duration.ofMinutes(120))
						.reuseRefreshTokens(false).build())
				.build();

		return new InMemoryRegisteredClientRepository(registeredClient);
授权码请求的返回记录
{
    "access_token": "eyJraWQiOiI3Y2FhMDI3MC0xNWM3LTQ0NTgtYWQxMi1iNmFhNTg5ZmE1MWMiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzcnkiLCJhdWQiOiJtZXNzYWdpbmctY2xpZW50IiwibmJmIjoxNjY5ODg5NzU2LCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwibWVzc2FnZS5yZWFkIiwibWVzc2FnZS53cml0ZSJdLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAiLCJleHAiOjE2Njk4OTAwNTYsImNsYWltLTEiOiJ2YWx1ZS0xIiwiaWF0IjoxNjY5ODg5NzU2fQ.iObZNUDUYlkJl39ksOhV6PrM-wNwxwHAohMjUGFumwJqZr8d2NrASqRD839VtDvr717EzdXSEZpZNV6h6TMRizDE-fVjDdHdmGcd-ANwzCrDqGYLi5UE4nZ8drWLvhPW4QNCwFKDCOjJ6Mq_QPs_XN3lx2AXRuZW0GMAJx8NhIzGitgr9l0zky8BIBKyQRnFjej7GOo7zQb24-tvOwaGyfmCo2MKu6ZMe_l7Y5u-T6twOeaWFiP75KL41QyJZO3hCAgqKysszVJITezITCjJLrHHibv7adlE2N23mVZhb2pVVve8Y3ji--Qawa71v6P9f7Ax_SDzgo4OUzH95KnwWA",
    "refresh_token": "_-g2uERYiodVRQJ5r5E1peVPDHmVTFEDoVW3pr0HVkwIx_zY_pIDl7M5UYUarAKCkqKo-xeJ2rIBiln7CwCGi-mbGsTbCGwJn6Pzj-M5yB4U0tTQ1bFe54EI3AV7ateF",
    "scope": "openid profile message.read message.write",
    "id_token": "eyJraWQiOiI3Y2FhMDI3MC0xNWM3LTQ0NTgtYWQxMi1iNmFhNTg5ZmE1MWMiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzcnkiLCJ6b25laW5mbyI6IkV1cm9wZS9QYXJpcyIsImJpcnRoZGF0ZSI6IjE5NzAtMDEtMDEiLCJnZW5kZXIiOiJmZW1hbGUiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzcnkiLCJsb2NhbGUiOiJlbi1VUyIsInVwZGF0ZWRfYXQiOiIxOTcwLTAxLTAxVDAwOjAwOjAwWiIsImF6cCI6Im1lc3NhZ2luZy1jbGllbnQiLCJuaWNrbmFtZSI6IlVzZXIiLCJleHAiOjE2Njk4OTE1NjIsImlhdCI6MTY2OTg4OTc2MiwiZW1haWwiOiJzcnlAZXhhbXBsZS5jb20iLCJ3ZWJzaXRlIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhZGRyZXNzIjp7ImZvcm1hdHRlZCI6IkNoYW1wIGRlIE1hcnNcbjUgQXYuIEFuYXRvbGUgRnJhbmNlXG43NTAwNyBQYXJpc1xuRnJhbmNlIn0sInByb2ZpbGUiOiJodHRwczovL2V4YW1wbGUuY29tL3NyeSIsImdpdmVuX25hbWUiOiJGaXJzdCIsIm1pZGRsZV9uYW1lIjoiTWlkZGxlIiwicGljdHVyZSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vc3J5LmpwZyIsImF1ZCI6Im1lc3NhZ2luZy1jbGllbnQiLCJuYW1lIjoiRmlyc3QgTGFzdCIsInBob25lX251bWJlciI6IisxICg2MDQpIDU1NS0xMjM0O2V4dD01Njc4IiwiZmFtaWx5X25hbWUiOiJMYXN0In0.BL1TuwdfIqvAM5sfMLd3s3NvNipednsgZdvNEcO6_PyXCR9i7PIy4MvqzRdZfuoNVoJa16qL1e1wGuI1pNp21rk8PMSBusO1jNOSbYGoI5hFaBMeWFKQgYJc-i5e8kI5yyd-Nar6xuDYSgijoe1Wte4UxhFM1Jeuq7WjcCl_AaevNZM7WzV2914oTVismOCYOkLHw-ISHopifOi4TfW3vOAVZfZFKNdGNIwbNMEZw8YGM4tI_MDF6CBfbmqiWWGOihINEiBPmYn2RIqRSCSMsFfuXpdYAm-zlEpXMjgWl-X2ENwjYI5XbOgfOf-zfFQ0jPbVMhaduj5GXY3y9YkImA",
    "token_type": "Bearer",
    "expires_in": 3599
}

单点登录实现方式概述

oauth2实现单点登录

主要是查看之前 authorization server && client && resource 使用1 中的示例

就客户端而言,两个客户端使用相同的clientId和clientSecret,客户端的地址可以不一样(一定要能重定向回来),服务端的重定向地址必须包含多个客户端配置,人后以下是测试的客户端配置

    @Value("${server.port}")
    private String port;

    // 尝试通过http://localhost:8080/.well-known/openid-configuration获取相关接口
    private ClientRegistration localClientRegistration() {
        return ClientRegistration.withRegistrationId("messaging-client")
                .clientId("messaging-client")
                .clientSecret("secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("http://127.0.0.1:"+port+"/login/oauth2/code/messaging-client")
                .scope("openid", "profile", "message.read", "message.write")
                .authorizationUri("http://127.0.0.1:8080/oauth2/authorize")
                .tokenUri("http://127.0.0.1:8080/oauth2/token")
                .userInfoUri("http://127.0.0.1:8080/userinfo")
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .jwkSetUri("http://127.0.0.1:8080/oauth2/jwks")
                .clientName("messaging-client")
                .build();
    }

由于请求的session存储的cookie是基于ip的,所以本地测试修改session存储的cookie的名称需要修改,或者修改hosts

server:
  port: 8090
  servlet:
    #防止Cookie冲突,冲突会导致登录验证不通过
    session:
      cookie:
        name: OAUTH2-CLIENT-SESSIONID${server.port}

什么都不需要修改,也不需要@EnableOAuth2Sso注解。然后就可以实现单点登录,

成功测试

当然上述是基于授权服务端的用户登录是存放cookie的,如果使用JWT,可能需要另外考虑

CAS(中央认证服务)

发现这个需要搭建一个cas的服务端,而security只是支持我们去连接cas服务器,。。。。。无语,以为服务端和客户端security都实现的

security cas客户端认证流程

官方文档

web浏览器、CAS服务器和Spring Security安全服务之间的基本交互如下

1、用户访问需要认证的服务,抛出异常,ExceptionTranslationFilter进行处理

2、如果使用了cas,那么ExceptionTranslationFilter将会调用CasAuthenticationEntryPoint进行身份认证

3、CasAuthenticationEntryPoint将会重定向到 对应的cas认证页面,例如my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas.

4、如果存在session cookie ,那么用户不需要再次进行认证,会从定向会原有的服务地址,附带令牌,例如server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ.

5、CasAuthenticationFilter会 处理这个请求,请求验证后会被配置的AuthenticationManager 进行处理

6、然后CasAuthenticationProvider中会使用ticket请求cas服务器my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas&ticket=ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl=https://server3.company.com/webapp/login/cas/proxyreceptor.

7、如果对CAS验证服务的请求包含代理回调URL(在pgt-URL参数中),CAS将在XML响应中包含pgt-Iou字符串。此pgt Iou代表授权票据Iou的代理。然后,CAS服务器将创建自己的HTTPS连接返回pgt Url。这是为了相互验证CAS服务器和声明的服务URL。如果cas验证成功,HTTPS连接将用于向原始web应用程序发送代理授权票证。例如: server3.company.com/webapp/login/cas/proxyreceptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH.

8、Cas20Ticket Validator将分析从CAS服务器接收到的XML。它将向CasAuthenticationProvider返回一个票据响应,其中包括用户名(必填)、代理列表(如果涉及)和代理授权票据欠条(如果请求了代理回调)。

9、CasAuthenticationProvider中的CasProxyDecider会验证cas响应的票据,CasProxyDecider有这几个实现RejectProxyTickets, AcceptAnyCasProxyandNamedCasProxyDecider

10、如果验证没有问题CasAuthenticationProvider会返回一个CasAuthenticationToken

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-cas</artifactId>
        </dependency>

cas服务端搭建

项目地址:https://github.com/apereo/cas-overlay-template

项目文档:https://apereo.github.io/cas/6.6.x/index.html

最新的版本似乎是需要jdk17,而且需要使用gradle。

其他

算了,仅仅了解这么多,以后再说。

SAML2单点登录

总是SAML2是实现单点登录的,记录一下

gateway和security

仅做说明,未尝试

1、并不是多有服务都需要用户登录后访问,所以部分服务是可以直接放通访问权限

2、对于和用户关联的服务,肯定需要认证后才可以访问,一个是通过结合授权中心,不过最新的spring-authorization-server需要用户登录的模式只有授权码模式,这样oauth2就是作为授权服务器的客户端,整合spring-boot-starter-oauth2-client,这样所有访问网关的服务都会重定向到授权服务进行认证,认证通过后才能访问其他服务,而其他微服务整合oauth2-resource。

3、就淘宝和京东而言,似乎登录虽然重定向了,但是明显不是授权码模式,而是直接登录,然后登录的令牌相关的cookie设置在根域名下,然后所有服务都可以获取到相关的token,从而获取用户信息。 我们可以单独提供认证的服务,网关中设置全局过滤器,如果没有认证,就重定向到认证服务,

4、由于gateway使用的是webflux,相关security配置项查看https://docs.spring.io/spring-security/reference/5.8/reactive/configuration/webflux.html和https://docs.spring.io/spring-security/reference/5.8/reactive/index.html

关联

  • 关联的主题:
  • 上一篇:
  • 下一篇:
  • image: 20221021/1
  • 转载自:

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

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

相关文章

【信息奥赛题解】昆虫繁殖(详细分析题解 C++ 代码)

昆虫繁殖问题 &#x1f31f; 【题目名称】昆虫繁殖 【题目描述】 科学家在热带森林中发现了一种特殊的昆虫&#xff0c;这种昆虫的繁殖能力很强。每对成虫过 XXX 个月后开始产卵&#xff0c;每月产 YYY 对卵&#xff0c;每对卵要过两个月长成成虫。 假设每个成虫不死&#…

SpringCloud01:回顾微服务和微服务架构

回顾微服务和微服务架构微服务微服务架构微服务架构技术栈微服务 微服务架构 微服务架构的四个核心问题 服务很多&#xff0c;客户端怎么访问&#xff1f;这么多服务&#xff0c;服务之间如何通信&#xff1f;这么多服务&#xff0c;如何治理&#xff1f;服务挂了怎么办&#x…

selenium开启Google持久化调试

google selenium调试 需要下载和自己浏览器版本相匹配的版本 这里用google浏览器演示 查看浏览器版本 google浏览器通过在搜索栏输入如下内容查看 chrome://settings/helpchromedriver 下载 到如下任意链接下载对应的版本 CNPM Binaries Mirror http://chromedriver.sto…

#pragma pack(n)内存对齐

1. 为什么要对齐&#xff1f; #pragma pack主要是用在字节对齐方面&#xff0c;为什么要对齐呢&#xff1f; 因为计算机中内存空间都是按照byte划分的&#xff0c;从理论上讲似乎对任何类型的变量的访问可以从任何地址开始&#xff0c;但实际情况是在访问特定变量的时候经常在…

chatGPT非常重要的能力居然不行,就让这个工具来拯救,让你SQL操作无忧

引言 各位好&#xff0c;相信看见这篇文章的朋友&#xff0c;应该也去体验过了chatGPT了吧~&#xff0c;确实chatGPT拉近了我们与未来科技的距离&#xff0c;所有别人火也是非常有道理的&#xff0c;为其点赞。 本文主要是关注chatGPT的SQL能力&#xff1b;因为本人从事IT教育…

用友T+数据备份与恢复方法汇总

一、正常数据备份与恢复 适合于用友T能正常登陆、正常备份的情况。 1、数据备份 以系统管理员admin登陆系统管理&#xff0c;点击“账套维护”&#xff0c;选择好需要备份的账套&#xff0c;点击“备份”&#xff0c;同时设置好备份路径&#xff0c;保存备份文件即可&#xf…

(附源码)Springboot服装网购网站 毕业设计 010234

Springboot服装网购网站 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管…

Day825.死锁问题 -Java 并发编程实战

死锁问题 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于死锁问题。 用 Account.class 作为互斥锁&#xff0c;来解决银行业务里面的转账问题&#xff0c;虽然这个方案不存在并发问题&#xff0c;但是所有账户的转账操作都是串行的&#xff0c;例如账户 A 转账户 B…

【华为上机真题 2022】完全二叉树非叶子节点后序遍历

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

不知道哪个视频软件可以一键成片?建议收藏这些软件

随着短视频平台的不断发展&#xff0c;越来越多的小伙伴加入进了短视频剪辑的行列当中。现在市面上的视频剪辑软件很多&#xff0c;但是对于新手小白来说&#xff0c;能够快速上手使用的视频编辑软件才是重要的。那你们知道一键成片视频软件哪个好吗&#xff1f;有想要制作短视…

2、基于XML的依赖注入详细配置

一、依赖注入详细配置 1、通过P名称空间为Bean注入值 1、简化setter方式注入的配置&#xff0c;可以不使用<property />子标签来为属性设置值&#xff0c;直接在<bean />标签中设置各依赖项的值。 2、注入值类型说明&#xff1a; 对于String、基本类型及其包装类使…

visio绘图小技巧

1.如何在图框的任意位置添加点&#xff1f; 先选中x点指令&#xff0c;再按住ctrl键&#xff0c;即可在任意位置画点 2.如何画出锯齿形线段&#xff1f; visio里面好像没有现成的锯齿形线段&#xff0c;所以可以利用直线反复折画&#xff0c;但是这里有个小技巧&#xff0c;就…

[附源码]JAVA毕业设计小王防疫副食品配送商城(系统+LW)

[附源码]JAVA毕业设计小王防疫副食品配送商城&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项…

获取网易云音乐开放接口api的推荐歌单

网易云音乐开放api接口 网址&#xff1a;https://binaryify.github.io/NeteaseCloudMusicApi/#/?idneteasecloudmusicapi 项目地址&#xff1a;https://github.com/Binaryify/NeteaseCloudMusicApi 下载下来之后&#xff0c;安装依赖&#xff1a;npm install 启动服务&#xf…

决策树(decision tree)

决策树构建&#xff1a;特征选择、决策树的生成和决策树的修剪。 特征选择 特征选择在于选取对训练数据具有分类能力的特征。这样可以提高决策树学习的效率&#xff0c;如果利用一个特征进行分类的结果与随机分类的结果没有很大差别&#xff0c;则称这个特征是没有分类能力的…

Lifeform——站在3D虚拟数字身份与元宇宙结合的风口之上

Web3和元宇宙是公认的下一个风口&#xff0c;吸引了大量用户的关注和参与。从《我的世界》中的场景编辑器&#xff0c;到DecentreLand&#xff0c;以及Sandbox中玩家对自己虚拟地块的装饰。人们在虚拟世界中举办各种活动&#xff0c;比如演唱会、节日庆祝或者比赛、游戏甚至婚礼…

PrizmDoc Viewer添加了新的Excel渲染选项

PrizmDoc Viewer添加了新的Excel渲染选项 添加了新的Microsoft Excel渲染选项。PrizmDoc Viewer现在提供了在MSO渲染模式下查看Excel文档时自动调整单元格宽度和/或高度的选项。 PrizmDoc Viewer Microsoft Office转换附加选项现在与运行在Microsoft Windows Server 2019/2022上…

谷粒商城8:分布式锁使用和springcache的整合

1.分布式锁redisson使用 ①分布式锁简介 ②分布式锁学习过程 ③最终分析 加入锁为原子操作&#xff1a;设置过期时间设置锁 删除锁为原子操作&#xff1a;业务流程删除锁 将锁的过期时间调长 ④Redisson的使用 引入依赖程序化配置使用 创建redisson客户端 Redisson.create(…

6.论文学习Modality-aware Mutual Learning for Multi-modal Medical Image Segmentation

目录一.摘要1.背景解决方法1.如何有效整合来自多模态医学图像的信息2.如何处理常见模式缺失的情况2.解决1.ML2.MA3.结论二.方法2.1模态特定模型Modality-specific Model2.2 Modality-Aware Module2.3互学策略Mutual Learning Strategy三.实验与结果3.1数据集和评估指标3.2运行细…

杨校老师课堂之Spring Boot框架面试题【开发工程师面试前必看】

1. 什么是 Spring Boot&#xff1f; Spring Boot 是 Spring 开源组织下的子项目&#xff0c;是 Spring 组件一站式解决方案&#xff0c;主要是简化了使用Spring 的难度&#xff0c;简省了繁重的配置&#xff0c;提供了各种启动器&#xff0c;使开发者能快速上手。 2. 为什么要用…