一、简单的Spring Authorization Server示例代码

news2024/11/29 20:35:09

需要有一定的OAuth2的基础
需要有一定的Spring Security基础

Spring Authorization Server

官方简介:Spring Authorization Server is a framework that provides implementations of the OAuth 2.1 and OpenID Connect 1.0
个人理解为OAuth 2.1 and OpenID Connect 1.0的实现。
关于OAuth2有很多的版本,Spring Authorization Server是现在官网正在维护的版本。这里实现的是OAuth2.1,OAuth2.1里面没有密码模式。

在这里插入图片描述

这里用的版本是0.4.2,jdk8的,1.x的版本需要jdk17

这里可以用很简单的代码就实现OAuth2服务端

代码结构如下:
在这里插入图片描述

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

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

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
</dependency>

applicalication.yml
就配了一个端口

server:
  port: 7000

logging:
  level:
    org.springframework.security: debug

启动类, 没啥特别的

@SpringBootApplication
public class Oauth2ServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(Oauth2ServerApplication.class, args);
	}

}

private.key:私钥

MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBKxqzuZocGA9bhUsYCCfsl5cH
6omJ9I554J50tlsDl7AQLf+wX7YYWPdJa2V8o1y55wBqYiFE4JQ4H0sQog8itRS+OqwDKjwbedKo
5KTfUzvGjcFL77CWpU844n8e5A5e1oJbAPYPR/6ccHsPUgSxTAJMabaH3i5PTZ3aXiSjsMuXcg5/
HCKONUopgsb8idWSIvfVf2jM7wYFF5oRux4nlOzOCedQoEOSZQa55DElrFV/nVmi7ExIXH7GVG79
rIq0sDMqViQ6MVuXuFmyqAKd5pGA0f8+6echooKPKZklHGnHDMWmJDlCyoVnJSTti3yEhHaf0lfn
Ao/8qMU+iNX7AgMBAAECggEAZLQKCaQ6+WZ5qybETVUDK06kCBZ3eZorJNK7CPGAZVEREn5IjDR5
hBvtXzNEB0RLNQd+qfdajMPfwZpe0d8KsPdiRwHjZwr/pvtNnYsFgP+tbAe+u83La93mfStnRj1y
WHLQJo1Lug+4Zuok3YnOtHeBw0BhTlfAIMu//XWS+FprVxuBwIB6xvLTtGf5CV230y4DNmemFBuX
93qLFDSG3r/iEZ6U78CzjHjitG0kMfPSQ2TmraUJoel01t9JOONGqtsoitaWR7J5NcwLtrHqYXD5
R9d6E7i5e45Xf8HbrNVF0mgw+JQLkmhcTqWHo0Ws634v1r2HBa0HhYs/s2S3MQKBgQD7C7d43WVg
TprhoU/C6oWQOEAZSdSC8qsqg8CaiO0sv4YMGN7dxOWwuuX3OeXB8EjaL//kjnVyyueb4GF6ugMC
kDmoPg8PFOMP3VOs/0PoXQowC57cWMzzcSVepaHr6GDGl5kPZnhx2TLaPKtTRucPz6/+kWkTQCjz
3kOswTjJzQKBgQDE+v2y5gv4/s7iGfeaGMUB7IWn1G51Ay2pwoI2b60tITcbrhM0t4yxWAKUspoC
x3Qk5QNrUfevyvggBbPk0BL5Zy42HoBKGlh+hwcq8wmzdOGD7wcIvWvITceUxLrjD3/HogRrB6l4
VGsQ4Yq4e+iIc3geC6TDVNz1KMJbaR+25wKBgBOCXJa69dbfJPAl3hHyscB8bpbIgwhOHXknVf9s
ZqoUlDE6eY9YbtUmIRruV+mTZ8X09vjnDT+HfypA7LJh5Dv9w01MzVTJtb+U3pzSFY/oMxN6w7Sx
/fNpNpM9YfD4VRT50P4+Y1vNmkMVdeb52pkC9dVdrYG+ebBB9JZnSad9AoGBAMJoWQU8iGp5yWNb
b4S1l5Jbhlnqjg2MUn/uCaeCNq+IzaPS/P+VfBT3oKxzTQ8bHOTg5awA3Oyx7ItmNXLJbUCa9f/R
wJniQJ6303ovHc7wtzYILbARixPIuAZ611wLyvgTTjr39+lbn8OsZcXH/OrW06ELqtRhqCWJ0bB4
IyXXAoGBAMuMNmblY+iGfShJHWyAYESlFqyhYycfFXpec9Admf1znQ1SIo5VRxaDkMsGZImb5pPD
ZQUxNR5kJNr8rqYt21+kwNn0bteROkjQuP50P3dPcfaZfaYoKSSVEs4bnhzvtWCiHkCqf9jWHV+U
XFkhyPqfh2BZkghtU3MshbEX5IdG

