栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

oauth2-authorization-server;oauth2-resource-server;oauth2-client

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

oauth2-authorization-server;oauth2-resource-server;oauth2-client

spring-security-oauth2-authorization-server
  1. 依赖项
    
    
        
            org.springframework.boot
            spring-boot-starter-parent
            2.5.12
            
        
        4.0.0
    
        test
        default-authorizationserver
    
        
            
                org.springframework.boot
                spring-boot-starter-web
            
            
                org.springframework.boot
                spring-boot-starter-security
            
            
                org.springframework.boot
                spring-boot-starter-jdbc
            
            
                org.springframework.security
                spring-security-oauth2-authorization-server
                0.2.3
            
            
                com.h2database
                h2
                2.1.212
            
        
    
  2. 核心代码
    package sample.config;
    
    import org.springframework.context.annotation.Bean;
    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.password.PasswordEncoder;
    import org.springframework.security.provisioning.InMemoryUserDetailsManager;
    import org.springframework.security.web.SecurityFilterChain;
    
    
    @EnableWebSecurity
    public class DefaultSecurityConfig {
    
        @Bean
        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests(authorizeRequests ->
                            authorizeRequests.anyRequest().authenticated()
                    )
                    .formLogin(Customizer.withDefaults());
            return http.build();
        }
    
        @Bean
        UserDetailsService users() {
            UserDetails user = User.withUsername("user")
                    .password(passwordEncoder().encode("password"))
                    .roles("USER")
                    .build();
            return new InMemoryUserDetailsManager(user);
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
    }
    
    package sample.config;
    
    import com.nimbusds.jose.jwk.JWKSet;
    import com.nimbusds.jose.jwk.RSAKey;
    import com.nimbusds.jose.jwk.source.JWKSource;
    import com.nimbusds.jose.proc.SecurityContext;
    import org.springframework.beans.factory.annotation.Autowired;
    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.jdbc.datasource.embedded.EmbeddedDatabase;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
    import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
    import org.springframework.security.config.Customizer;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.NoOpPasswordEncoder;
    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.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.ClientSettings;
    import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
    import org.springframework.security.web.SecurityFilterChain;
    import sample.jose.Jwks;
    
    import java.util.UUID;
    
    
    @Configuration(proxyBeanMethods = false)
    public class AuthorizationServerConfig {
    
    //自定义用户授权页面
    	
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public SecurityFilterChain authenticationServerSecurityFilterChain(HttpSecurity http) throws Exception {
            OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
            return http.formLogin(Customizer.withDefaults()).build();
        }
    
        
        @Bean
        public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
            RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                    .clientId("messaging-client")
                    .clientSecret(passwordEncoder.encode("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/messaging-client-oidc")
                    .redirectUri("http://127.0.0.1:8080/authorized")
                    .scope(OidcScopes.OPENID)
                    .scope("message.read")
                    .scope("message.write")
                    .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                    .build();
            JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
            registeredClientRepository.save(registeredClient);
            return registeredClientRepository;
        }
    
    
        @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 JWKSource jwkSource() {
            RSAKey rsaKey = Jwks.generateRsa();
            JWKSet jwkSet = new JWKSet(rsaKey);
            return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
        }
    
        public static void main(String[] args) {
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            System.err.println(passwordEncoder.encode("secret"));
        }
    
        @Bean
        public ProviderSettings providerSettings() {
            return ProviderSettings.builder().issuer("http://localhost:9000").build();
        }
    
        @Bean
        public EmbeddedDatabase embeddedDatabase() {
            return new EmbeddedDatabaseBuilder()
                    .generateUniqueName(true)
                    .setType(EmbeddedDatabaseType.H2)
                    .setScriptEncoding("UTF-8")
                    .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
                    .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
                    .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
                    .build();
        }
    }
    
    package sample.jose;
    
    import com.nimbusds.jose.jwk.Curve;
    import com.nimbusds.jose.jwk.ECKey;
    import com.nimbusds.jose.jwk.OctetSequenceKey;
    import com.nimbusds.jose.jwk.RSAKey;
    
    import javax.crypto.SecretKey;
    import java.security.KeyPair;
    import java.security.interfaces.RSAPublicKey;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.ECPublicKey;
    import java.security.interfaces.ECPrivateKey;
    import java.util.UUID;
    
    
    public final class Jwks {
    
        private Jwks() {
    
        }
    
        public static RSAKey generateRsa() {
            KeyPair keyPair = KeyGeneratorUtils.generateRsaKey();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            return new RSAKey.Builder(publicKey)
                    .privateKey(privateKey)
                    .keyID(UUID.randomUUID().toString())
                    .build();
        }
    
        public static ECKey generateEc() {
            KeyPair keyPair = KeyGeneratorUtils.generateEcKey();
            ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
            ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
            Curve curve = Curve.forECParameterSpec(publicKey.getParams());
            return new com.nimbusds.jose.jwk.ECKey.Builder(curve, publicKey)
                    .privateKey(privateKey)
                    .keyID(UUID.randomUUID().toString())
                    .build();
        }
    
        public static OctetSequenceKey generateSecret() {
            SecretKey secretKey = KeyGeneratorUtils.generateSecretKey();
            return new OctetSequenceKey.Builder(secretKey)
                    .keyID(UUID.randomUUID().toString())
                    .build();
        }
    }
    
    package sample.jose;
    
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import java.math.BigInteger;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.spec.ECFieldFp;
    import java.security.spec.ECParameterSpec;
    import java.security.spec.ECPoint;
    import java.security.spec.EllipticCurve;
    
    
    final class KeyGeneratorUtils {
    
        private KeyGeneratorUtils() {
    
        }
    
        static SecretKey generateSecretKey() {
            SecretKey hmacKey;
            try {
                hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey();
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
            return hmacKey;
        }
    
        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;
        }
    
        static KeyPair generateEcKey() {
            EllipticCurve ellipticCurve = new EllipticCurve(
                    new ECFieldFp(
                            new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")),
                    new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
                    new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"));
            ECPoint ecPoint = new ECPoint(
                    new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
                    new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"));
            ECParameterSpec ecParameterSpec = new ECParameterSpec(
                    ellipticCurve,
                    ecPoint,
                    new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"),
                    1);
            KeyPair keyPair;
            try {
                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
                keyPairGenerator.initialize(ecParameterSpec);
                keyPair = keyPairGenerator.generateKeyPair();
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
            return keyPair;
        }
    
    }
    
  3. 配置文件
    server:
      port: 9000
    
    logging:
      level:
        root: INFO
        org.springframework.web: INFO
        org.springframework.security: INFO
        org.springframework.security.oauth2: INFO
  4. sql
CREATE TABLE oauth2_registered_client (
    id varchar(100) NOT NULL,
    client_id varchar(100) NOT NULL,
    client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
    client_secret varchar(200) DEFAULT NULL,
    client_secret_expires_at timestamp DEFAULT NULL,
    client_name varchar(200) NOT NULL,
    client_authentication_methods varchar(1000) NOT NULL,
    authorization_grant_types varchar(1000) NOT NULL,
    redirect_uris varchar(1000) DEFAULT NULL,
    scopes varchar(1000) NOT NULL,
    client_settings varchar(2000) NOT NULL,
    token_settings varchar(2000) NOT NULL,
    PRIMARY KEY (id)
);
CREATE TABLE oauth2_authorization_consent (
    registered_client_id varchar(100) NOT NULL,
    principal_name varchar(200) NOT NULL,
    authorities varchar(1000) NOT NULL,
    PRIMARY KEY (registered_client_id, principal_name)
);
CREATE TABLE oauth2_authorization (
    id varchar(100) NOT NULL,
    registered_client_id varchar(100) NOT NULL,
    principal_name varchar(200) NOT NULL,
    authorization_grant_type varchar(100) NOT NULL,
    attributes blob DEFAULT NULL,
    state varchar(500) DEFAULT NULL,
    authorization_code_value blob DEFAULT NULL,
    authorization_code_issued_at timestamp DEFAULT NULL,
    authorization_code_expires_at timestamp DEFAULT NULL,
    authorization_code_metadata blob DEFAULT NULL,
    access_token_value blob DEFAULT NULL,
    access_token_issued_at timestamp DEFAULT NULL,
    access_token_expires_at timestamp DEFAULT NULL,
    access_token_metadata blob DEFAULT NULL,
    access_token_type varchar(100) DEFAULT NULL,
    access_token_scopes varchar(1000) DEFAULT NULL,
    oidc_id_token_value blob DEFAULT NULL,
    oidc_id_token_issued_at timestamp DEFAULT NULL,
    oidc_id_token_expires_at timestamp DEFAULT NULL,
    oidc_id_token_metadata blob DEFAULT NULL,
    refresh_token_value blob DEFAULT NULL,
    refresh_token_issued_at timestamp DEFAULT NULL,
    refresh_token_expires_at timestamp DEFAULT NULL,
    refresh_token_metadata blob DEFAULT NULL,
    PRIMARY KEY (id)
);
oauth2-resource-server的
  1. 依赖项
    
    
        
            org.springframework.boot
            spring-boot-starter-parent
            2.5.12
            
        
        4.0.0
    
        test.oauth
        com.oauth2.resource
    
        
            
                org.springframework.boot
                spring-boot-starter-web
            
            
                org.springframework.boot
                spring-boot-starter-oauth2-resource-server
            
            
                org.springframework.boot
                spring-boot-starter-security
            
            
                org.springframework.boot
                spring-boot-starter-oauth2-resource-server
            
        
    
    
  2. 核心代码
    package sample.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.web.SecurityFilterChain;
    
    @EnableWebSecurity
    public class ResourceServerConfig {
        @Bean
        SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http.mvcMatcher("/messages
    package sample.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class MessagesController {
    
        @GetMapping("/messages")
        public String[] getMessages() {
            return new String[] {"Message 1", "Message 2", "Message 3"};
        }
    }
    
  3. 配置文件
server:
  port: 8090

logging:
  level:
    root: INFO
    org.springframework.web: INFO
    org.springframework.security: INFO
    org.springframework.security.oauth2: INFO
#    org.springframework.boot.autoconfigure: DEBUG

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:9000
oauth2-client
  1. 依赖项
    
    
        
            org.springframework.boot
            spring-boot-starter-parent
            2.5.12
            
        
        4.0.0
    
        test.oauth
        com.oauth2.client
    
        
            
                org.springframework.boot
                spring-boot-starter-web
            
            
                org.springframework.boot
                spring-boot-starter-security
            
            
                org.springframework.boot
                spring-boot-starter-oauth2-client
            
            
                org.springframework
                spring-webflux
                5.2.21.RELEASE
            
            
                io.projectreactor.netty
                reactor-netty
                1.0.18
            
        
    
  2. 核心代码
    package sample.config;
    
    import org.springframework.context.annotation.Bean;
    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.configuration.WebSecurityCustomizer;
    import org.springframework.security.web.SecurityFilterChain;
    
    import static org.springframework.security.config.Customizer.withDefaults;
    
    
    @EnableWebSecurity
    public class SecurityConfig {
    
        
        @Bean
        WebSecurityCustomizer webSecurityCustomizer() {
            return (web) -> web.ignoring().antMatchers("/webjars
        @Bean
        SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests(authorizeRequests ->
                            authorizeRequests.anyRequest().authenticated()
                    )
                    .oauth2Login(oauth2Login ->
                            oauth2Login.loginPage("/oauth2/authorization/messaging-client-oidc"))
                    .oauth2Client(withDefaults());
            return http.build();
        }
    }
    
    package sample.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
    import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
    import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
    import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
    import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
    import org.springframework.web.reactive.function.client.WebClient;
    
    
    @Configuration
    public class WebClientConfig {
    
    	@Bean
    	WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
    		ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
    				new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    		return WebClient.builder()
    				.apply(oauth2Client.oauth2Configuration())
    				.build();
    	}
    
    	@Bean
    	OAuth2AuthorizedClientManager authorizedClientManager(
    			ClientRegistrationRepository clientRegistrationRepository,
    			OAuth2AuthorizedClientRepository authorizedClientRepository) {
    
    		OAuth2AuthorizedClientProvider authorizedClientProvider =
    				OAuth2AuthorizedClientProviderBuilder.builder()
    						.authorizationCode()
    						.refreshToken()
    						.clientCredentials()
    						.build();
    		DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
    				clientRegistrationRepository, authorizedClientRepository);
    		authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
    
    		return authorizedClientManager;
    	}
    }
    
    package sample.web;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
    import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
    import org.springframework.security.oauth2.core.OAuth2AccessToken;
    import org.springframework.security.oauth2.core.OAuth2Error;
    import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.reactive.function.client.WebClient;
    
    import javax.servlet.http.HttpServletRequest;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
    import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
    
    
    @RestController
    public class AuthorizationController {
        private final WebClient webClient;
        private final String messagesBaseUri;
    
        public AuthorizationController(WebClient webClient,
                                       @Value("${messages.base-uri}") String messagesBaseUri) {
            this.webClient = webClient;
            this.messagesBaseUri = messagesBaseUri;
        }
    
        @GetMapping(value = "/authorize", params = "grant_type=authorization_code")
        public List authorizationCodeGrant(
                @RegisteredOAuth2AuthorizedClient("messaging-client-authorization-code")
                        OAuth2AuthorizedClient authorizedClient) {
    
            String[] messages = this.webClient
                    .get()
                    .uri(this.messagesBaseUri)
                    .attributes(oauth2AuthorizedClient(authorizedClient))
                    .retrieve()
                    .bodyToMono(String[].class)
                    .block();
    
            return messages != null ? Arrays.asList(messages) : Collections.singletonList("没数据");
        }
    
        @GetMapping(value = "/authorize", params = "grant_type=client_credentials")
        public List clientCredentialsGrant() {
    
            String[] messages = this.webClient
                    .get()
                    .uri(this.messagesBaseUri)
                    .attributes(clientRegistrationId("messaging-client-client-credentials"))
                    .retrieve()
                    .bodyToMono(String[].class)
                    .block();
            return messages != null ? Arrays.asList(messages) : Collections.singletonList("没数据");
        }
    }
    
  3. 配置文件
    server:
      port: 8080
    
    logging:
      level:
        root: INFO
        org.springframework.web: INFO
        org.springframework.security: INFO
        org.springframework.security.oauth2: INFO
    #    org.springframework.boot.autoconfigure: DEBUG
    
    spring:
      thymeleaf:
        cache: false
      security:
        oauth2:
          client:
            registration:
              messaging-client-oidc:
                provider: spring
                client-id: messaging-client
                client-secret: secret
                authorization-grant-type: authorization_code
                redirect-uri: "http://127.0.0.1:8080/login/oauth2/code/{registrationId}"
                scope: openid
                client-name: messaging-client-oidc
              messaging-client-authorization-code:
                provider: spring
                client-id: messaging-client
                client-secret: secret
                authorization-grant-type: authorization_code
                redirect-uri: "http://127.0.0.1:8080/authorized"
                scope: message.read,message.write
                client-name: messaging-client-authorization-code
              messaging-client-client-credentials:
                provider: spring
                client-id: messaging-client
                client-secret: secret
                authorization-grant-type: client_credentials
                scope: message.read,message.write
                client-name: messaging-client-client-credentials
            provider:
              spring:
                issuer-uri: http://localhost:9000
    
    messages:
      base-uri: http://127.0.0.1:8090/messages
    

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/872873.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号