第01章-准备工作

1、申请微信登录

https://open.weixin.qq.com

(1)注册开发者账号:准备营业执照

(2)邮箱激活

(3)完善开发者资料

(4)开发者资质认证:1-2个工作日审批、300元

(5)创建网站应用:提交审核,7个工作日审批(免费)

(6)熟悉微信登录流程

参考文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

2、OAuth2.0简介

OAuth 2.0是目前最流行的授权机制,用来授权第三方应用,获取用户数据。

OAuth 2.0 一词中的 “Auth” 表示 “授权”,字母 “O” 是 Open 的简称,表示 “开放” ,连在一起就表示 “开放授权”。这也是为什么我们使用 OAuth 的场景,通常发生在开放平台的环境下。

越来越多的第三方应用都在向用户提供使用微信登录的解决方案,来减少用户注册的繁琐操作。而这个解决方案的背后原理,也是我们要讲到的 OAuth 2.0 技术。

2.1、一个简单解释

快递员问题

我住在一个大型的居民小区。

小区有门禁系统。

进入的时候需要输入密码。

我经常网购和外卖,每天都有快递员来送货。我必须找到一个办法,让快递员通过门禁系统,进入小区。

如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,这样好像不太合适。万一我想取消他进入小区的权力,也很麻烦,我自己的密码也得跟着改了,还得通知其他的快递员。

有没有一种办法,让快递员能够自由进入小区,又不必知道小区居民的密码,而且他的唯一权限就是送货,其他需要密码的场合,他都没有权限?

授权机制的设计

于是,我设计了一套授权机制。

第一步,门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员需要首先按这个按钮,去申请授权。

第二步,他按下按钮以后,屋主(也就是我)的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。

我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。

第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。

第四步,快递员向门禁系统输入令牌,进入小区。

有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。

互联网场景

我们把上面的例子搬到互联网,就是 OAuth 的设计了。

首先,居民小区就是储存用户数据的网络服务。比如,微信储存了我的好友信息,获取这些信息,就必须经过微信的"门禁系统"。

其次,快递员(或者说快递公司)就是第三方应用,想要穿过门禁系统,进入小区。

最后,我就是用户本人,同意授权第三方应用进入小区,获取我的数据。

简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

令牌与密码

令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。