public.key公钥

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwSsas7maHBgPW4VLGAgn7JeXB+qJifSO
eeCedLZbA5ewEC3/sF+2GFj3SWtlfKNcuecAamIhROCUOB9LEKIPIrUUvjqsAyo8G3nSqOSk31M7
xo3BS++wlqVPOOJ/HuQOXtaCWwD2D0f+nHB7D1IEsUwCTGm2h94uT02d2l4ko7DLl3IOfxwijjVK
KYLG/InVkiL31X9ozO8GBReaEbseJ5TszgnnUKBDkmUGueQxJaxVf51ZouxMSFx+xlRu/ayKtLAz
KlYkOjFbl7hZsqgCneaRgNH/PunnIaKCjymZJRxpxwzFpiQ5QsqFZyUk7Yt8hIR2n9JX5wKP/KjF
PojV+wIDAQAB

这个秘钥对在官方示例里面是生成并保存在内存里面的,每次都不一样,这个秘钥对是我自己生成的,保存在文件里面的,你也可以用官方的生成秘钥的那段代码。

读取秘钥

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IoUtil;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * TODO description
 *
 * @author qiudw
 * @date 5/18/2023
 */
public class SecurityUtils {

	public static PublicKey loadPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
		try (InputStream inputStream = SecurityUtils.class.getClassLoader().getResourceAsStream("public.key")) {
			assert inputStream != null;
			byte[] publicKey = IoUtil.readBytes(inputStream);
			KeyFactory keyfactory = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec encodeRule = new X509EncodedKeySpec(Base64.decode(publicKey));
			return keyfactory.generatePublic(encodeRule);
		}
	}
	public static PrivateKey loadPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
		try (InputStream inputStream = SecurityUtils.class.getClassLoader().getResourceAsStream("private.key")) {
			assert inputStream != null;
			byte[] privateKey = IoUtil.readBytes(inputStream);
			KeyFactory keyfactory = KeyFactory.getInstance("RSA");
			PKCS8EncodedKeySpec encodeRule = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
			return keyfactory.generatePrivate(encodeRule);
		}
	}

}

LoginController
定义了一下登录页面,默认的登录页面需要加在国外的资源,太慢

/**
 * 登录相关的控制器
 *
 * @author qiudw
 * @date 7/11/2023
 */
@Controller
@RequestMapping
public class LoginController {

	/**
	 * 跳转到登录页面
	 *
	 * @return 登录页面的地址
	 */
	@GetMapping("/login")
	public ModelAndView loginPage() {
		return new ModelAndView("login");
	}

	/**
	 * 首页
	 *
	 * @return 首页路径
	 */
	@GetMapping({ "/", "/index" })
	public ModelAndView index() {
		return new ModelAndView("index");
	}

}

登录界面 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<form th:action="@{/login}" method="post">
    <div>
        异常信息:<span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION}"></span>
    </div>
  <input type="text" name="username" placeholder="用户名" /> <br/>
  <input type="password" name="password" placeholder="密码" /> <br/>
  <button type="submit">登录</button> <br/>
</form>
</body>
</html>

安全配置

import com.demo.oauth2.utils.SecurityUtils;
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.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
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.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.jwt.JwtDecoder;
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.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.TokenSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;

