热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

idea基于jwt搭建简单的认证服务器和资源服务器

在前面的文章,资源服务器的认证是每次都要去请求认证服务器,本次文章会对此点进行改进,资源服务器内部基于算法,无需多次请求认证服务器  需要eureka注册中心,认证服务器,资源服务

在前面的文章,资源服务器的认证是每次都要去请求认证服务器,本次文章会对此点进行改进,资源服务器内部基于算法,无需多次请求认证服务器

 

 需要eureka注册中心,认证服务器,资源服务器

pom文件


4.0.0
com.qiuxie
qiuxie-parent

pom
1.0-SNAPSHOT

one-oauth-service
eureka-service
one-login-service
jwt-oauth-service
jwt-login-service



org.springframework.boot
spring-boot-starter-parent
2.3.12.RELEASE


2.1
2.1.18.RELEASE






org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR12
pom
import



org.springframework.boot
spring-boot-starter-web
2.0.1.RELEASE



org.projectlombok
lombok
1.18.4



org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
2.2.9.RELEASE



org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2.2.9.RELEASE



org.springframework.security.oauth
spring-security-oauth2
2.3.4.RELEASE


org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.11.RELEASE







org.apache.maven.plugins
maven-compiler-plugin

1.8
1.8
utf-8




org.springframework.boot
spring-boot-maven-plugin




  eureka注册中心



qiuxie-parent
com.qiuxie
1.0-SNAPSHOT

4.0.0
com.eureka
eureka-service


org.springframework.cloud
spring-cloud-starter-netflix-eureka-server



  

server.port=8761
spring.application.name=eureka-service
eureka.instance.hostname=localhost
eureka.client.service-url.defaultZOne=http://localhost:8761/eureka
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

  

package com.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author yourheart
* @Description
* @create 2022-02-14 20:33
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
} 

认证服务器



qiuxie-parent
com.qiuxie
1.0-SNAPSHOT

4.0.0
jwt.oauth
jwt-oauth-service



org.springframework.boot
spring-boot-starter-web


org.springframework.boot
spring-boot-starter-tomcat





org.springframework.boot
spring-boot-starter-undertow



org.springframework.cloud
spring-cloud-starter-oauth2


org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure




org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure



org.springframework.security.oauth
spring-security-oauth2



org.springframework.cloud
spring-cloud-starter-netflix-eureka-client



  

server.port=2002
spring.application.name=jwt-oauth
#注册到eureka注册中心,如果是注册到集群就用逗号连接多个,单实例写上一个就好
eureka.client.service-url.defaultZOne=http://localhost:8761/eureka
logging.level.jwt.oauth=debug
logging.level.web=debug
spring.devtools.add-properties=false

  

获取token
http://localhost:2002/oauth/token?client_secret=13301455191qiuxieM&grant_type=password&
username=admin&password=13301455191qiuxieM&client_id=client_qiuxie
校验toekn
http://localhost:2002/oauth/check_token?token=
刷新token
http://localhost:2002/oauth/token?grant_type=refresh_token&client_id=client_qiuxie&client_secret=13301455191qiuxieM&refresh_token=8ca8f970-3815-44e2-baee-5f4f41ec607a

  

package jwt.oauth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author yourheart
* @Description
* @create 2022-03-01 19:51
*/
@SpringBootApplication
@EnableDiscoveryClient
public class JwtOauthApplication {
public static void main(String[] args) {
SpringApplication.run(JwtOauthApplication.class,args);
}
}

  

package jwt.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* @author yourheart
* @Description
* @create 2022-02-15 20:05
*/
@Configuration
@EnableAuthorizationServer //开启认证服务器功能
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
security.allowFormAuthenticationForClients()
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
/**
* 客户端详情配置
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
clients.inMemory()
.withClient("client_qiuxie")
.secret("13301455191qiuxieM")
.resourceIds("loginId")
.authorizedGrantTypes("password","refresh_token")
.scopes("all");
}
/**
* 配置token令牌相关
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
endpoints.tokenStore(tokenStore())
.tokenServices(authorizationServerTokenServices())
.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.GET);
}
/**
* 描述token信息
* @return
*/
public AuthorizationServerTokenServices authorizationServerTokenServices(){
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore());
/**
* 添加jwt令牌
*/
tokenServices.setTokenEnhancer(jwtAccessTokenConverter());
tokenServices.setAccessTokenValiditySeconds(120);//令牌有效时间30s
tokenServices.setRefreshTokenValiditySeconds(259200);//刷新令牌有效时间3天
return tokenServices;
}
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
private String sign_key="qiuxie1992";
/**
* 返回jwt令牌装换器(生成jwt令牌)
* @return
*/
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter cOnverter= new JwtAccessTokenConverter();
/**
* 签名秘钥
*/
converter.setSigningKey(sign_key);
/**
* 验证使用的秘钥
*/
converter.setVerifier(new MacSigner(sign_key));
return converter;
}
}

  

