• SpringBoot整合微信扫码登录


    准备工作

    在这里插入图片描述

    1.登录官网了解到,学习者想本地服务调用还是有一定的门槛,今天就带你跨门槛
    2.没有开发者资质认证
    在这里插入图片描述
    3.偷偷借用一下别人的,感谢
    微信扫码三大必要参数
    AppID :wx410c26fd9ec1a11e
    AppSecret:013a851aab426250781003108de8c275
    一同认证域名,返回数据要用:http://www.xiangxueketang.cn

    基本思路流程

    http://www.xiangxueketang.cn(1)大体的意思就是,按照微信开放平台的请求方式,把已经认证过并生成的AppID,AppSecret作为微信OAuth2.0授权的依据,请求方式如下

    // 微信开放平台授权baseUrl
            String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                    "?appid=%s" +
                    "&redirect_uri=%s" +
                    "&response_type=code" +
                    "&scope=snsapi_login" +
                    "&state=%s" +
                    "#wechat_redirect";
    
            // 回调地址
            String redirectUrl = ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL; //获取业务服务器重定向地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (2)访问授权url后会得到一个微信登录二维码,用户扫码授权点击“确认登录”后,微信服务器会向你的服务器(别人的域名)发起回调,给你一个参数code,临时票据,像下面

    http://www.xiangxueketang.cn/api/ucenter/wx/callback?code=0817of0w3h5X1Z2yU10w3q0DxI37of0m&state=atguiguwxlogin
    
    • 1

    (3)然后你根据微信服务返回的临时票据+AppID+AppSecret,按照微信开放平台的请求方式,把参数带过去,然后微信服务会返回一个带有微信用户的信息Token回来,像下面
    在这里插入图片描述(4)根据你的业务流程,用返回的信息做验证登录或其他

    搭建SpringBoot

    1.自行创建建工程,但注意端口一定要为80
    2.因为,在用户授权登录后,微信服务会重定向,但重定的地址为审核时填写的授权域名+资源路径
    3.而我们的本机ip是127.0.0,
    我们得将别人已经通过审核得域名解析到自己的主机ip上
    4.在C:\Windows\System32\drivers\etc 找到hosts文件
    5.里面添加一条信息:
    在这里插入图片描述6.保存,注意要在管理员权限下

    引入依赖

      <!--httpclient-->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.5.1</version>
            </dependency>
            <!--commons-io-->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                 <version>2.6</version>
            </dependency>
            <!--gson-->
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                 <version>2.8.2</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    加入配置文件

    # 服务端口
    server.port=80
    # 服务名
    spring.application.name=service-wxLogin
    
    #返回json的全局时间格式
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    spring.jackson.time-zone=GMT+8
    
    # 微信开放平台appid
    wx.open.app_id=wx410c26fd9ec1a11e
    # 微信开放平台 appsecret
    wx.open.app_secret=013a851aab426250781003108de8c275
    # 微信开放平台 重定向url http://服务器名称/api/ucenter/wx/callback
    wx.open.redirect_url=http://www.xiangxueketang.cn/api/ucenter/wx/callback
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    代码实现

    工具类

    @Component
    //@PropertySource("classpath:application.properties")
    public class ConstantPropertiesUtil implements InitializingBean {
    
        @Value("${wx.open.app_id}")
        private String appId;
    
        @Value("${wx.open.app_secret}")
        private String appSecret;
    
        @Value("${wx.open.redirect_url}")
        private String redirectUrl;
    
        public static String WX_OPEN_APP_ID;
        public static String WX_OPEN_APP_SECRET;
        public static String WX_OPEN_REDIRECT_URL;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            WX_OPEN_APP_ID = appId;
            WX_OPEN_APP_SECRET = appSecret;
            WX_OPEN_REDIRECT_URL = redirectUrl;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    /**
     *  依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
     * @author 
     *
     */
    public class HttpClientUtils {
    
    	public static final int connTimeout=10000;
    	public static final int readTimeout=10000;
    	public static final String charset="UTF-8";
    	private static HttpClient client = null;
    
    	static {
    		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
    		cm.setMaxTotal(128);
    		cm.setDefaultMaxPerRoute(128);
    		client = HttpClients.custom().setConnectionManager(cm).build();
    	}
    
    	public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
    		return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
    	}
    
    	public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
    		return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
    	}
    
    	public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
    			SocketTimeoutException, Exception {
    		return postForm(url, params, null, connTimeout, readTimeout);
    	}
    
    	public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
    			SocketTimeoutException, Exception {
    		return postForm(url, params, null, connTimeout, readTimeout);
    	}
    
    	public static String get(String url) throws Exception {
    		return get(url, charset, null, null);
    	}
    
    	public static String get(String url, String charset) throws Exception {
    		return get(url, charset, connTimeout, readTimeout);
    	}
    
    	/**
    	 * 发送一个 Post 请求, 使用指定的字符集编码.
    	 *
    	 * @param url
    	 * @param body RequestBody
    	 * @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
    	 * @param charset 编码
    	 * @param connTimeout 建立链接超时时间,毫秒.
    	 * @param readTimeout 响应超时时间,毫秒.
    	 * @return ResponseBody, 使用指定的字符集编码.
    	 * @throws ConnectTimeoutException 建立链接超时异常
    	 * @throws SocketTimeoutException  响应超时
    	 * @throws Exception
    	 */
    	public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
    			throws ConnectTimeoutException, SocketTimeoutException, Exception {
    		HttpClient client = null;
    		HttpPost post = new HttpPost(url);
    		String result = "";
    		try {
    			if (StringUtils.isNotBlank(body)) {
    				HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
    				post.setEntity(entity);
    			}
    			// 设置参数
    			Builder customReqConf = RequestConfig.custom();
    			if (connTimeout != null) {
    				customReqConf.setConnectTimeout(connTimeout);
    			}
    			if (readTimeout != null) {
    				customReqConf.setSocketTimeout(readTimeout);
    			}
    			post.setConfig(customReqConf.build());
    
    			HttpResponse res;
    			if (url.startsWith("https")) {
    				// 执行 Https 请求.
    				client = createSSLInsecureClient();
    				res = client.execute(post);
    			} else {
    				// 执行 Http 请求.
    				client = HttpClientUtils.client;
    				res = client.execute(post);
    			}
    			result = IOUtils.toString(res.getEntity().getContent(), charset);
    		} finally {
    			post.releaseConnection();
    			if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
    				((CloseableHttpClient) client).close();
    			}
    		}
    		return result;
    	}
    
    
    	/**
    	 * 提交form表单
    	 *
    	 * @param url
    	 * @param params
    	 * @param connTimeout
    	 * @param readTimeout
    	 * @return
    	 * @throws ConnectTimeoutException
    	 * @throws SocketTimeoutException
    	 * @throws Exception
    	 */
    	public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
    			SocketTimeoutException, Exception {
    
    		HttpClient client = null;
    		HttpPost post = new HttpPost(url);
    		try {
    			if (params != null && !params.isEmpty()) {
    				List<NameValuePair> formParams = new ArrayList<NameValuePair>();
    				Set<Entry<String, String>> entrySet = params.entrySet();
    				for (Entry<String, String> entry : entrySet) {
    					formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
    				}
    				UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
    				post.setEntity(entity);
    			}
    
    			if (headers != null && !headers.isEmpty()) {
    				for (Entry<String, String> entry : headers.entrySet()) {
    					post.addHeader(entry.getKey(), entry.getValue());
    				}
    			}
    			// 设置参数
    			Builder customReqConf = RequestConfig.custom();
    			if (connTimeout != null) {
    				customReqConf.setConnectTimeout(connTimeout);
    			}
    			if (readTimeout != null) {
    				customReqConf.setSocketTimeout(readTimeout);
    			}
    			post.setConfig(customReqConf.build());
    			HttpResponse res = null;
    			if (url.startsWith("https")) {
    				// 执行 Https 请求.
    				client = createSSLInsecureClient();
    				res = client.execute(post);
    			} else {
    				// 执行 Http 请求.
    				client = HttpClientUtils.client;
    				res = client.execute(post);
    			}
    			return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
    		} finally {
    			post.releaseConnection();
    			if (url.startsWith("https") && client != null
    					&& client instanceof CloseableHttpClient) {
    				((CloseableHttpClient) client).close();
    			}
    		}
    	}
    
    
    
    
    	/**
    	 * 发送一个 GET 请求
    	 *
    	 * @param url
    	 * @param charset
    	 * @param connTimeout  建立链接超时时间,毫秒.
    	 * @param readTimeout  响应超时时间,毫秒.
    	 * @return
    	 * @throws ConnectTimeoutException   建立链接超时
    	 * @throws SocketTimeoutException   响应超时
    	 * @throws Exception
    	 */
    	public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
    			throws ConnectTimeoutException,SocketTimeoutException, Exception {
    
    		HttpClient client = null;
    		HttpGet get = new HttpGet(url);
    		String result = "";
    		try {
    			// 设置参数
    			Builder customReqConf = RequestConfig.custom();
    			if (connTimeout != null) {
    				customReqConf.setConnectTimeout(connTimeout);
    			}
    			if (readTimeout != null) {
    				customReqConf.setSocketTimeout(readTimeout);
    			}
    			get.setConfig(customReqConf.build());
    
    			HttpResponse res = null;
    
    			if (url.startsWith("https")) {
    				// 执行 Https 请求.
    				client = createSSLInsecureClient();
    				res = client.execute(get);
    			} else {
    				// 执行 Http 请求.
    				client = HttpClientUtils.client;
    				res = client.execute(get);
    			}
    
    			result = IOUtils.toString(res.getEntity().getContent(), charset);
    		} finally {
    			get.releaseConnection();
    			if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
    				((CloseableHttpClient) client).close();
    			}
    		}
    		return result;
    	}
    
    
    	/**
    	 * 从 response 里获取 charset
    	 *
    	 * @param ressponse
    	 * @return
    	 */
    	@SuppressWarnings("unused")
    	private static String getCharsetFromResponse(HttpResponse ressponse) {
    		// Content-Type:text/html; charset=GBK
    		if (ressponse.getEntity() != null  && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
    			String contentType = ressponse.getEntity().getContentType().getValue();
    			if (contentType.contains("charset=")) {
    				return contentType.substring(contentType.indexOf("charset=") + 8);
    			}
    		}
    		return null;
    	}
    
    
    
    	/**
    	 * 创建 SSL连接
    	 * @return
    	 * @throws GeneralSecurityException
    	 */
    	private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
    		try {
    			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
    				public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
    					return true;
    				}
    			}).build();
    
    			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
    
    				@Override
    				public boolean verify(String arg0, SSLSession arg1) {
    					return true;
    				}
    
    				@Override
    				public void verify(String host, SSLSocket ssl)
    						throws IOException {
    				}
    
    				@Override
    				public void verify(String host, X509Certificate cert)
    						throws SSLException {
    				}
    
    				@Override
    				public void verify(String host, String[] cns,
    								   String[] subjectAlts) throws SSLException {
    				}
    
    			});
    
    			return HttpClients.custom().setSSLSocketFactory(sslsf).build();
    
    		} catch (GeneralSecurityException e) {
    			throw e;
    		}
    	}
    
    	public static void main(String[] args) {
    		try {
    			String str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
    			//String str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");
                /*Map map = new HashMap();
                map.put("name", "111");
                map.put("page", "222");
                String str= postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/
    			System.out.println(str);
    		} catch (ConnectTimeoutException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (SocketTimeoutException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303

    controller层

    /**
     * 

    * *

    * * @author hdt * @since 2021-03-19 */
    @Api(description="微信登录") @Controller @RequestMapping("/api/ucenter/wx") @CrossOrigin public class UcenterMemberController { @Autowired private UcenterMemberService memberService; @GetMapping("login") public String wxlogin(){ //方式1 https://open.weixin.qq.com/connect/qrconnect? // appid=APPID&redirect_uri=REDIRECT_URI& // response_type=code&scope=SCOPE&state=STATE#wechat_redirect //方式2 // 微信开放平台授权baseUrl String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" + "?appid=%s" + "&redirect_uri=%s" + "&response_type=code" + "&scope=snsapi_login" + "&state=%s" + "#wechat_redirect"; // 回调地址 String redirectUrl = ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL; //获取业务服务器重定向地址 try { redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); //url编码 } catch (UnsupportedEncodingException e) { throw new GuliException(20001, e.getMessage()); } String qrcodeUrl = String.format( baseUrl, ConstantPropertiesUtil.WX_OPEN_APP_ID, redirectUrl, "atguiguwxlogin"); return "redirect:" + qrcodeUrl; } //http://www.xiangxueketang.cn/api/ucenter/wx/callback?code=0817of0w3h5X1Z2yU10w3q0DxI37of0m&state=atguiguwxlogin @GetMapping("callback") public String callback(String code, String state){ //1获取参数code,临时票据 System.out.println("code="+code); System.out.println("state="+state); //2拿code,换取access_token、openid String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" + "?appid=%s" + "&secret=%s" + "&code=%s" + "&grant_type=authorization_code"; String accessTokenUrl = String.format(baseAccessTokenUrl, ConstantPropertiesUtil.WX_OPEN_APP_ID, ConstantPropertiesUtil.WX_OPEN_APP_SECRET, code); String result = null; try { result = HttpClientUtils.get(accessTokenUrl); System.out.println("result="+result); } catch (Exception e) { e.printStackTrace(); } //2.1解析json Gson gson = new Gson(); HashMap map = gson.fromJson(result, HashMap.class); String accessToken = (String)map.get("access_token"); String openid = (String)map.get("openid"); //3 换取用户信息 //访问微信的资源服务器,获取用户信息 String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" + "?access_token=%s" + "&openid=%s"; String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid); String resultUserInfo = null; try { resultUserInfo = HttpClientUtils.get(userInfoUrl); System.out.println("resultUserInfo="+resultUserInfo); } catch (Exception e) { e.printStackTrace(); } //3.1解析json HashMap userMap = gson.fromJson(resultUserInfo, HashMap.class); String nickname = (String)userMap.get("nickname"); String headimgurl = (String)userMap.get("headimgurl"); //自己的业务 /** //4根据openid查询用户 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("openid",openid); UcenterMember member = memberService.getOne(queryWrapper); //5判断用户是否存在,用户不存在,走注册 if(member==null){ member = new UcenterMember(); member.setNickname(nickname); member.setAvatar(headimgurl); member.setOpenid(openid); memberService.save(member); } String token = JwtUtils.getJwtToken(member.getId(),member.getNickname()); **/ return null; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    结果

    1.输入请求地址
    在这里插入图片描述2.微信扫码
    在这里插入图片描述3.控制台
    在这里插入图片描述

  • 相关阅读:
    在伦敦银投资中,技术是万能的?
    基于springboot+vue的加盟店管理系统(前后端分离)
    一文看懂 Camera2 framework:以具体 preview 流程为例 独家原创
    怎样在PDF上直接编辑文字?这几种编辑方法需要掌握
    L1-087 机工士姆斯塔迪奥-PAT 团体程序设计天梯赛 GPLT
    【批处理DOS-CMD命令-汇总和小结】-环境变量、路径变量、搜索文件位置相关指令——set、path、where
    CISAW培训可以自学报名考试吗?
    域名备案流程(个人备案,腾讯云 / 阿里云)
    on java8之接口
    python pip3 安装psycopg2报错
  • 原文地址:https://blog.csdn.net/daai5201314/article/details/126327575