/**
 * 安全配置
 *
 * @author qiudw
 * @date 7/10/2023
 */
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

	@Bean
	@Order(1)
	public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
		OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
		http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
				// Enable OpenID Connect 1.0
				.oidc(Customizer.withDefaults());
		http.exceptionHandling()
				.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
				.and()
				.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
		return http.build();
	}

	@Bean
	@Order(2)
	public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/login").permitAll()
				.anyRequest().authenticated()
				.and()
				.formLogin()
				.loginPage("/login")
				.loginProcessingUrl("/login")
				.usernameParameter("username")
				.passwordParameter("password")
				.and()
				.csrf().disable();
		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.builder()
				.username("admin")
				.password("{noop}admin")
				.roles("ADMIN")
				.build();
		return new InMemoryUserDetailsManager(userDetails);
	}

	@Bean
	public JWKSource<SecurityContext> jwkSource() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
		RSAPublicKey publicKey = (RSAPublicKey) SecurityUtils.loadPublicKey();
		RSAPrivateKey privateKey = (RSAPrivateKey) SecurityUtils.loadPrivateKey();
		RSAKey rsaKey = new RSAKey.Builder(publicKey)
				.privateKey(privateKey)
				.keyID("key-id")
				.build();
		JWKSet jwkSet = new JWKSet(rsaKey);
		return new ImmutableJWKSet<>(jwkSet);
	}

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

	@Bean
	public AuthorizationServerSettings authorizationServerSettings() {
		return AuthorizationServerSettings.builder().build();
	}

	@Bean
	public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
		RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
				.clientId("demo-client")
				.clientIdIssuedAt(Instant.now())
				.clientSecret("{noop}demo-secret")
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
				.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)
				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
				.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
				.authorizationGrantType(AuthorizationGrantType.PASSWORD)
				.redirectUri("https://baidu.com")
				.redirectUri("http://127.0.0.1:7100/login/oauth2/code/messaging-client-oidc")
				.redirectUri("http://127.0.0.1:7100/login/oauth2/code/demo-client-name")
				.tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(1L)).build())
				.scope(OidcScopes.OPENID)
				.scope(OidcScopes.PROFILE)
				.scope(OidcScopes.EMAIL)
				.scope(OidcScopes.ADDRESS)
				.scope(OidcScopes.PHONE)
				.scope("client.create")
				.scope("client.read")
				// 不需要跳转到授权页面
//				.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
				.build();
		return new InMemoryRegisteredClientRepository(registeredClient);
	}

}

这里客户端、用户都是保存在内存里面的,为了方便演示。

# client_id: demo-client
# client_secret: demo-secret
# base64: ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=
# 在线base64: https://c.runoob.com/front-end/693/

### 查看oauth配置
GET {{baseUrl}}/.well-known/oauth-authorization-server

### 查看OpenID的配置
GET {{baseUrl}}/.well-known/openid-configuration

### jwks
GET {{baseUrl}}/oauth2/jwks

### 浏览器模式 - 浏览器
http://127.0.0.1:7000/oauth2/authorize?response_type=code&client_id=demo-client&redirect_uri=https://baidu.com&scope=openid client.read client.create

### 浏览器模式(方式一) - 授权码换取token
POST {{baseUrl}}/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=

grant_type=authorization_code&redirect_uri=https://baidu.com&code=OHd9nQj4ykR3KqGSFk14dbCRx7ifO4Vu2R_SO5CEKvqFjd3FkKJRkfpW5IuLmj0gg8PXFc3oShRtTYqAs_Tzk9SaBnwGUFqvnqmFSljuawZKwjlVcmXflGRF0PJs3Q7B

### 浏览器模式(方式二) - 授权码换取token
POST {{baseUrl}}/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&redirect_uri=https://baidu.com&client_id=demo-client&client_secret=demo-secret&code=XmdmrulYday-0sxw0A1_5VBrWekozvFMzLECyG6rBV7G348Py453YOguQ5VKOilD4q2ihlEL_7_2fuKuatl5HaKf9YyTjr_3yHiiRlyGmsFO3Lf-rKixY2FoZ5rQnPSS

### 获取用户
GET {{baseUrl}}/userinfo
Authorization: Bearer {{accessToken}}

### 客户端模式
POST {{baseUrl}}/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=