(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

(2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。

(3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。

注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。

2.4、OAuth2的四种方式

OAuth 的核心就是向第三方应用颁发令牌,OAuth 2.0 的标准是 RFC 6749 文件。由于互联网有多种场景,标准中定义了获得令牌的四种授权方式(authorization grant ):

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password)
  • 客户端凭证(client credentials)

注意,不管哪一种授权方式,第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(AppID)和客户端密钥(AppSecret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。

参考文档:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html

2.5、微信登录获取令牌时序图

微信登录使用了授权码方式

第02章-显示登录二维码

1、创建用户微服务

1.1、创建数据库

资料:资料>微信扫码登录>guigu_syt_user.sql

1.2、创建service-user微服务

1.3、添加依赖

在service-user中添加依赖:

<dependencies>
<!--实体-->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>model</artifactId>
<version>1.0</version>
</dependency> <!--服务通用配置-->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-util</artifactId>
<version>1.0</version>
</dependency> <!--自定义安全模块-->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>spring-security</artifactId>
<version>1.0</version>
</dependency> <!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> <!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

1.4、使用代码生成器

找到service-util模块中的代码生成器,修改moduleName为user,并执行,然后删除entity包,相关类中引入model模块中的类

1.5、创建配置文件

在server-user模块中resources目录下创建文件

application.yml

spring:
application:
name: service-user
profiles:
active: dev,redis

application-dev.yml

mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:com/atguigu/syt/user/mapper/xml/*.xml
server:
port: 8203
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://localhost:3306/guigu_syt_user?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
username: root
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8 logging:
level:
root: info
file:
path: user feign:
client:
config:
default:
connect-timeout: 2000 #连接建立的超时时长,单位是ms,默认1s
read-timeout: 2000 #处理请求的超时时间,单位是ms,默认为1s sentinel:
enabled: true #开启Feign对Sentinel的支持 wx:
open:
app-id: wxc606fb748aedee7c # 微信开放平台 appid
app-secret: 073e8e1117c1054b14586c8aa922bc9c #微信开放平台 appsecret
redirect-uri: http://localhost:8200/api/user/wx/callback #微信开放平台 重定向url
syt-base-url: http://localhost:3000 #预约挂号平台baserul

注意:此处重定向url的主机地址必须为 localhost:8200,因为这是在微信开放平台中预先配置的参数。生产环境中这个参数需要根据实际情况进行修改。

1.6、创建启动类

package com.atguigu.syt.user;

@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"})
public class ServiceUserApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceUserApplication.class, args);
}
}

1.7、创建常量类

创建utils包,创建ConstantProperties.java常量类

package com.atguigu.syt.user.utils;

@Configuration
@ConfigurationProperties(prefix="wx.open") //读取节点
@Data //使用set方法将wx.ope节点中的值填充到当前类的属性中
public class ConstantProperties {
private String appId;
private String appSecret;
private String redirectUri;
private String sytBaseUrl;
}

避免红色提示:在service的pom.xml中添加如下依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

2、Controller

2.1、创建controller

service-user微服务中创建controller.front包,front包中创建FrontWxController

package com.atguigu.syt.user.controller.front;

@Api(tags = "微信扫码登录")
@Controller//注意这里没有配置 @RestController
@RequestMapping("/front/user/wx")
@Slf4j
public class FrontWxController { @Resource
private ConstantProperties constantProperties; @GetMapping("login")
public String login(HttpSession session){
try { StringBuffer baseUrl = new StringBuffer()
.append("https://open.weixin.qq.com/connect/qrconnect")
.append("?appid=%s")
.append("&redirect_uri=%s")
.append("&response_type=code")
.append("&scope=snsapi_login")
.append("&state=%s")
.append("#wechat_redirect"); //处理回调url
String redirectUri = URLEncoder.encode(constantProperties.getRedirectUri(), "UTF-8");
//处理state:生成随机数,存入session
//ThreadLocalRandom解决了Random在高并发环境下随机数生成性能问题
long nonce = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
//十六进制表示的随机数
String state = Long.toHexString(nonce);
log.info("生成 state = " + state);
session.setAttribute("wx_open_state", state); String qrcodeUrl = String.format(
baseUrl.toString(),
constantProperties.getAppId(),
redirectUri,
state
); return "redirect:" + qrcodeUrl; } catch (Exception e) {
throw new GuiguException(ResultCodeEnum.URL_ENCODE_ERROR, e);
}
}
}

2.2、网关配置

在server-gateway中添加如下配置

        - id: service-user
predicates: Path=/*/user/**
uri: lb://service-user

3、跨站请求伪造

参考资料:https://baike.baidu.com/item/跨站请求伪造/13777878?fr=aladdin

3.1、介绍

跨站请求伪造,Cross-site request forgery,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法

3.2、攻击细节

跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的

3.3、例子

假如一家银行用以运行转账操作的URL地址如下:http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName

那么,一个恶意攻击者可以在另一个网站上放置如下代码:

如果有账户名为Tom的用户访问了恶意站点,而他之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。

3.4、防御措施

添加校验token

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再运行CSRF攻击。这种数据通常是请求中的一个数据项。服务器将其生成并附加在请求中,其内容是一个伪随机数。当客户端提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

第03章-登录回调

1、Controller

注意:微信服务器配置授权回调域要和redirect-uri一致

service-user微服务中创建controller.api包,api包中创建ApiWxController

package com.atguigu.syt.user.controller.api;

@Api(tags = "微信扫码登录回调")
@Controller//注意这里没有配置 @RestController
@RequestMapping("/api/user/wx")
@Slf4j
public class ApiWxController { /**
* 登录回调
* @param code
* @param state
* @param session
* @return
*/
@GetMapping("callback")
public String callback(String code, String state, HttpSession session) {
//得到授权临时票据code和state参数
log.info("callback被调用");
log.info("code = " + code);
log.info("state = " + state);
String sessionState = (String) session.getAttribute("wx_open_state");
log.info("sessionState = " + sessionState);
log.info("seesion_id = " + session.getId()); if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state) || !state.equals(sessionState)) {
throw new GuiguException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
} return null;
}
}

