spring-boot重头再来 6
文章目录
分布式理论
分布式系统是建立在网络之上的软件系统,是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
RPC远程过程调用
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
RPC两个核心模块:通讯,序列化。
Zookeeper安装
由于我经常用的系统依然是windows系统,所以我们在windows环境下安装zookeeper
下载地址:Apache ZooKeeper
这里我安装的是3.4.14版本
-
下载tar.gz解压包,下载后解压
-
解压后,此时直接运行/bin/zkServer.cmd是无法成功运行的(闪退),没有zoo.cfg配置文件
-
这时我们需要修改zoo.cfg配置文件,可是我们conf文件夹下并没有zoo.cfg文件,那该怎么办?我们将zoo_sample.cfg复制一份改名为zoo.cfg,并修改其中的dataDir属性,可见原来是/tmp,这一看就是linux特有的目录位置,我们将其修改为./
# The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. # dataDir=/tmp/zookeeper dataDir=./ # the port at which the clients will connect clientPort=2181 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=1
-
启动/bin/zkServer.cmd,这时应该可以成功运行,至少我的可以
-
使用zkCli.cmd测试
-
运行/bin/zkCli.cmd
-
输入
ls /
-
-
输入
create -e /biang henniubi
![在这里插入图片描述](https://img-blog.csdnimg.cn/63fc75163afb4a50b7508dc6ac153b84.png#pic_center)
-
输入
get /biang
-
输入
ls /
测试成功!
Dubbo
官方文档 Apache Dubbo
Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架
Apache Dubbo |ˈdʌbəʊ| 提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。
dubbo-admin安装
dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。
但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,不过这个监控即使不装也不影响使用。
-
第一步自然是下载
地址 :https://github.com/apache/dubbo-admin/tree/master
-
打包
解压进入目录
dubbo-admin-master
运行cmd命令
mvn clean package -Dmaven.test.skip=true
当然啦,像我就运行命令失败了,出现了版本不对应的错误
Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.0:compile
(造成这个错误的原因不只是版本不对应,只是我刚好是这个问题)这时候我们可以使用我们的idea来打开dubbo-admin-master文件夹,然后利用IDEA强大的maven功能,
使用生命周期->package功能来进行打包
dubbo-admin运行与测试
-
到dubbo-admin\target 目录下,执行dubbo-admin-0.0.1-SNAPSHOT.jar,也就是执行下列语句
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
由于配置
dubbo-admin\src\main\resources \application.properties
中,dubbo.registry.address属性中默认指定的分布式应用程序协调服务是zookeeper,所以zookeeper服务需要先行打开,不然上述的语句运行后会报错。 -
访问
localhost:7001
默认账号密码是root root
- 输入密码后进入dubbo-admin界面
安装完成!
SpringBoot + Dubbo + zookeeper
框架搭建
-
启动zookeeper (运行zookeeper文件夹下的 \bin\zkServer.cmd)
-
新建空项目,再在其中新建新springboot模块 provider-server
-
导入springweb,zookeeper ,dubbo的依赖
<!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.3</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient --> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency> <!-- 引入zookeeper --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.7.0</version> </dependency>
-
在新模块中新建service包并新建新建Service PokemonService
package com.example.service.impl; import com.example.service.PokemonService; import org.apache.dubbo.config.annotation.Service; import org.springframework.stereotype.Component; /** * @author BIANG * @Date 2021/7/23 21:13 */ //值得注意的是这个Service是Dubbo包下的不是springboot的 @Service //为了和上面的Dubbo包下的Service区分,所以这里使用万能的Component @Component public class PokemonServiceImpl implements PokemonService { /** * 获取Pikachu字符串 * * @return Pikachu */ @Override public String getPikachu() { return "Pikachu"; } }
-
配置模块的properties
# 应用名称 spring.application.name=provider-server # 应用服务 WEB 访问端口 server.port=8081 #当前应用名字 dubbo.application.name=provider-server #注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 #扫描指定包下服务 dubbo.scan.base-packages=com.example.service
-
运行主程序 没有报错
-
运行Dubbo(到dubbo/dubbo-admin/target下 执行
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
),并访问localhost:7001
,查看到对应的提供者
-
同样的新建一个消费者模块,导入同样的依赖
-
先配置properties
# 应用名称 spring.application.name=consumer-server # 应用服务 WEB 访问端口 server.port=8082 #当前应用名字 dubbo.application.name=consumer-server #注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181
-
接下来就是编写service
package com.example.service.impl; import com.example.service.PokemonService; import com.example.service.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; /** * @author BIANG * @Date 2021/7/24 1:17 */ @Service public class UserServiceImpl implements UserService { @Reference PokemonService pokemonService; /** * 获取宝可梦 */ @Override public void getPokemon(){ String ticket = pokemonService.getPikachu(); System.out.println("得到"+ticket+"啦"); } }
显然,两个类处于不同项目,java是没办法直接获取到PokemonService类的,这时我们可以通过两种方法来解决
-
pom坐标(很遗憾我并不知道是什么东西)
-
按照全类名进行匹配,也就是将提供者的接口搬到消费者项目中的同个路径
-
测试
在消费者项目中编写测试类
package com.example;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ConsumerServerApplicationTests {
@Autowired
UserService userService;
@Test
void contextLoads() {
userService.getPokemon();
}
}
成功运行
Spring Security
简介
在 Web 开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。
Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。
用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
实战基本环境配置
-
新建项目,导入spring web依赖、thymeleaf依赖
-
随便写点html、css和放点js,包括
-
static
-
css
-
base.css
* { margin: 0; padding: 0; } body { background-color: #666666; } .w { margin: 0 auto; } .center { position: absolute; top: calc(50% - 150px); left: calc(50% - 250px); } .login{ width: 500px; height: 300px; } .login-content{ width: 485px; height: 300px; border-radius: 20px; border:5px solid transparent; background-clip:padding-box,border-box; background-origin:padding-box,border-box; background-image:linear-gradient(#5A5A5A,#5A5A5A),linear-gradient(#3c5cef, #b45b9b); text-align: center; } .login-title { width: 100%; height: 65px; padding-top: 20px ; line-height: 65px; font-family: 微软雅黑,serif; text-align: center; color: azure; font-size: 24px; font-weight: 100; } .login-input-frame { width: 80%; height: 60px; text-align: center; } .login-input { display: inline-block; width: 60%; height: 30px; margin: 15px 0; text-align: center; outline:none; border-radius: 10px; border:2px solid transparent; transition: width 1s ease 0s; background-clip:padding-box,border-box; background-origin:padding-box,border-box; background-image:linear-gradient(rgba(90,90,90,.9),rgba(90,90,90,.9)),linear-gradient(#3c5cef, #b45b9b); color: white; } .login-input:hover, .login-input:focus { width: 80%; transition: width 1s ease 0s; } .placeholder-light:hover::-webkit-input-placeholder{ color: rgba(255,255,255,.3); } .login-button-frame { width: 80%; height: 95px; text-align: center; } .login-button { position: relative; width: 30%; height: 30px; margin: 17px 0 47px 0; border:2px solid transparent; background-clip:padding-box,border-box; background-origin:padding-box,border-box; background-image:linear-gradient(rgba(90,90,90,.9),rgba(90,90,90,.9)),linear-gradient(#3c5cef, #b45b9b); color: rgba(255,255,255,.5); transition: color .5s; z-index: 0; } .login-button:before{ content: ''; position: absolute; left: 0; top: 0; right: 0; bottom: 0; background-image:linear-gradient(rgba(90,90,90,.9),rgba(90,90,90,.9)),linear-gradient(#5476fd, #ff82d9); opacity: 0; transition: opacity .5s; z-index: -1; } .login-button:hover { color: rgba(255,255,255,.8); } .login-button:hover::before { opacity: 1; } .login-to-button { position: relative; display: inline-block; width: 40%; padding:17.94% 0; margin: calc((300px - 40%) * 0.5) 0; text-align: center; line-height: 0; color: rgba(255,255,255,.5); border:10px solid transparent; border-radius: 50%; background-clip:padding-box,border-box; background-origin:padding-box,border-box; background-image:linear-gradient(rgba(90,90,90,.9),rgba(90,90,90,.9)),linear-gradient(#3c5cef, #b45b9b); font-size: 36px; transition: color .5s; z-index: 0; } .login-to-button:before{ content: ''; position: absolute; left: 0; top: 0; right: 0; bottom: 0; background-image:linear-gradient(#5476fd, #ff82d9),linear-gradient(rgba(90,90,90,.9),rgba(90,90,90,.9)); border-radius: 50%; opacity: 0; transition: opacity .5s; z-index: -1; } .login-to-button:hover { color: rgba(255,255,255,.8); } .login-to-button:hover::before { opacity: 1; } a { color: #888888; text-decoration: none; transition: color .5s; } a:hover { color: white; } .information{ width: 90%; margin: 3px auto; border-radius: 10px; border:2px solid transparent; background-clip:padding-box,border-box; background-origin:padding-box,border-box; background-image:linear-gradient(#5A5A5A,#5A5A5A),linear-gradient(#3c5cef, #b45b9b); text-align: center; line-height: 33px; color: white; } .level { position: absolute; width: 40%; top: calc(50% + 160px); left: 30%; }
-
-
-
templates
-
index.html
<!DOCTYPE html> <html lang="zh_CN" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <meta charset="UTF-8"> <title>首页</title> <!-- <link rel="stylesheet" href="../static/base.css">--> <link rel="stylesheet" th:href="@{/css/base.css}"> </head> <body> <div class="center login " th:fragment="nav-menu"> <div class="login-content w" sec:authorize="!isAuthenticated()"> <button class="login-to-button"> <a th:href="@{/login}">登录</a> </button> </div> <div class="login-content w" sec:authorize="isAuthenticated()"> <div class="information"> 用户名:<span sec:authentication="principal.username"></span> </div> <div class="information"> 角色:<span sec:authentication="principal.authorities"></span> </div> <div class="information" sec:authorize="hasRole('vip1')"> <div>VIP1</div> <span><a th:href="@{/level1(id=1)}">Level-1-1</a></span> <span><a th:href="@{/level1(id=2)}">Level-1-2</a></span> <span><a th:href="@{/level1(id=3)}">Level-1-3</a></span> </div> <div class="information" sec:authorize="hasRole('vip2')"> <div>VIP2</div> <span><a th:href="@{/level2(id=1)}">Level-2-1</a></span> <span><a th:href="@{/level2(id=2)}">Level-2-2</a></span> <span><a th:href="@{/level2(id=3)}">Level-2-3</a></span> </div> <div class="information" sec:authorize="hasRole('vip3')"> <div>VIP3</div> <span><a th:href="@{/level3(id=1)}">Level-3-1</a></span> <span><a th:href="@{/level3(id=2)}">Level-3-2</a></span> <span><a th:href="@{/level3(id=3)}">Level-3-3</a></span> </div> </div> </div> </body> </html>
-
views
-
level1
-
1.html
<!DOCTYPE html> <html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>Level-1-1</title> <link rel="stylesheet" th:href="@{/css/base.css}"> </head> <body> <!--主容器--> <div class="ui container"> <div th:replace="~{index::nav-menu}"></div> <div class="information level" style="text-align: center"> <h3>Level-1-1</h3> </div> </div> </body> </html>
-
2.html
-
3.html
-
-
level2
- 1.html
- 2.html
- 3.html
-
level3
- 1.html
- 2.html
- 3.html
-
login.html
<!DOCTYPE html> <html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>登录</title> <link rel="stylesheet" th:href="@{/css/base.css}"> </head> <body> <div class="login center"> <div class="login-content w"> <form th:action="@{/loginCheck}" method="post"> <h3 class="login-title ">登录</h3> <div class="login-input-frame w"> <label> <input type="text" class="login-input placeholder-light " name="username" placeholder="请输入用户名"> </label> </div> <div class="login-input-frame w"> <label> <input type="password" class="login-input placeholder-light password" name="password" placeholder="请输入密码"> </label> </div> <div class="login-button-frame w"> <input type="submit" class="login-button" value="登录"/> </div> </form> </div> </div> </body> </html>
-
-
其他地方没什么要求,就不上代码啦
-
-
新建controller包以及RouterController
package com.example.springbootnine.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; /** * @author BIANG * @date 2021/7/24 17:30 */ @Controller public class RouterController { @RequestMapping({"/","/index"}) public String index(){ return "index"; } @RequestMapping("/login") public String toLogin(){ return "views/login"; } @GetMapping("/level1") public String level1(@RequestParam("id") int id){ System.out.println(id); return "views/level1/"+id; } @GetMapping("/level2") public String level2(@RequestParam("id") int id){ System.out.println(id); return "views/level2/"+id; } @GetMapping("/level3") public String level3(@RequestParam("id") int id){ System.out.println(id); return "views/level3/"+id; } }
-
测试一下,可以访问成功即可
添加入Spring Security
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理
记住几个类:
- WebSecurityConfigurerAdapter:自定义Security策略
- AuthenticationManagerBuilder:自定义认证策略
- @EnableWebSecurity:开启WebSecurity模式
-
首先我们需要添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency>
值得一提的是,下面的依赖是thymeleaf推出的配合springsecurity5的依赖,可以在thymeleaf中直接查找springsecurity5相关的各种属性
-
新建config包,新建配置类SecurityConfig
package com.example.springbootnine.config; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * @author BIANG * @date 2021/7/25 0:26 */ // 开启WebSecurity模式 @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 定制请求的授权规则 // 首页所有人可以访问 http.authorizeRequests().antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); // 没有权限默认会到登录页面,需要开启登录的页面 // /login页面 http.formLogin() .usernameParameter("username") .passwordParameter("password") .loginPage("/login") .loginProcessingUrl("/loginCheck"); //注销,开启了注销功能,跳到首页 //http.logout().logoutSuccessUrl("/"); // 防止网站工具:get,post http.csrf().disable();//关闭csrf功能,登录失败肯定存在的原因 //开启记住我功能: cookie,默认保存两周,自定义接收前端的参数 http.rememberMe().rememberMeParameter("remember"); } //定义认证规则 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //这些数据正常应该中数据库中读 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("biang").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3") .and() .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3") .and() .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1"); } }
这里面的
/loginCheck
对应 登录表单的action,/login
对应 发送登录表单的url -
运行主程序,成功按需求访问