package jwt.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.ArrayList;
/**
* @author yourheart
* @Description
* @create 2022-02-21 20:20
*/
@Configuration
public class SecurityConfiger extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 注册认证管理器到容器
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 密码编码器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}

/**
* 处理用户名和密码
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetails userDetails=new User("admin","13301455191qiuxieM",new ArrayList<>());
auth.inMemoryAuthentication()
.withUser(userDetails).passwordEncoder(passwordEncoder);
}
}

  资源服务器,不用重复调用认证服务器



qiuxie-parent
com.qiuxie
1.0-SNAPSHOT

4.0.0
jwt.login
jwt-login-service



org.springframework.cloud
spring-cloud-starter-oauth2


org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure




org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure



org.springframework.security.oauth
spring-security-oauth2



org.springframework.cloud
spring-cloud-starter-netflix-eureka-client



org.springframework.boot
spring-boot-starter-web



org.projectlombok
lombok



  

server.port=2003
spring.application.name=jwt-login
#注册到eureka注册中心,如果是注册到集群就用逗号连接多个,单实例写上一个就好
eureka.client.service-url.defaultZOne=http://localhost:8761/eureka
logging.level.jwt.login=debug
logging.level.web=debug
spring.devtools.add-properties=false
resourceId=loginId
signKey=qiuxie1992

  

package jwt.login;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @author yourheart
* @Description
* @create 2022-02-28 20:14
*/
@Controller
@CrossOrigin
@RequestMapping("/home")
public class HomeController {
@RequestMapping("/index")
@ResponseBody
public String indexs(Model model, HttpSession session, HttpServletRequest request) {
return "进入主界面";
}
}

  

package jwt.login;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author yourheart
* @Description
* @create 2022-03-01 21:06
*/
@SpringBootApplication
@EnableDiscoveryClient
public class JwtLoginApplication {
public static void main(String[] args) {
SpringApplication.run(JwtLoginApplication.class,args);
}
}

  

/**
* Project Name:tec
* File Name:LoginAuthController.java
* Package Name:com.java.controller.front
* Date:下午9:27:26
* Copyright (c) 2020, bluemobi All Rights Reserved.
*
*/
package jwt.login;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* Description:

* Date: 下午9:27:26

*
* @author 喵星人
* @version
* @see
*/
@Controller
@RequestMapping("/loginauth")
@Slf4j
public class LoginAuthController {

// 使用账号和密码进行登录
@PostMapping(value = "/login")
@ResponseBody
public Map doLogin(@RequestBody User user) {
Map resultMap=new HashMap<>();
if ("qiuxie".equals(user.getUserName())&&"123".equals(user.getPassWord())){
resultMap.put("code","100");
resultMap.put("msg","用户名和密码正确");
}else {
resultMap.put("code","-100");
resultMap.put("msg","用户名和密码错误,登录失败");
}
return resultMap;
}
}

 

/**
* Project Name:springboot
* File Name:LoginController.java
* Package Name:com.java.controller.front
* Date:下午5:22:59
* Copyright (c) 2019, bluemobi All Rights Reserved.
*
*/
package jwt.login;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* Description:

* Date: 下午5:22:59

*
* @author 邱燮
* @version
* @see
*/
@Controller
@RequestMapping("/re")
public class LoginController {
// 跳转注册页面
@RequestMapping("/toRe")
@ResponseBody
public String toRe(HttpServletRequest request) {
return "进入注册界面";
}
}

  