3、Service

根据微信的openid判断数据库是否存在当前用户信息

接口:UserInfoService

/**
* 根据openid查询用户信息
* @param openid
* @return
*/
UserInfo getByOpenId(String openid);

实现:UserInfoServiceImpl

@Override
public UserInfo getByOpenId(String openid) { LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserInfo::getOpenid, openid);
return baseMapper.selectOne(queryWrapper);
}

3、完善回调方法

资料:资料>微信扫码登录>CookieUtils.java放入service-util模块

package com.atguigu.syt.user.controller.api;

@Api(tags = "微信扫码登录回调")
@Controller//注意这里没有配置 @RestController
@RequestMapping("/api/user/wx")
@Slf4j
public class ApiWxController { @Resource
private ConstantProperties constantProperties; @Resource
private UserInfoService userInfoService; @Resource
private RedisTemplate redisTemplate; /**
* 登录回调
* @param code
* @param state
* @param session
* @return
*/
@GetMapping("callback")
public String callback(String code, String state, HttpSession session, HttpServletResponse response) {
try {
//得到授权临时票据code和state参数
log.info("callback被调用");
log.info("code = " + code);
log.info("state = " + state);
String sessionState = (String) session.getAttribute("wx_open_state");
log.info("sessionState = " + sessionState);
log.info("seesion_id = " + session.getId()); if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state) || !state.equals(sessionState)) {
throw new GuiguException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
} //使用code和appid以及appscrect换取access_token
StringBuffer baseAccessTokenUrl = new StringBuffer()
.append("https://api.weixin.qq.com/sns/oauth2/access_token")
.append("?appid=%s")
.append("&secret=%s")
.append("&code=%s")
.append("&grant_type=authorization_code"); String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
constantProperties.getAppId(),
constantProperties.getAppSecret(),
code); //使用httpclient发送请求
byte[] respdata = HttpUtil.doGet(accessTokenUrl);
String result = new String(respdata);
log.info("accesstokenInfo:" + result); JSONObject resultJson = JSONObject.parseObject(result);
if (resultJson.getString("errcode") != null) {
log.error("获取access_token失败:" + resultJson.getString("errcode") + resultJson.getString("errmsg"));
throw new GuiguException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
} String accessToken = resultJson.getString("access_token");
String openId = resultJson.getString("openid");
log.info(accessToken);
log.info(openId); //根据access_token获取微信用户的基本信息
//先根据openid进行数据库查询
UserInfo userInfo = userInfoService.getByOpenId(openId); if (userInfo != null) { //存在
log.info("判断用户是否被禁用");
if(userInfo.getStatus() == UserStatusEnum.LOCK.getStatus()){
log.error("用户已被禁用");
throw new GuiguException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
}
}else{ log.info("注册用户");
//使用access_token换取受保护的资源:微信的个人信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
//使用httpclient发送请求
String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openId);
byte[] respdataUser = HttpUtil.doGet(userInfoUrl);
String resultUserInfo = new String(respdataUser); JSONObject resultUserInfoJson = JSONObject.parseObject(resultUserInfo);
if (resultUserInfoJson.getString("errcode") != null) {
log.error("获取用户信息失败:" + resultUserInfoJson.getString("errcode") + resultUserInfoJson.getString("errmsg"));
throw new GuiguException(ResultCodeEnum.FETCH_USERINFO_ERROR);
} //解析用户信息
String nickname = resultUserInfoJson.getString("nickname");
String headimgurl = resultUserInfoJson.getString("headimgurl"); //用户注册
userInfo = new UserInfo();
userInfo.setOpenid(openId);
userInfo.setNickName(nickname);
userInfo.setHeadImgUrl(headimgurl);
userInfo.setStatus(UserStatusEnum.NORMAL.getStatus());
userInfoService.save(userInfo); } //获取用户名,如果没有用户名(未实名认证),则获取昵称
String name = userInfo.getName();
if (StringUtils.isEmpty(name)) {
name = userInfo.getNickName();
} //生成token
String token = UUID.randomUUID().toString().replaceAll("-", "");
//将token做key,用户id做值存入redis
redisTemplate.opsForValue()//30分钟
.set("user:token:" + token, userInfo.getId(), 30, TimeUnit.MINUTES); //将token和name存入cookie
//将"资料>微信登录>CookieUtils.java"放入service-utils模块
int cookieMaxTime = 60 * 30;//30分钟
CookieUtils.setCookie(response, "token", token, cookieMaxTime);
CookieUtils.setCookie(response, "name", URLEncoder.encode(name), cookieMaxTime);
CookieUtils.setCookie(response, "headimgurl", URLEncoder.encode(userInfo.getHeadImgUrl()), cookieMaxTime); return "redirect:" + constantProperties.getSytBaseUrl(); } catch (GuiguException e) {
log.error(ExceptionUtils.getStackTrace(e));
return "redirect:" + constantProperties.getSytBaseUrl()
+ "?code=201&message=" + URLEncoder.encode(e.getMsg());
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
return "redirect:" + constantProperties.getSytBaseUrl()
+ "?code=201&message="+URLEncoder.encode("登录失败");
}
}
}

