华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录

今天开始打算搭建个人网站,至于做什么网站,想了很久,还是搭建新闻类和电商类吧,用到的技术多点,可以练练手。
前端技术: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 库
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第1张图片
准备后端

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)
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第2张图片
镜像拉取

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不同)
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第3张图片
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第4张图片
在启动时配置激活dev
可以切换多环境并可以动态读取配置文件
注解:
EnableDiscoveryClient 服务发现 主方法上
RefreshScope 动态读取配置文件 使用到配置文件的地方(Controller)
EnableFeignClients 开启feign功能 主方法上
FeignClient 远程调用 接口上
6.创建order项目并暴露接口
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第5张图片

试用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
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第6张图片
9.修改配置文件
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第7张图片
配置sentinel的dashboard地址,并开启sentinel
10.实现feign接口的fallback
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第8张图片
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第9张图片
重启关闭服务端,调用消费端
失败,原因:feign.sentinel.enabled=true;上图配置少了d;修复后如图

华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第10张图片

JWT鉴权

华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第11张图片
头部区域放置元数据,
载荷区域放置有效信息
签名部分放置头编码+载荷编码+盐的算法生成:

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配置文件
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第12张图片
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服务
华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第13张图片
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 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("令牌不正确,请重新登录");
            }
        }
    }

}

华为云搭建余浩杰的个人网站(新闻+电商+空间)01-主页后台管理系统的登录_第14张图片

跨域问题解决

纠结了两个小时解决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;
    #    }
    #}

}

你可能感兴趣的:(docker,java,vue)