package jwt.login;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* @author yourheart
* @Description
* @create 2022-02-24 20:11
*/
@Configuration
@EnableResourceServer
@EnableWebSecurity
public class ResourceServerConfiger extends ResourceServerConfigurerAdapter {
@Value("${resourceId}")
private String resourceId;
@Value("${signKey}")
private String signKey;
/**
* 进行token校验
* @param resources
* @throws Exception
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//jwt令牌
resources.resourceId(resourceId).tokenStore(tokenStore()).stateless(true);//无状态设置
}
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 返回jwt令牌转换器
* @return
*/
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter cOnverter= new JwtAccessTokenConverter();
/**
* 签名秘钥
*/
converter.setSigningKey(signKey);
converter.setVerifier(new MacSigner(signKey));
return converter;
}
/**
* 针对api接口进行认证或是不认证
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests()
.antMatchers("/home/**").authenticated() //这里面的请求都是需要认证的
.anyRequest().permitAll(); //其他的请求不认证
}
}

 

/**
* Project Name:tec
* File Name:User.java
* Package Name:com.java.bean
* Date:下午2:55:06
* Copyright (c) 2020, bluemobi All Rights Reserved.
*
*/
package jwt.login;
import lombok.Data;
import java.io.Serializable;
/**
* Description:

* Date: 下午2:55:06

*
* @author 喵星人
* @version
* @see
*/
@Data
public class User implements Serializable {
private Integer id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String passWord;
/**
* 创建时间
*/
private String newTime;
/**
* 修改时间
*/
private String updateTime;
/**
* 邮件
*/
private String email;
/**
* 校验码
*/
private String checkCode;
/**
* 万能密码
*/
private String universalPassword;
/**
* 昵称
*/
private String nickname;
}

  使用postman调用

获取token

http://localhost:2002/oauth/token?client_secret=13301455191qiuxieM&grant_type=password&username=admin&password=13301455191qiuxieM&client_id=client_qiuxie

  

 

 

 校验token

http://localhost:2002/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibG9naW5JZCJdLCJleHAiOjE2NTQ3OTEyMjEsInVzZXJfbmFtZSI6ImFkbWluIiwianRpIjoiYjYyZDVkM2MtNzlmMC00YTk1LTk4OTItYTg1NGZhMDBlZmEwIiwiY2xpZW50X2lkIjoiY2xpZW50X3FpdXhpZSIsInNjb3BlIjpbImFsbCJdfQ.U2LEt_XITsaOAyjeJoHpn82t44THByndPI8lSyc7oIY

  

 

 使用刷新token获取新的token

http://localhost:2002/oauth/token?grant_type=refresh_token&client_id=client_qiuxie&client_secret=13301455191qiuxieM&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibG9naW5JZCJdLCJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJiNjJkNWQzYy03OWYwLTRhOTUtOTg5Mi1hODU0ZmEwMGVmYTAiLCJleHAiOjE2NTUwNTAzMDEsImp0aSI6ImE5NmQ2OWI4LWRlMGItNDQ1ZS1iODdlLTNmOTM0ODg0MmM0NiIsImNsaWVudF9pZCI6ImNsaWVudF9xaXV4aWUifQ.G7WxC4-Y11UKHHN723vokjGYIxfA7pP6_mcxj4mSZfU

  

 

 

接口调用

 

 

 

 带认证的记得在请求头中加入授权码

 

 

 

 



推荐阅读
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • centos安装Mysql的方法及步骤详解
    本文介绍了centos安装Mysql的两种方式:rpm方式和绿色方式安装,详细介绍了安装所需的软件包以及安装过程中的注意事项,包括检查是否安装成功的方法。通过本文,读者可以了解到在centos系统上如何正确安装Mysql。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • Tomcat安装与配置教程及常见问题解决方法
    本文介绍了Tomcat的安装与配置教程,包括jdk版本的选择、域名解析、war文件的部署和访问、常见问题的解决方法等。其中涉及到的问题包括403问题、数据库连接问题、1130错误、2003错误、Java Runtime版本不兼容问题以及502错误等。最后还提到了项目的前后端连接代码的配置。通过本文的指导,读者可以顺利完成Tomcat的安装与配置,并解决常见的问题。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文详细介绍了cisco路由器IOS损坏时的恢复方法,包括进入ROMMON模式、设置IP地址、子网掩码、默认网关以及使用TFTP服务器传输IOS文件的步骤。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • 微软评估和规划(MAP)的工具包介绍及应用实验手册
    本文介绍了微软评估和规划(MAP)的工具包,该工具包是一个无代理工具,旨在简化和精简通过网络范围内的自动发现和评估IT基础设施在多个方案规划进程。工具包支持库存和使用用于SQL Server和Windows Server迁移评估,以及评估服务器的信息最广泛使用微软的技术。此外,工具包还提供了服务器虚拟化方案,以帮助识别未被充分利用的资源和硬件需要成功巩固服务器使用微软的Hyper - V技术规格。 ... [详细]
author-avatar
我爱你800916
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有