4、前端整合

资料:资料>微信扫码登录>myheader.vue

将myheader.vue复制到前端项目的layouts目录中,覆盖原来的文件

第04章-spring session共享(了解)

前面我们使用了访问令牌和redis的形式代替session,也可以使用Spring提供的SpringSession实现session共享

开启SpringSession的步骤如下:

1、添加依赖

在service-user中添加spring-session依赖

<dependencies>
<!--spring session-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>

2、添加json序列化配置

在service-util的RedisConfig中添加如下配置:将默认的jdk序列化方案修改为json序列化方案

@Bean //(name = "springSessionDefaultRedisSerializer")
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}

上面的配置注入如下的位置

此时,之前我们存入session中的防止CSRF攻击的参数state,就可以自动存储在redis中了

3、测试session共享

在FrontWxController中添加测试代码

@GetMapping("testSaveSession") //在8203执行session存储
public void testSaveSession(HttpSession session){
session.setAttribute("user", "helen");
} @GetMapping("testGetSession") //在8213执行session获取
public void testGetSession(HttpSession session){
String user = (String)session.getAttribute("user");
log.info(user);
}

源码:https://gitee.com/dengyaojava/guigu-syt-parent

尚医通-day10【微信扫码登录】(内附源码)的更多相关文章

  1. java小项目之:抽奖系统!java初学者必备(内附源码)

    [Java]Java摇奖源码,Java抽奖源码,Java随机抽奖源码 任务描述 本次任务要求为某商场开发一套幸运抽奖系统,客户必须首先注册成为该商场会员,会员登录成功后,就可以参加抽奖活动了.注册 用 ...

  2. C#/ASP.NET MVC微信公众号接口开发之从零开发(二) 接收微信消息并且解析XML(附源码)

    文章导读: C#微信公众号接口开发之从零开发(一) 接入微信公众平台 微信接入之后,微信通过我们接入的地址进行通信,其中的原理是微信用户发送消息给微信公众账号,微信服务器将消息以xml的形式发送到我们 ...

  3. 菜鸟学习SSH(一)——Struts实现简单登录(附源码)

    从今天开始,一起跟各位聊聊java的三大框架——SSH.先从Struts开始说起,Struts对MVC进行了很好的封装,使用Struts的目的是为了帮助我们减少在运用MVC设计模型来开发Web应用的时 ...

  4. 菜鸟学SSH(一)——Struts实现简单登录(附源码)

    从今天开始,一起跟各位聊聊java的三大框架——SSH.先从Struts开始说起,Struts对MVC进行了很好的封装,使用Struts的目的是为了帮助我们减少在运用MVC设计模型来开发Web应用的时 ...

  5. 基于电商直播SDK快速实现一个淘宝直播APP【内附源码】

    现在各大互联网APP都标配电商直播带货了,没有直播带货开发经验都感觉自己跟不上技术的进步.今天快速基于Java实现一个安卓端电商直播APP,深入理解整个电商直播开发流程.我们最终实现效果如下: 按照惯 ...

  6. 从零实现在线云相亲APP|程序员脱单神器(内附源码Demo)

    实时音视频通话涉及到的技术栈.人力成本.硬件成本非常大,一般个人开发者基本无法独立完成一个功能健全并且稳定的实时音视频应用.本文介绍一天之内,无任何实时音视频低层技术的android开发者完成实时相亲 ...

  7. 干货:Java多线程详解(内附源码)

      线程是程序执行的最小单元,多线程是指程序同一时间可以有多个执行单元运行(这个与你的CPU核心有关). 在java中开启一个新线程非常简单,创建一个Thread对象,然后调用它的start方法,一个 ...

  8. jquery省市区三级联动(数据来源国家统计局官网)内附源码下载

    很久很久没有写博了. 今天更新了项目的省市区三级联动数据,更新后最新的海南三沙都有,分享给所有需要的小伙伴们... JQUERY + JSON,无数据库,纯JS代码,无加密,无压缩,可直接使用在任何项 ...

  9. 花时三月 终于Spring Boot 微信点餐开源系统! 附源码

    架构 前后端分离:             Nginx与Tomcat的关系在这篇文章,几分钟可以快速了解: https://www.jianshu.com/p/22dcb7ef9172 补充: set ...

  10. 如何0代码实现多人音视频通话?【内附源码/Demo】

    3月15日新增"1860+1194",全国进入了抗疫关键时期.响应政策多地采取了社会面清零策略. 3月14日零点,深圳按下了暂停键. 应疫情防控要求,深圳全市暂停生产经营活动,严格 ...

