ye-guo / blog-contents Goto Github PK
View Code? Open in Web Editor NEW文章
文章
title: Mysql数据库远程登录失败
date: 2024-04-09 09:56
updated: 2024-04-09 09:56
category: 技术杂谈
tags:
连接数据库报错:Access denied for user ‘root‘@ ‘xxx‘ (using password: YES)
本地的数据库,其他IP地址的访问都是没有权限的,需要的本机用户给予权限才能访问到。
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '您的密码' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '' WITH GRANTOPTION;
前端:三件套 + React + 组件库 Ant Design + Umi + Ant Design Pro(现成的管理系统)
后端:
部署:服务器 / 容器(平台)
1.初始化项目:使用umijs脚手架初始化项目√
2.引入组件:√
3.项目瘦身:删除不需要文件√
1.初始化项目:使用springboot模板初始化,引入依赖,√
2.数据库连接池:druid连接池整合√
3.持久层工具:mybatis-plus整合√
4.数据库连接:数据库连接测试√
5.数据库表:数据库表设计√
id(主键)bigint
username 昵称 varchar
userAccount 登录账号
avatarUrl 头像 varchar
gender 性别 tinyint
userPassword 密码 varchar
phone 电话 varchar
email 邮箱 varchar
userStatus 用户状态 int 0 - 正常
createTime 创建时间(数据插入时间)datetime
updateTime 更新时间(数据更新时间)datetime
isDelete 是否删除 0 1(逻辑删除)tinyint
userRole 用户角色 0 - 普通用户 1 - 管理员
/api/user/register POST
1.用户输入:用户在前端输入账号、密码、校验密码
2.后端校验:校验用户账号、密码、校验密码是否有效
3.密码加密:使用sm4算法密码加密√
4.插入数据库表:将该用户insert用户表中√
5.返回数据:对成功数据封装响应类返回
6.测试√
校验错误处都抛出业务异常
/api/user/login POST
1.用户输入:用户在前端输入账号、密码
2.后端校验:校验用户账号、密码是否有效
3.数据库查询:查询用户,如果user为空则返回
4.查询结果:存在,对前端密码加密和查询到的对比
5.密码正确:为用户设置session
6.返回数据:正确则脱除敏感信息后封装成响应类返回信息
7.测试√
校验错误处都抛出业务异常
/api/user/current GET // 查询当前用户
/api/user/logout POST // 退出登录
/api/user/selectAll GET // 查询所有
/api/user/remove POST // 删除
...
public class UserConstant {
/**
* 用户登录态key
*/
public static final String USER_LOGIN_STATE = "userLoginState";
/**
* 普通用户
*/
public static final int DEFAULT_ROLE = 0;
/**
* 管理员
*/
public static final int ADMIN_ROLE = 1;
}
public class Result<T> implements Serializable {
private int code;
private T data;
private String message;
private String description;
public Result(int code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
// 以下重载构造函数可以根据自己业务需要书写提供语法糖
public Result(int code,T data) {
this(code,data,"成功");
}
// 失败不返回数据
public Result(ErrorCode errorCode){
this(errorCode.getCode(),null, errorCode.getMessage());
}
public Result(ErrorCode errorCode,String description) {
this.code = errorCode.getCode();
this.data = null;
this.message = errorCode.getMessage();
this.description = description;
}
public Result(int code,T data,String message,String description) {
this.code = code;
this.data = data;
this.message = message;
this.description = description;
}
}
public class ResultUtils {
// 成功
public static <T> Result<T> success(T data) {
return new Result<>(20000,data);
}
// 失败
public static <T> Result<T> err(ErrorCode errorCode) {
return new Result<>(errorCode);
}
// 以下重载构造函数可以根据自己业务需要书写提供语法糖
public static <T> Result<T> err(ErrorCode errorCode,String description) {
return new Result<>(errorCode,description);
}
public static <T> Result<T> err(int code,String message,String description) {
return new Result<>(code,null,message,description);
}
}
public enum ErrorCode {
NOT_LOGIN(40000,"未登录"),
PARAMS_ERROR(40001,"请求参数错误"),
USER_USED_ERROR(40002,"账号已存在"),
NOT_AUTHORITY(40003,"无权限"),
NOT_FOUND_ERROR(40004,"未找到"),
PASSWORD_ERROR(40005,"密码错误"),
SYSTEM_ERROR(50000,"运行时异常")
;
private int code;
private String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
业务返回错误处直接抛出异常,在全局异常处理器集中处理异常,并返回给前端
public class BusinessException extends RuntimeException{
private final int code;
private final String description;
public BusinessException(int code, String message, String description) {
super(message);
this.code = code;
this.description = description;
}
public BusinessException(ErrorCode errorCode, String description) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
this.description = description;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
this.description = "";
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
}
使用spring提供@RestControllerAdvice注解,全局异常控制器处理抛出的异常,使用lombok的@slf4j来记录日志
@Slf4j
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(BusinessException.class)
public Result businessExceptionHandler(BusinessException e) {
log.error("businessException:",e.getMessage(),e.getDescription());
return ResultUtils.err(e.getCode(),e.getMessage(),e.getDescription());
}
@ExceptionHandler(RuntimeException.class)
public Result runtimeExceptionHandler(RuntimeException e) {
log.error("runtimeException:",e);
return ResultUtils.err(ErrorCode.SYSTEM_ERROR,"运行时异常");
}
}
实现HandlerInterceptor接口实现preHandle()方法在请求处理前进行全局的处理
@Component
public class GlobalPreInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false); // 参数设置为false,如果Session不存在则返回null
if (session == null || session.getAttribute(UserConstant.USER_LOGIN_STATE) == null) {
// Session不存在或用户未登录,可以进行相应的处理
throw new BusinessException(ErrorCode.NOT_LOGIN,"未登录");
}
return true;
}
}
将全局拦截器注册到配置类中
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private GlobalPreInterceptor globalPreInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(globalPreInterceptor)
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns("/user/login","/user/register",
"/user/logout"); // 不拦截登录路径
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.maxAge(3600); // 允许携带身份验证信息
}
}
springboot可以根据配置文件后缀识别当前环境,application.yml为基本配置,启用某个环境,环境中与基本配置中相同的属性会覆盖掉,其他的会使用基本配置.
application.yml,application-production.yml,application-test.yml多个配置文件
可通过以下命令来启用某环境配置
java -jar xxxx.jar --spring.profiles.active=production
//启用生产环境配置
1.app.tsx 运行时配置
getInitialState()函数
每次初始化getInitialState()会查询当前用户,将一些返回值存入InitialState,全局可取
每次刷新页面都会执行getInitialState()
切换页面不执行
layout: RunTimeLayoutConfig
.
├── config
│ └── config.ts
├── dist
├── mock
│ └── app.ts|tsx
├── src
│ ├── .umi
│ ├── .umi-production
│ ├── layouts
│ │ ├── BasicLayout.tsx
│ │ ├── index.less
│ ├── models
│ │ ├── global.ts
│ │ └── index.ts
│ ├── pages
│ │ ├── index.less
│ │ └── index.tsx
│ ├── utils
│ │ └── index.ts
│ ├── services
│ │ └── api.ts
│ ├── app.(ts|tsx)
│ ├── global.ts
│ ├── global.(css|less|sass|scss)
│ ├── overrides.(css|less|sass|scss)
│ ├── favicon.(ico|gif|png|jpg|jpeg|svg|avif|webp)
│ └── loading.(tsx|jsx)
├── node_modules
│ └── .cache
│ ├── bundler-webpack
│ ├── mfsu
│ └── mfsu-deps
├── .env
├── plugin.ts
├── .umirc.ts
├── package.json
├── tsconfig.json
└── typings.d.ts
umi max提供了很多插件如
export default defineConfig({
routes,//路由的配置
fastRefresh: true,//快速热更新配置
model: {},//数据流插件
initialState: {},//一个全局的初始数据流,可以用它在插件之间共享数据
antd: {},//antd 插件
request: {},//网络请求配置
access: {},//权限插件
headScripts: [
// 解决首次加载时白屏的问题
{
src: '/scripts/loading.js',
async: true,
},
],
mfsu: {//打包提速
strategy: 'normal',
},
proxy: proxy.dev//代理
})
将带有/api前缀请求代理到后端地址解决开发时跨域问题,build后失效
export default proxyConfig{
dev: {
'/api/': {
target: 'http://localhost:8080/',
changeOrigin: true,
},
},
}
如定义响应、注册params、注册响应数据类型
declare namespace API {
type Response<T> = {
code: number;
data: T;
message: string;
description?: string;
},
type RegisterParams={
userAccount:string;
userPassword:string;
checkPassword:string;
type?: string;
},
type RegisterResult = number;
type CurrentUser = {
userRole: number;
...//其他属性
}
}
如下
export async function register(body: API.RegisterParams, options?: { [key: string]: any }) {
return request<API.Response<API.RegisterResult>>('/api/user/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
...//其他接口
可以配置请求拦截器和相应拦截器,来进行个性化处理,可以配置根据当前环境更改基础请求地址,方便开发
export const request = {
baseURL: process.env.NODE_ENV==='production'?"https://production.envriment.com":"http://localhost:8080",
// 请求拦截器
requestInterceptors: [
(config) => {
// 拦截请求配置,进行个性化处理。
const url = config.url.concat('?token = 123');
return { ...config, url};
}
],
// 响应拦截器
responseInterceptors: [
(response) => {
// 拦截响应数据,进行个性化处理
const { data } = response;
if(!data.success){
message.error('请求失败!');
}
return response;
}
]
};
/src/access.ts是默认的权限定义文件
// /src/access.ts
export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) {
const { currentUser } = initialState ?? {};
return {
canAdmin: currentUser && currentUser.userRole === 1,
};
}
路由配置文件中,只有是canAdmin才能访问/admin路径下页面
export default[
{ path: '/welcome', name: '欢迎', icon: 'smile', component: './Welcome' },
{
path: '/admin',
name: '管理页',
icon: 'crown',
access: 'canAdmin', // 只有canAdmin能访问
routes: [
{ path: '/admin', redirect: '/admin/user-manage' },
],
},
]
/src/pages/目录是页面目录,在该目录下加入自己需要的页面。需要其他组件在/src/components/ 目录下进行开发或引入
对已有接口进行前后端联调,检查bug,解决
location ^~ /api/ {
proxy_pass http://127.0.0.1:8080/api/;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers '*';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X- Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0; return 204;
}
}
@CrossOrigin(origins = { "http://xxx.com/"}, methods = { RequestMethod.DELETE },allowCredentials = "true")
origins
: 同样是指定允许跨域请求的源,即允许访问资源的域名。在这里,同样只允许来自 "http://xxx.com" 的跨域请求。methods
: 允许请求的方法。allowCredentials
: 指定是否允许发送身份验证信息(例如 cookie)到目标域。在这里设置为 true
,表示允许发送身份验证信息。@Configuration
public class WebMvcConfg implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
//当**Credentials为true时,**Origin不能为星号,需为 具体的ip地址【如果接口不带cookie,ip无需设成具体ip】
.allowedOrigins(
"http://127.0.0.1:9527",
"http://127.0.0.1:8082",
"http://127.0.0.1:8083")
//是否允许证书 不再默认开启
.allowCredentials(true)
//设置允许的方法
.allowedMethods("*")
//跨域允许时间
.maxAge(3600);
}
// 或
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//当 Credentials为true时,Origin不能为星号,需为 具体的ip地址【如果接口不带cookie,ip无需设成具体ip】,使用allowedOriginPatterns可以带*
.allowedOriginPatterns("*")
//设置允许的方法
.allowedMethods("*")
//指定是否允许发送身份验证信息(例如 cookie)到目标域
.allowCredentials(true)
//跨域允许时间
.maxAge(3600); // 允许携带身份验证信息
}
}
这个配置文件最后引入到nginx.conf主配置文件的http区域中
server {
#SSL 默认访问端口号为 443
listen 443 ssl;
#请填写绑定证书的域名
server_name example.com;
# gzip config
gzip on;
gzip_min_length 1k;
gzip_comp_level 9;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
#请填写证书文件的相对路径或绝对路径 注意容器nginx配置路径是挂载对应目录
ssl_certificate /ssl/domain_bundle.crt;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key /ssl/domain.key;
ssl_session_timeout 5m;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
// 网关支持
location ^~ /api/ {
rewrite ^/api(.*)$ $1 break;
proxy_pass http://172.17.0.1:8080/;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers '*';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X- Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0; return 204;
}
}
location / {
try_files $uri /index.html;
}
if ($host != "example.com") {
return 403; # 如果请求的域名不匹配,返回 403 禁止访问
}
}
server {
listen 80;
#请填写绑定证书的域名
server_name example;
#把http的域名请求转成https
return 301 https://$host$request_uri;
}
1.前端:在服务器端手动安装nginx
或者:centos直接用yum下载不需要下面这么麻烦,
使用yum直接下载nginx,将构建的dist目录放在配置的root目录下 √
# 下载 nginx curl -o nginx-1.21.6.tar.gz http://nginx.org/download/nginx-1.21.6.tar.gz # 解压 nginx tar -zxvf nginx-1.21.6.tar.gz # 进入 nginx 目录
cd nginx-1.21.6 # 安装 nginx 需要的环境
yum install pcre pcre-devel -y
yum install openssl openssl-devel -y
yum -y install gcc-c++
# 配置文件检查
./configure --with-http_ssl_module --with-http_v2_module --with-stream
# 编译
make
make install
# 查看是否有 nginx 的可执行文件
ls /usr/local/nginx/sbin/nginx
# 配置全局环境变量, shift+g 定位到最后一行,再按 o 在最后一行插入
vim /etc/profile
#在最后一行添加:export PATH=$PATH:/usr/local/nginx/sbin
#修改环境变量后执行:source /etc/profile
# 启动nginx
nginx
# 查看启动情况
netstat -ntlp #查看启动情况
# 修改 nginx 配置后,需要运行如下命令,才生效
nginx -s reload
#####
#补充:后端打包后。将编译好的文件压缩为zip
#使用命令解压压缩文件到指定目录
unzip dist.zip -d your_project_directory
#随后进入其中的dist目录使用命令
mv * ../
#将dist目录的所有文件移至外层即your_project_directory目录,并对dist文件夹进行删除
#随后我们对nginx.conf配置文件进行更改
#tips好习惯:在对配置文件进行更改的时候,先对配置文件进行备份
cp nginx.conf nginx.default.conf
#使用自带的vim编译器进行编辑
vim nginx.conf
#tips :报错退出 esc + :wq
#i 插入模式 o 新增一行
#主要对user进行编辑,修改默认值为root,同时修改lcoation下的root 后为我们解压好的目录
#随后移动这个文件
cp nginx.conf /usr/local/nginx
mv nginx.conf ./conf/
#随后开启nginx服务器,访问服务器的默认公网地址即可
nginx
后端:下载好java,将打包好的jar传到linux服务器,linux下载java,直接命令启动√
// 运行 jar 包
jar -jar ./Your_project.jar --server.port=8080 -spring.profiles.active=production
// 后台运行
# 后台运行 jar 包
nohup java -jar ./Your_project.jar --server.port=8080 -- spring.profiles.active=production &
jps:查看所有运行的java程序
非常方便简单
// Dockerfile
# 使用 NGINX 官方提供的稳定版作为基础镜像
FROM nginx:stable
# 暴露 NGINX 默认的 HTTP 端口
EXPOSE 80
# 在容器启动时自动启动 NGINX 服务
CMD ["nginx", "-g", "daemon off;"]
将dockerfile、dist、nginx配置文件放在目录中进行构建镜像
构建镜像:docker build -t project_name:version .
运行容器配置挂载目录,宿主机挂载目录根据自己需求而定
docker run -id --name=Your_project_name -p 80:80 -v /service/your_project/dist:/usr/share/nginx/html -v /service/your_project/logs:/var/log/nginx -v /service/your_project/nginx:/etc/nginx/conf.d -v
/service/your_project/ssl:/ssl 镜像:版本
# 使用 maven jdk8作为基础镜像
FROM maven:3.5-jdk-8-alpine as builder
# 指定工作目录/app,cp pom.xml和src ==》 /app目录下
WORKDIR /app
COPY pom.xml .
COPY src ./src
# maven打包跳过测试
RUN mvn package -DskipTests
# 运行,指定环境
CMD ["java","-jar","/app/target/your_project.jar","--spring.profiles.active=production"]
如果项目在github仓库中直接clone过来,否则自己打包上传
构建镜像:docker build -t project_name:version .
运行容器配置挂载目录,宿主机挂载目录根据自己需求而定
docker run -id --name=Your_project_name 镜像:版本
云服务商容器平台(腾讯、阿里、微信云)等,可快速部署,更省心省力,注意收费规则
容器平台的好处:
不用输命令来操作,更方便省事
不用在控制台操作,更傻瓜式、更简单
大厂运维,比自己运维更省心
额外的能力,比如监控、告警、其他(存储、负载均衡、自动扩缩容、流水线)
到各大运营商(腾讯、阿里等)注册想要的域名
将域名解析到服务器地址,nginx绑定相应server_name,如果是云托管平台,使用CNAME解析到平台提供的地址上
title: Docker常用命令
date: 2023-07-27 17:28
updated: 2023-07-27 17:28
category: 开发记录
tags:
systemctl start docker
systemctl stop docker
systemctl restart docker
systemctl status docker
systemctl enable docker
docker images
docker images -q # 查看所有镜像的id
docker search 镜像名称
docker pull 镜像名称/镜像名称:版本号
docker rmi 镜像id/名称 # 删除指定本地镜像
docker rmi `docker images -q` # 删除所有本地镜像
docker ps # 查看正在运行的容器
docker ps -a # 查看所有容器
docker ps -aq # 查看所有容器id
docker run 参数
docker run -it --name=x1 镜像 /bin/bash # 例
docker run -id --name=x2 镜像
# -i:保持容器运行,通常与-t同时使用,加it两个参数后,容器创建后自动进入容器,退出后容# 器自动关闭
# -t:为容器重新分配一个伪输入终端,通常与-i同时使用
# -it创建的容器一般称为交互式容器,-id创建容器一般称为守护式容器
# exec在后台运行时进入容器
docker exec -it c1 /bin/bash # 例 退出后容器不会关闭
# --name:为创建的容器命名
# -d 后台运行
#-i 参数表示以交互模式(Interactive Mode)运行容器允许你与容器的主进程进行交互
docker stop 容器名称
docker start 容器名称
docker rm 容器名称
docker rm `docker ps -aq` # 删除所有容器
docker inspect 容器名称
docker run -it --name=x2 -v 宿主机目录(文件):容器内目录(文件) 镜像
# 创建c3数据卷容器
docker run -it --name=c3 -v /volume 镜像 /bin/bash
# 创建c1、c2容器,使用--volumes-from设置数据卷
docker run -it --name=c1 --volumes-from c3 镜像 /bin/bash
docker run -it --name=c2 --volumes-from c3 镜像 /bin/bash
# 搜索镜像
docker search 应用镜像
# 拉取镜像
docker pull 应用镜像
# 创建容器,设置端口映射和数据卷映射,第一个80是宿主机端口号
docker run -id --name=xxx -p 80:80 -v 宿主机目录:容器内目录 应用镜像
# 将容器制作成镜像
docker commit 容器id/镜像名称:版本号
# 将镜像压缩
docker save -o 压缩文件名称 镜像名称:版本号
# 将压缩解为镜像
docker load -i 压缩文件名称
关键字 | 作用 | 备注 |
---|---|---|
FROM | 指定父镜像 | 指定dockerfile基于那个image构建 |
MAINTAINER | 作者信息 | 用来标明这个dockerfile谁写的 |
LABEL | 标签 | 用来标明dockerfile的标签 可以使用Label代替Maintainer 最终都是在docker image基本信息中可以查看 |
RUN | 执行命令 | 执行一段命令 默认是/bin/sh 格式: RUN command 或者 RUN ["command" , "param1","param2"] |
CMD | 容器启动命令 | 提供启动容器时候的默认命令 和ENTRYPOINT配合使用.格式 CMD command param1 param2 或者 CMD ["command" , "param1","param2"] |
ENTRYPOINT | 入口 | 一般在制作一些执行就关闭的容器中会使用 |
COPY | 复制文件 | build的时候复制文件到image中 |
ADD | 添加文件 | build的时候添加文件到image中 不仅仅局限于当前build上下文 可以来源于远程服务 |
ENV | 环境变量 | 指定build时候的环境变量 可以在启动的容器的时候 通过-e覆盖 格式ENV name=value |
ARG | 构建参数 | 构建参数 只在构建的时候使用的参数 如果有ENV 那么ENV的相同名字的值始终覆盖arg的参数 |
VOLUME | 定义外部可以挂载的数据卷 | 指定build的image那些目录可以启动的时候挂载到文件系统中 启动容器的时候使用 -v 绑定 格式 VOLUME ["目录"] |
EXPOSE | 暴露端口 | 定义容器运行的时候监听的端口 启动容器的使用-p来绑定暴露端口 格式: EXPOSE 8080 或者 EXPOSE 8080/udp |
WORKDIR | 工作目录 | 指定容器内部的工作目录 如果没有创建则自动创建 如果指定/ 使用的是绝对地址 如果不是/开头那么是在上一条workdir的路径的相对路径 |
USER | 指定执行用户 | 指定build或者启动的时候 用户 在RUN CMD ENTRYPONT执行的时候的用户 |
HEALTHCHECK | 健康检查 | 指定监测当前容器的健康监测的命令 基本上没用 因为很多时候 应用本身有健康监测机制 |
ONBUILD | 触发器 | 当存在ONBUILD关键字的镜像作为基础镜像的时候 当执行FROM完成之后 会执行 ONBUILD的命令 但是不影响当前镜像 用处也不怎么大 |
STOPSIGNAL | 发送信号量到宿主机 | 该STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。 |
SHELL | 指定执行脚本的shell | 指定RUN CMD ENTRYPOINT 执行命令的时候 使用的shell |
docker build -f dockerfile -t centos:7 .
-f :指定dockerfile 文件(默认不写的话指的是当前目录)
-t :镜像名和标签
title: Github添加ssh
date: 2024-04-11 18:10
updated: 2024-04-11 18:10
category: 技术杂谈
tags:
ssh-keygen -t rsa -b 4096 -C "${your_email}@example.com"
your_email 是你的邮箱名
Enter file in which to save the key (/c/Users/${your_user}/.ssh/id_rsa):
your_user 是你的用户名
/c/Users/${your_user}/.ssh/id_rsa already exists.
Overwrite (y/n)? y
your_user 是你的用户名
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
$ ssh -T [email protected]
// 输入你之前生成ssh密钥时输入的密码
Enter passphrase for key '/c/Users/${your_user}/.ssh/id_rsa':
your_user 是你的用户名
出现
Hi ${your_github_account}! You've successfully authenticated, but GitHub does not provide shell access.
your_github_account 是你的github账号
表示成功
title: HTTPS原理
author: yeguo
date: 2024-02-15 11:14
updated: 2024-02-15 11:14
category: 技术杂谈
tags:
在TLS/SSL握手过程中,ClientHello
和 ServerHello
消息本身通常不加密,它们是明文传输的。这是因为在握手的初步阶段,双方还没有建立起加密通道,因此初始的握手消息需要在明文中传递。
1.客户端Hello: 客户端在握手开始时向服务器发送ClientHello
消息,其中包含一个随机数(ClientRandom
),以及其他一些握手参数。
2.服务器Hello: 服务器在接收到ClientHello
后,向客户端发送ServerHello
消息,其中包含另一个随机数(ServerRandom
),以及选择的加密套件、协商的协议版本等信息。
ServerHello
阶段通常包含服务器的数字证书。数字证书中包含了服务器的公钥以及与之相关的信息,如证书颁发者、有效期等。这个数字证书是由服务器的证书链中的最顶层证书签发的,而这个最顶层的证书通常由一个受信任的证书颁发机构(CA,Certificate Authority
)签发。
客户端在收到
ServerHello
消息中的数字证书后,会验证证书的合法性,包括检查证书的签名是否有效、证书是否在有效期内、证书是否由受信任的CA签发等。验证通过后,客户端可以提取服务器的公钥,用于后续密钥协商和加密通信的建立。这个公钥通常用于协商会话密钥的加密
- CA验证: 客户端在收到服务器的证书后,需要验证服务器证书的有效性。这个验证过程包括以下几个步骤:
- 证书链验证: 客户端需要检查服务器证书是否是由可信的CA签发的。客户端本地存储了一组信任的CA的根证书,用于验证服务器证书链的合法性。
- 证书是否过期: 客户端检查服务器证书的有效期,确保它没有过期。
- 域名匹配: 客户端检查服务器证书中的域名是否与实际连接的域名匹配。这是为了防止中间人攻击。
- 公钥提取: 如果服务器证书通过了验证,客户端就可以从服务器证书中提取服务器的公钥。
- 密钥交换: 客户端使用服务器的公钥来加密一个
Pre-Master Secret
并发送给服务器。服务器使用自己的私钥解密这个消息,得到Pre-Master Secret
。
**3.生成Pre-Master Secret:**客户端使用这两个随机数以及其他一些握手参数,例如支持的加密算法、协商的协议版本等,生成一个共同的Pre-Master Secret
。
4.Pre-Master Secret的安全传输: 客户端将生成的Pre-Master Secret
通过服务器的公钥进行加密,然后发送给服务器。这一步使用了非对称加密,确保了Pre-Master Secret
在传输过程中的安全性。
5.Master Secret的生成算法: 一旦服务器收到已加密的Pre-Master Secret
并解密,客户端和服务器都使用以下算法计算Master Secre
t:
MasterSecret = PRF(PreMasterSecret, "master secret", ClientHello.random + ServerHello.random)
// "master secret"是PRF中的标识符,指示生成Master Secret。PRF 表示伪随机函数(Pseudo-Random Function)
6.Master Secret生成: 服务器收到已加密的Pre-Master Secret
后,使用自己的私钥进行解密,获取Pre-Master Secret
。然后,客户端和服务器都使用双方生成的随机数以及Pre-Master Secret
计算出一个共同的Master Secret
。
7.生成会话密钥: 通过Master Secret
再次使用密钥派生函数(PRF
或 HKDF
),生成最终用于对称加密通信的对称会话密钥。
伪随机函数(Pseudo-Random Function,PRF)是一种能够生成近似于真随机函数的算法,其输出表现为随机性。在SSL/TLS握手过程中,PRF的主要作用是生成密钥材料,例如生成Master Secret。
在SSL/TLS中,PRF的具体实现取决于协议版本。在TLS 1.2及之前的版本中,PRF的定义如下:
PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
secret
是Pre-Master Secret。label
是一个字符串标识符,用于区分生成不同的密钥材料。seed
是随机数。 P_MD5
和 P_SHA-1
是基于MD5和SHA-1散列函数的伪随机函数。S1和S2是通过拆分Pre-Master Secret而得到的两个半分。
在TLS 1.2中,PRF的计算方式得到了改变,引入了更灵活的PRF(secret, label, seed) = P_hash(secret, label + seed)
,其中P_hash
是一个基于HMAC(Hash-based Message Authentication Code)的伪随机函数,可以使用不同的散列算法,如SHA-256或SHA-384。
TLS 1.3进一步简化了密钥交换的流程,使用了更现代的密码学原语,并且取消了显式的PRF。在TLS 1.3中,密钥的生成主要通过HKDF(HMAC-based Extract-and-Expand Key Derivation Function)实现。
TLS 1.3中的HKDF算法会使用双方生成的随机数。在握手过程中,客户端和服务器各自生成一个随机数,并在握手消息中交换这些随机数。这些随机数用于增加握手过程的熵,从而提高密钥的随机性。
在HKDF的上下文中,这些随机数被作为输入之一,用于派生出最终的对称密钥。这样,不同的会话将有不同的随机数,导致生成不同的密钥,增强了密钥的唯一性和安全性。
TLS 1.3中使用的HKDF(HMAC-based Extract-and-Expand Key Derivation Function)算法是基于HMAC(Hash-based Message Authentication Code)的。HKDF包括两个步骤:Extract和Expand。
Extract: 在这一步,将原始输入(包括双方生成的随机数)通过一个伪随机函数(PRF)与salt(如果提供的话)进行HMAC运算,生成一个伪随机密钥(PRK,Pseudo-Random Key)。
公式表示为:PRK = HMAC-Hash(salt, input)
Expand: 利用PRK和其他信息(比如上下文标签,长度等),通过多次调用伪随机函数派生出最终的对称密钥。
公式表示为:OKM = HKDF-Expand(PRK, info, L)
其中,OKM
是输出密钥材料,HKDF-Expand
是一个用于扩展密钥材料的函数,info
包含了上下文标签和其他信息,L
是输出密钥材料的长度。
具体的哈希函数、PRF和其他参数的选择取决于协商时所选择的密码套件,HKDF是一个灵活的密钥派生方案,可以适应不同的密码学要求。
加密套件是TLS通信中用于协商加密算法、鉴别方法等参数的一组规范。在TLS握手过程中,客户端和服务器会协商选用一个加密套件,这个套件定义了一组加密算法、鉴别方法和其他相关参数。
TLS 1.3支持的一些加密套件包括:
这些套件包括了对称加密算法、鉴别算法和其他相关参数的组合。具体选用哪个套件取决于客户端和服务器双方的支持和协商结果。
数字证书是一种用于在网络中进行安全通信的电子证明书。它用于验证与证书相关联的实体(通常是一个网站或服务)的身份。数字证书的主要组成部分包括:
数字证书通过在通信中引入第三方(CA)的信任,提供了一种机制来确保通信的安全性和真实性。在HTTPS中,服务器通常会提供数字证书以证明其身份,同时客户端会验证证书的有效性。这样,双方可以协商出一个对称密钥,用于后续通信的加密。
数字签名是一种用于验证文档、消息或数据的完整性和来源真实性的技术。数字签名通过使用加密算法生成一个与特定数据相关联的数字代码(签名),并将其附加到原始数据上。这个数字签名可以被其他人用于验证数据的完整性和签名者的身份。
下面是数字签名的基本步骤:
数字签名的关键在于私钥只能由签名者持有,而公钥可以被分发给任何需要验证签名的人。这样,任何人都可以使用公钥验证数字签名,但只有持有相应私钥的实体才能生成有效的数字签名。
数字签名验证通常涉及以下步骤:
如果在这个过程中的任何一步失败,或者签名不匹配,那么验证将失败,表示消息可能已经被篡改或签名者并非预期的实体。
需要注意的是,数字签名的有效性与签名者的私钥和签名算法有关。只有持有相应私钥的实体才能生成有效的数字签名。
服务器通常会从数字证书颁发机构(CA,Certificate Authority)处获得数字证书。下面是简要的过程:
通常情况下,数字证书的有效期是有限的,因此在证书到期之前,服务器可能需要定期更新证书,通常是通过提交新的CSR给CA进行重新签发。
需要注意的是,一些大型的CA可能会向企业出售数字证书,而其他一些CA可能会提供免费的证书服务。选择CA通常取决于用例的需求和信任级别。
对于传统的服务器通信,服务器通常只需要一次性地获取证书,然后可以用该证书与多个客户端建立安全连接。证书是服务器身份的标识,用于证明服务器是可信的。一旦服务器获取了有效的数字证书,它就可以与多个客户端建立安全通信,而不需要为每个客户端单独申请证书。
这是因为数字证书是为特定的域名(或IP地址)签发的,并不是为特定的客户端。当客户端与服务器建立连接时,它会验证服务器的数字证书是否有效,以确保与服务器通信的安全性。因此,服务器只需要在初始设置中获取并配置好证书,之后便可以与多个客户端进行通信。
二、点击头像,选择Settings
三、选择左侧栏Developer Settings
五、生成新token
六、填个Note (随便填),只勾选repo即可,点击最下面Generate token
八、Github下载开源的PicGo
九、配置PicGo的GitHub设置
https://raw.githubusercontent.com/${your_github_account}/Images/main/pictures/xxx.png
https://cdn.jsdelivr.net/gh/GitHub账号名/仓库名
可以配置cdnhttps://cdn.jsdelivr.net/gh/
是 jsDelivr 的 CDN(内容分发网络)服务地址,可以用于托管 GitHub 仓库中的文件和资源。通过这个地址,你可以直接访问 GitHub 仓库中的文件,而无需使用 GitHub 网站或者 Git 工具PicGo文档
https://picgo.github.io/PicGo-Doc/
十、配置Typora自动自动上传
title: Java
author: yeguo
date: 2023-08-24 17:41
updated: 2023-08-24 17:41
category: 知识笔记
tags:
把给定对象添加到当前集合中
清空集合中所有元素
把给定的对象在当前集合中删除
判断当前集合中是否包含给定的对象
判断当前集合是否为空
返回集合中元素的个数
在此集合中的指定位置插入指定的元素
删除指定索引处的元素,返回被删除的元素
修改指定索引处的元素,返回被修改的元素
返回指定索引处的元素
返回集合中第一个元素
返回集合中最后一个元素
返回集合中严格大于给定元素的最小元素
返回集合中严格小于给定元素的最大元素
移除并返回集合中的第一个元素
移除并返回集合中的最后一个元素
在原迭代器基础上添加了可以添加元素方法add()
键唯一,值不一定
添加元素,返回被覆盖的元素
根据键删除键值对元素
移除所有的键值对元素
判断集合是否包含指定的键
判断集合是否包含指定的值
判断集合是否为空
集合的长度,也就是集合中键值对的个数
Collection中默认方法 default Stream stream() //创建流
无,先将键值对转为集合使用Collection的stream()方法 //创建流
使用Arrays工具类的静态方法stream() //创建流
使用Stream接口中的静态方法stream() //创建流
1.Streamfilter(Predicate<? super T> predicate) 过滤
2.Stream limit(long maxSize) 获取前几个元素
3.Stream skip(long n) 跳过前几个元素
4.Stream distinct() 元素去重,依赖(hashCode和equals方法)
5.static Stream concat(Stream a, Stream b) 合并a、b两个流为一个流
6.Stream map(Function<T,R> mapper) 转换流中的数据类型
注意:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
修改Stream流中的数据,不会影响原来集合或者数组中的数据
1.void forEach(consumer action) 遍历
2.long count() 统计
3.toArray() 收集流中数据放到数组中
4.collect(Collector collector) 收集流中数据,放到集合中
1.引用处必须是函数式接口
2.被引用的方法必须已经存在
3.被引用的方法的形参和返回值需要跟抽象方法保持一致
4.被引用方法的功能要满足当前需求
格式:类名::静态方法
范例:Integer::parseInt
格式:对象::成员方法
其他类:其他类对象::方法名
本类: this::方法名 // 静态方法中没有this,需要new一个本类的对象调用
父类: super:: 方法名 //静态方法中没有super ,需要new一个本类的对象调用
格式:类名::new
范例:Student::new
1.使用类名引用成员方法
格式:类名::方法名
范例:String::toUpperCase
规则:
1.需要有函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持 一致,返回值需要保持一致。
4,被引用方法的功能需要满足当前的需求
抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
在Stream流当中,第一个参数一般都表示流里面的每一个数据。
假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能 引用String这个类中的方法
第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
总结:如果抽象方法的第一个参数是A类型的只能引用A类中的方法,被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
2.引用数组的构造方法
格式:数据类型[]::new
范例:int[]::new
创建一个指定类型的数组
细节:数组的类型,需要跟流中数据的类型保持一致。
运行时异常和编译时异常的区别?
编译时异常:除了RuntimeExcpetion和他的子类,其他都是编译时异常
编译阶段需要进行处理,作用在于提醒程序员
运行时异常:RuntimeException本身和所有子类,都是运行时异常。编译阶段不报错, 是程序运行时出错的,一般是由于参数传递错误带来的问题
1.public String getMessage() 返回此throwable的详细消息字符串
2.public String toString() 返回此可抛出的简短描述
3.public void printStackTrace() 把异常的错误信息输出在控制台
delete方法细节
2.10流的作用?
3.O流按照流向可以分类哪两种流?
输出流:程序 --->文件
输入流:文件---->程序
4.IO流按照操作文件的类型可以分类哪两种流?
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件
5.什么是纯文本文件?
用windows.系统自带的记事本打开并且能读懂的文件
txt文件,md文件,ml文件,lrc文件等
默认为UTF-8
字符流原理解析
①创建字符输入流对象
底层:关联文件,并创建缓冲区(长度为8192的字节数组)
②读取数据
底层:1.判断缓冲区中是否有数据可以读取
2.缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满 缓冲区如果文件中也没有数据了,返回-1
3.缓冲区有数据:就从缓冲区中读取。
空参的read方法:一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
有参的read方法:把读取字节,解码,强转三步合并了,强转之后的字符放到数组中.
1.转换流的名字是什么?
2.转换流的作用是什么?
1.打印流有几种?各有什么特点?
title: Docker Compose
author: yeguo
date: 2023-07-28 11:00
updated: 2023-07-28 11:00
category: 技术杂谈
tags:
# Compose目前已经完全支持Linux、Mac OS和Windows,在我们安装Compose之前,需要先安装Docker。下面我 们以编译好的二进制包方式安装在Linux系统中。
curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 设置文件可执行权限
chmod +x /usr/local/bin/docker-compose
# 查看版本信息
docker-compose -version
# 二进制包方式安装的,删除二进制文件即可
rm /usr/local/bin/docker-compose
mkdir ~/docker-compose
cd ~/docker-compose
version: '3'
services:
nginx:
image: nginx
ports:
- 80:80
links:
- app
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
app:
image: app
expose:
- "8080"
mkdir -p ./nginx/conf.d
server {
listen 80;
access_log off;
location / {
proxy_pass http://app:8080;
}
}
docker-compose up
http://192.168.149.135/hello
123
132456465
title: Git常用命令
author: yeguo
date: 2023-07-26 16:01
updated: 2023-07-26 16:01
category: 技术杂谈
tags:
git config --global user.name 用户名
git config --global user.email 邮箱
git init // 初始化当前目录
git init 目录 //指定目录初始化
git status
git add 文件名
git commit -m "提交说明" 文件名
git reflog
git log //详细的历史记录
git reset --hard 版本号
git branch 分支名
git branch -v
git checkout 分支名
git merge 分支名
git merge 远程库链接或别名/分支 --allow-unrelated-histories
git merge git-demo/main --allow-unrelated-histories //例
git branch -m 旧名字 新名字
git remote add 别名 远程库链接
git remote -v
git push 别名/远程库链接 分支
git pull 别名/远程库链接 拉取分支
git fetch 别名/远程库链接 分支
git clone 远程库链接
title: Docker 私有仓库
author: yeguo
date: 2023-07-28 11:00
updated: 2023-07-28 11:00
category: 技术杂谈
tags:
# 1、拉取私有仓库镜像
docker pull registry
# 2、启动私有仓库容器
docker run -id --name=registry -p 5000:5000 registry
# 3、打开浏览器 输入地址http://私有仓库服务器ip:5000/v2/_catalog,看到{"repositories":[]} 表示私有仓库 搭建成功
# 4、修改daemon.json
vim /etc/docker/daemon.json
# 在上述文件中添加一个key,保存退出。此步用于让 docker 信任私有仓库地址;注意将私有仓库服务器ip修改为自己私有仓库服务器真实ip
{"insecure-registries":["私有仓库服务器ip:5000"]}
# 5、重启docker 服务
systemctl restart docker
docker start registry
# 1、标记镜像为私有仓库的镜像
docker tag centos:7 私有仓库服务器IP:5000/centos:7
# 2、上传标记的镜像
docker push 私有仓库服务器IP:5000/centos:7
#拉取镜像
docker pull 私有仓库服务器ip:5000/centos:7
title: Maven
date: 2023-08-30 11:40
updated: 2023-08-30 11:40
category: 知识笔记
tags:
将自己项目使用的依赖隐藏(不透明)
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<!--隐藏依赖,true-->
<optional>true</optional>
</dependency>
排除使用的依赖中使用的其他依赖,解决和自己项目的依赖冲突
<dependency>
<groupId>top.aidjajd</groupId>
<artifactId>aidjajd_project</artifactId>
<version>1.0-SNAPSHOT</version>
<!--排除依赖log4j-->
<exclusions>
<exclusion>
<!--写入排除坐标不需要写version-->
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
Scope | 主代码 | 测试代码 | 打包 | 范例 |
---|---|---|---|---|
compile(默认) | Y | Y | Y | log4j |
test | Y | junit | ||
provided | Y | Y | servlet-api | |
runtime | Y | jdbe |
间接依赖\直接-> | compile(默认) | test | provided | runtime |
---|---|---|---|---|
compile(默认) | compile | test | provided | runtime |
test | ||||
provided | ||||
runtime | runtime | test | provided | runtime |
Maven对项目构建的生命周期划分为3套
主要关注阶段:clean -> compile -> test -> package -> install
生命周期过程每一个阶段对应一个插件(plugin)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.