grant_type=client_credentials&scope=openid

### 注销令牌
POST {{baseUrl}}/oauth2/revoke
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVtby1jbGllbnQ6ZGVtby1zZWNyZXQ=

token_type_hint=refresh_token&token=klA-i51s2hVbankpkg9kRh9jk1EIFJnwHhdAYHbS7LoW9YBwMRvqSShOL_8h_LgunSytWh-08JveyLedHfAUD8ovrTjled6i7HYIgwKQUSP18zUTCThs-HV8AeboTtfX

idea里面可以直接发送请求
在这里插入图片描述

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

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

相关文章

用技术指标伦敦金行情走势图

经常有投资者说&#xff0c;伦敦金行情走势图老是涨跌涨跌&#xff0c;抓不准它涨跌的规律&#xff0c;老是被它弄得头昏脑胀。其实看伦敦金行情走势图的方法有很多&#xff0c;最直接的就是使用技术指标。技术指标本来就是投资者为了避免伦敦金行情走势图上价格干扰性波动&…

玩转AI二维码:分享我的漂亮二维码生成秘诀

这几天我又生成了很多漂亮的二维码图片&#xff0c;有了一些感受和想法&#xff0c;特总结此文&#xff0c;分享给大家 先看效果&#xff0c;喜欢的可以继续读下去&#xff08;遵守平台规则&#xff0c;图片已阉割&#xff0c;需要更多图片参数的同学可直接看文章最后&#xf…

Leap AI + Python 开发绘图应用

使用python语言&#xff0c;并借助Leap AI网站的api key&#xff0c;可以轻松实现AI绘图功能。使用时&#xff0c;用户只要输入prompt提示词&#xff0c;几秒钟之内服务器就能生成图片并返回图片的链接地址。开发人员可以利用这个功能开发个性化的绘图软件&#xff0c;或者整合…

后端Linux软件安装大全[JDK、Tomcat、MySQL、Irzsz...持续更新中]

文章目录 前言1.软件安装方式2.安装jdk3.安装Tomcat4.安装MySQL5.安装lrzsz 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚&…

Webkit浏览器内核探究——Webkit简介

文章目录 1、什么是Webkit2、Webkit做了什么3、Webkit组成4、应用程序如何利用Webkit的 1、什么是Webkit Wekbit是一个开源的Web浏览器引擎&#xff0c;也就是浏览器的内核。 Apple的Safari, Google的Chrome, Nokia S60平台的默认浏览器&#xff0c;Apple手机的默认浏览器&…

如何监测电路中恶性负载

随着社会的发展和科技的进步&#xff0c;人们对于用电的安全性和稳定性要求越来越高。电路中的恶性负载往往会导致电路故障&#xff0c;甚至引发火灾等严重事故。因此&#xff0c;如何监测电路中的恶性负载成为了一个重要的课题。本文将从恶性负载的定义、监测方法、防范措施等…

【新版系统架构】第十八章-安全架构设计理论与实践

信息安全体系架构设计 信息系统安全设计重点考虑&#xff1a;系统安全保障体系&#xff0c;信息安全体系架构 系统安全保障体系&#xff1a; 安全区域策略的确定&#xff0c;根据安全区域的划分&#xff0c;主管部门应制定针对性的安全策略统一配置和管理防病毒系统&#xff…

【实验一】java基础

1、每个非素数都可以唯一地被分解为若干素数地乘积&#xff0c;请编程对其进行验证。 个人答案&#xff1a; package 实验1;import java.util.Scanner;public class sushu {public static Scanner input new Scanner(System.in);public static void main(String[] args) {Sy…

【工具使用】VS Code 某些文件夹为灰色全局搜索搜不到该文件夹的内容

一&#xff0c;简介 本文主要介绍在VS Code使用过程中&#xff0c;出现文件夹为灰色&#xff0c;且搜索内容时灰色文件夹中的字符串检索不到问题的原因和解决方法&#xff0c;从而提高工作效率。供参考。 二&#xff0c;问题现象 VS Code中部分文件夹显示为灰色&#xff0c;…

找工作第三弹——JS面试题由浅入深