随机推荐

  1. Django笔记九之model查询filter、exclude、annotate、order_by

    在接下来四五篇笔记中,将介绍 model 查询方法的各个细节,为我们的查询操作提供各种便利. 本篇笔记将介绍惰性查找.filter.exclude.annotate等方法,目录如下: 惰性查找 fil ...

  2. 基于el-input实现数字区间输入框(已发布npm/github)

    项目地址:https://github.com/heyu3913/InputNumberRange  (求star) input-number-range tips:更多定制化需求请联系: 13102 ...

  3. ACM-NEFUOJ-最小树-Prim算法

    最小树1 Description 某省长调查交通情况,发现本省交通事故发生不断,于是决定在本省内全部修建地铁. 该省长得到的统计表中列出了任意两市之间的距离,为了确保任何两个市都可以直接 或者间接实现 ...

  4. [issues] webrtc 接入SRS丢包率不正确问题

    目录 [issues] webrtc 接入SRS丢包率不正确问题 原因和解决方法 srs增加rtx SDP协商 构建RTX包 [issues] webrtc 接入SRS丢包率不正确问题 原因和解决方法 ...

  5. Ubuntu2204部署容器引擎Containerd

    为什么使用containerd? 使用containerd的原因主要有两点吧,第一个是docker在k8s1.24以后不再支持,如果需要在k8s中继续使用docker作为容器引擎,我们需要额外部署cr ...

  6. 从内核源码看 slab 内存池的创建初始化流程

    在上篇文章 <细节拉满,80 张图带你一步一步推演 slab 内存池的设计与实现 >中,笔者从 slab cache 的总体架构演进角度以及 slab cache 的运行原理角度为大家勾勒 ...

  7. 升级:In-Place Upgrade升级MySQL5.6.26

    升级需谨慎,事前先备份 MySQL升级的实质是对数据字典的升级,数据字典有:sys.mysql.information_schema.performance_schema . MySQL升级的两种方式 ...

  8. 【Spring5】JdbcTemplate

    JdbcTemplate实现对数据库增删改查 步骤 导入Jar包 mysql-connector-java-8.0.28.jar:mysql数据库连接的相关依赖 spring-tx-5.2.6.REL ...

  9. 开心档之MySQL 连接

    MySQL 连接 使用mysql二进制方式连接 您可以使用MySQL二进制方式进入到mysql命令提示符下来连接MySQL数据库. 实例 以下是从命令行中连接mysql服务器的简单实例: [root@ ...

  10. go slice使用

    1. 简介 在go中,slice是一种动态数组类型,其底层实现中使用了数组.slice有以下特点: *slice本身并不是数组,它只是一个引用类型,包含了一个指向底层数组的指针,以及长度和容量. *s ...