今天开始打算搭建个人网站,至于做什么网站,想了很久,还是搭建新闻类和电商类吧,用到的技术多点,可以练练手。
前端技术:Vue+ElementUi
后端技术:SpringCloud AliBaBa+MySql+MyCat+Kafka+Elasticsearch+Hbase+MyBatis+爬虫+JWT
部署技术: Docker+Nginx
当然上面这些技术有些还不会,那就一起来慢慢更新吧!
我打算分10个博客搭建完我的个人主页,预计耗时2个月!
主页后台管理系统的登录
先从前端项目搭建开始吧!
1.安装node
2.安装nrm淘宝镜像
3.安装webpack
4.安装vue cli3.0版本
5.通过 Vue 脚手架创建项目admin-web
6. 配置 Vue 路由
7 .配置 Element-UI 组件库 element ui文档路径
8 .配置 axios 库
准备后端
SpringCloud Alibaba之Nacos
1.https://nacos.io/en-us/docs/quick-start-docker.html官网下载nacos docker
下载地址
2.修改example下的standalone-mysql-8.yaml(单机版)
version: "2"
services:
nacos:
image: nacos/nacos-server:latest
container_name: nacos-standalone-mysql
env_file:
- ../env/nacos-standlone-mysql.env
volumes:
- ./plugins/mysql/:/home/nacos/plugins/mysql/
- ./standalone-logs/:/home/nacos/logs
- ./init.d/custom.properties:/home/nacos/init.d/custom.properties
ports:
- "8081:8848"
- "9555:9555"
depends_on:
- mysql
restart: on-failure
mysql:
container_name: mysql
image: nacos/nacos-mysql:8.0.16
env_file:
- ../env/mysql.env
volumes:
- ./mysql:/var/lib/mysql
ports:
- "3307:3306"
3.mkdir ~/nacos
4.cd ~/nacos
5.把git下载的目录copy进来,cd nacos-docker/
6.docker-compose -f ~/nacos/nacos-docker/example/standalone-mysql-8.yaml up -d
7.docker-compose -f ~/nacos/nacos-docker/example/standalone-mysql-8.yaml logs -f 查看日志
8.鉴于华为云有很多端口用不了,我才做映射的。访问http://139.159.232.120:8081/nacos/可查看
9.建议使用standalone-derby.yaml(用mysql8已踩坑,与其他数据库冲突端口33060)
镜像拉取
docker pull nacos/nacos-server:1.1.3
docker run --name nacos -d -p 8848:8848 --privileged=true --restart=always -e JVM_XMS=512m -e JVM_XMX=2048m -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /home/nacos/logs:/home/nacos/logs nacos/nacos-server:1.1.3
试用Nacos
1.创建user项目并勾选AliBaBa
2.springBoot版本2.2.6对应SpringCloud Hoxton版本,对应SpringCloudAliBaBa 2.2.0版本
此处采用springBoot版本2.1.6,SpringCloud Greenwich版本,SpringCloudAliBaBa 0.9.0版本
3.配置文件优先级:bootstrap.properties>bootstrap.yml>application.properties>application.yml
4.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jiechuang</groupId>
<artifactId>user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<!--<springboot.version>2.1.6.RELEASE</springboot.version>-->
<!--<spring-cloud-alibaba.version>0.9.0.RELEASE</spring-cloud-alibaba.version>-->
<!--<spring-cloud.version>Greenwich</spring-cloud.version>-->
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.9.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--引入springboot的web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入springboot的web-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入配置中心阿里巴巴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--引入注册中心阿里巴巴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--sentinel-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
</project>
5.配置文件放到nacos(**配置文件名称一定要以yaml结尾,不同版本nacos注册地址key不同)
在启动时配置激活dev
可以切换多环境并可以动态读取配置文件
注解:
EnableDiscoveryClient 服务发现 主方法上
RefreshScope 动态读取配置文件 使用到配置文件的地方(Controller)
EnableFeignClients 开启feign功能 主方法上
FeignClient 远程调用 接口上
6.创建order项目并暴露接口
试用Sentinel
1.下载sentinal1.6.2的jar包
下载地址
2.mkdir ~/sentinael
3.cd sentinael
4.上传jar包
5.编写dockerFile文件
FROM java:8
MAINTAINER yuhaojie <yuhaojie_spring@163.com>
ADD sentinel-dashboard-1.6.2.jar sentinel.jar
CMD java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -jar sentinel.jar
6.打镜像
docker build -f ~/sentinael/sentinel_dockerfile -t sentinel:1.0 .
7.创建容器
docker run -itd --name=c_sentinel -p 8888:8888 sentinel:1.0
8.查看日志
docker logs c_sentinel
9.修改配置文件
配置sentinel的dashboard地址,并开启sentinel
10.实现feign接口的fallback
重启关闭服务端,调用消费端
失败,原因:feign.sentinel.enabled=true;上图配置少了d;修复后如图
JWT鉴权
头部区域放置元数据,
载荷区域放置有效信息
签名部分放置头编码+载荷编码+盐的算法生成:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret)
实战:
1.创建common项目
2.导入依赖
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
<scope>provided</scope>
</dependency>
3.修改user配置文件
4.编写工具类
@SuppressWarnings("all")
@ConfigurationProperties(prefix = "jwt.config")
@Data
public class JWTUtil {
//盐
@Value("${jwt.config.sault}")
private String sault;
//令牌有效时间,单位是毫秒(默认1分钟到期)
private String jwtTime;
//创建令牌
public String createJWT(String id, String subject, List<Integer> roleIds){
//当前时间戳
long now = System.currentTimeMillis();
JwtBuilder builder = Jwts.builder()
.setId(id)
.setSubject(subject)
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, sault)
.claim("roleIds", roleIds);//自定义属性
if(Long.valueOf(jwtTime)>0){
builder.setExpiration(new Date(now+Long.valueOf(jwtTime)));
}
return builder.compact();
}
//解析令牌
public Claims parseJWT(String token){
return Jwts.parser()
.setSigningKey(sault)
.parseClaimsJws(token)
.getBody();
}
}
5.注入到user服务
6.创建gateway工程
<dependencies>
<!--引入配置中心阿里巴巴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--引入注册中心阿里巴巴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--引入网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
spring:
cloud:
nacos:
discovery:
server-addr: 139.159.232.120:8081
gateway:
routes:
- id: user
uri: lb://user-dev
predicates:
- Path=/jiechuang_user/**
filters:
- StripPrefix=1
- Authorize
- id: login
uri: lb://user-dev
predicates:
- Path=/login/**
discovery:
locator:
enabled: true
servlet:
multipart:
max-file-size: 2048MB
max-request-size: 200MB
resolve-lazily: true
server:
port: 9092
jwt:
config:
sault: ZHANGJIE
jwtTime: 600000
以/jiechuang_user/**开头的请求都会转发到uri为lb://user-dev的地址上,
lb://user-dev(注册中心中服务的名称)即user-dev服务的负载均衡地址,
并用StripPrefix的filter 在转发之前将/jiechuang_user去除
不拦截/login开头,也不去除login
@Component
@Slf4j
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Autowired
private JWTUtil jwtUtil;
private static final String AUTHORIZATION_TOKEN = "Authorization";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
String authorization = exchange.getRequest().getHeaders().getFirst(AUTHORIZATION_TOKEN);
if (StringUtils.hasLength(authorization)) {
//对令牌认证
try {
jwtUtil.parseJWT(authorization);
return chain.filter(exchange);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("令牌不正确,请重新登录");
}
}
log.error("验证不通过,请重新登录");
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
};
}
}
7.登录方法生成token返回给前端,前端添加全局axios拦截器
axios.interceptors.request.use(config => {
// console.log(config)
config.headers.Authorization = window.sessionStorage.getItem('token')
// 在最后必须 return config
return config
})
8.自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@Documented
public @interface JWTPermission {
String role() default "all";
}
9.解析器
@Aspect
@Component
@Slf4j
public class JWTPermissionParse {
//public static final ThreadLocal<LogRecord> THREAD_LOCAL=new ThreadLocal<>();
//日志service注入
@Autowired
private JWTUtil jwtUtil;
//JWTPermission
@Pointcut(value = "@annotation(jwtPermission)")
public void cut(JWTPermission jwtPermission){
}
//进入切点前封装对象进ThreadLocal
@Before(value = "cut(jwtPermission)")
public void doBefore(JoinPoint joinPoint, JWTPermission jwtPermission){
if(!"all".equals(jwtPermission)){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String authorization = request.getHeader("Authorization");
try {
Claims claims = jwtUtil.parseJWT(authorization);
List<String> roleCodes = (List<String>) claims.get("roleCodes");
if(CollectionUtils.isEmpty(roleCodes)){
throw new NoPermissionException("无权限");
}
if(!roleCodes.contains(jwtPermission.role())&&!roleCodes.contains("admin")){
throw new NoPermissionException("无权限");
}
} catch (io.jsonwebtoken.ExpiredJwtException e){
e.printStackTrace();
throw new RuntimeException("令牌有效期已到,请重新登录");
}catch (NoPermissionException e) {
e.printStackTrace();
throw new NoPermissionException(e.getMessage());
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("令牌不正确,请重新登录");
}
}
}
}
跨域问题解决
纠结了两个小时解决gateway跨域问题,配置文件不起作用,最后请教前端同事配置代理
devServer: {
proxy: {
'/jiechuang_user': {
target: PROXY_URL
},
'/login': {
target: PROXY_URL
}
}
},
PROXY_URL指向gateway地址
部署前端时代理不生效解决方案(Nginx)
# user 指定运行 nginx 的用户和组(第一个参数为用户第二个为组,这里只有用户)
#user nobody;
# 指定工作进程数(一般设置为CPU核数)
worker_processes 1;
# 指定错误日志为 logs/ 目录下的 error.log 文件
#error_log logs/error.log;
# 指定错误日志,并指定写入格式为 notice
#error_log logs/error.log notice;
# 指定错误日志,并指定写入格式为 info
#error_log logs/error.log info;
# 指定 pid 文件(存放主进程 pid 号)
#pid logs/nginx.pid;
# nginx 连接配置模块
events {
# 指定每个工作进程最大连接数为 1024
worker_connections 1024;
}
# http 配置模块
http {
# 通过 include 加载 mime.types 文件,里面的 types {} 模块将文件扩展名映射到响应的 MIME 类型
include mime.types;
# 定义响应的默认 MIME 类型
default_type application/octet-stream;
# 写入格式 main 的内容格式如下
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
# 指定访问日志和写入格式为 main
#access_log logs/access.log main;
# 启用或者禁用 sendfile()
sendfile on;
# 启用或者禁用使用套接字选项(仅在 sendfile 使用时使用)
#tcp_nopush on;
# 0 值禁用保持活动的客户端连接
#keepalive_timeout 0;
# 65 s 超时
keepalive_timeout 65;
# 启用或者禁用 gzip
#gzip on;
# 虚拟主机配置模块
server {
# 监听 80 端口
listen 80;
# 监听域名为 localhost
server_name localhost;
# 将指定的 charset 添加到 “Content-Type” 响应头字段。如果此charset与source_charset指令中指定的charset不同,则执行转换。
#charset koi8-r;
# 指定该虚拟主机的访问日志
#access_log logs/host.access.log main;
# 将特定的文件或目录重新定位,如 php 文件,image 目录等
location / {
# 设置请求的根目录
root /usr/share/nginx/html;
# 定义索引,按顺序匹配
index index.html index.htm;
}
location /login {
# 设置请求的根目录
root /usr/share/nginx/html;
# 定义索引,按顺序匹配
index index.html index.htm;
proxy_pass http://139.159.232.120:9092/login;
}
location /jiechuang_user {
# 设置请求的根目录
root /usr/share/nginx/html;
# 定义索引,按顺序匹配
index index.html index.htm;
proxy_pass http://139.159.232.120:9092/jiechuang_user;
}
# 定义显示 404 错误的 uri
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
# location 精准匹配 '/50x.html'
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
# 正则表达式匹配 php 文件
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# 设置 FastCGI 服务器的地址。地址可以指定为一个域名或 IP 地址,以及一个端口
# fastcgi_pass 127.0.0.1:9000;
# 设置将在以斜杠结尾的URI之后追加的文件名,
# fastcgi_index index.php;
# 设置一个应该传递给FastCGI服务器的参数。
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# 加载 conf/fastcgi_params 文件
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
# ssl 配置,要启用 ssl 模块需要在编译 nginx 时加上 --with-http_ssl_module 参数
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}