目录 前言理论篇1.js的单线程2.js的数据类型3.js的内存泄漏4.js的宏任务与微任务&#xff08;事件循环机制&#xff09;5.js的作用域与作用域链6.js的闭包7.ES6新增8.原型和原型链8.深浅拷贝9.js的防抖和节流10.js中的this指向11.改变this指向的call、apply、bind12.事件流13.D…

Git 命令提交和分支控制

强大的分支和合并&#xff1a;Git 提供了强大的分支功能&#xff0c;使得开发者可以轻松创建、合并和管理分支。这种灵活性使得团队可以同时进行多个任务和实验性开发&#xff0c;而不会相互干扰 Git 在处理大型代码仓库和版本历史时表现出色。它使用了一种称为“快照”的机制…

数据处理轻松搞定:如何利用PaddleNLP高效处理大规模文本数据

目录 前言一、paddleNLP介绍、特性1-1、介绍1-2、特性介绍 二、paddleNLP安装三、PaddleNLP一键使用3-1、中文分词3-2、词性标注3-3、命名实体识别3-4、依存句法分析&#xff08;DDParser&#xff09;3-5、解语知识标注3-6、文本纠错&#xff08;ERNIE-CSC&#xff09;3-7、文本…

工业园区扬尘噪音监测,该怎么操作?这篇告诉你!

随着工业园区的不断发展和扩大&#xff0c;扬尘和噪声问题成为引起关注的重要环境挑战。工业活动产生的扬尘和噪声不仅对周边居民的生活质量和健康构成威胁&#xff0c;也对环境可持续性产生负面影响。 因此&#xff0c;进行有效的扬尘和噪声监测成为管理者和决策者必须关注和解…

【打包问题】执行mvn spotless:apply之后,import的包被去移除了

import的包被去移除了 出现的问题mvn spotless:apply的作用原因在哪解决方式快速恢复代码的规范性 出现的问题 执行mvn spotless:apply之后 mvn spotless:apply的作用 简单来说这个就是执行代码的规范化 原因在哪 我为了展示效果所以把下满应该引入的包注释了出来&#xf…

CDN应用的三大技术及五大优点

CDN应用的三大技术 CDN的实现需要依赖多种网络技术&#xff0c;如负载均衡技术、动态内容分发与复制技术、缓存技术等&#xff0c;以下为您做简要介绍&#xff1a; 1、负载均衡技术 负载均衡技术就是将网络中的流量尽可能平均分配到多个能完成相同任务的服务器或网络节点&am…

奇葩功能实现:级联选择框组件el-cascader实现同一级的二级只能单选,但是一级可以多选

前言&#xff1a; 其实也不能说这个功能奇葩&#xff0c;做项目碰到这种需求也算合理正常&#xff0c;只是确实没有能直接实现这一需求的现成组件。 el-cascader作为级联选择组件&#xff0c;并不能同时支持一级多选&#xff0c;二级单选的功能&#xff0c;只能要么是单选或者…

从零开始构建一个电影知识图谱,实现KBQA智能问答[下篇]:Apache jena SPARQL endpoint及推理、KBQA问答Demo超详细教学

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

Pyhon学习之条件判断和循环语句

1.if flag 1 if 1:print(type(True)) if 0:print(name) if flag:print("flag的类型&#xff1a;"str(type(flag)))根据这个结果&#xff0c;可以看出来&#xff0c;这里没有进行类型转换&#xff0c;bool就是int true 就是1 false 就是0 flag 0 if 1:print(type(…

Fiddler抓取app HTTPS请求

一、电脑和手机连接同一WIFI cmd->ipconfig&#xff0c;查看电脑当前IP地址为192.168.101.48 二、配置Fiddler Options选项勾选Allow remote computers to connect。 安装证书 勾选抓取HTTPS请求 三、手机端配置代理 手机端连接wifi&#xff0c;手动配置代理。 主机名&…

uniapp下上传图片后图片裁剪加图片旋转,支持H5和app

效果图 代码如下 <template><view class"container" v-show"isShow"><view><view class"cropper-content"><view v-if"isShowImg" class"uni-corpper":style"width: cropperInitW px;he…