问题:pom文件不生效。
解决:Settings -->Bulid,Execution,Deployment–>BuildTools–>Maven–>IgnoredFiles 下把所有带勾的选线取消。
1.电商项目通用模板
1.1电商技术特点:
技术:单体架构-》微服务(dubbo+mycat)、范围:java go(服务端) python(数据分析)、分布式、高并发、集群、负载均衡、高可用、海量数据,业务复杂,系统安全
1.2电商模式属术语
B2B:指进行电子交易的供需双方都是商家。利用互联网完成交易。
C2C:指消费者个人之间的电子商务行为。
B2C、C2B同理。
O2O:线上转线下
F2C:厂商到消费者
B2B2C:电子商务类型的网络购物商业模式(天猫)
2.系统设计
2.1技术架构
后端需掌握:spring+spring mvc+linux+mysql,mongodb+http/tcp+多线程+分布式架构+弹性计算架构,微服务架构+java性能优化+项目管理等
前端需掌握:html5+css3+vuejs+webpack+nodejs+Google V8引擎+javascript多线程+模块化+面向切面编程+设计模式+浏览器兼容+性能优化等
前后端交互:json
前端技术栈:vue.js+node.js+lua+html5+elementUI+theamleaf
后端技术栈:spring boot+spring cloud+spring security\oauth2/jwt+mysql/mycat(库表分离,读写分离)+redis+fastdfs+rabbitmq+定时任务+短信+支付+263邮件
数据持久化:mybatis plus+spring-data-redis+spring-data-elasticsearch
运维:cannal(监控)、docker(部署)、fastDFS(分布式存储)、eureka集群、redis集群、es集群(搜索)
2.2系统架构
3.框架搭建
3.1虚拟机准备
3.2项目结构说明
gateway:可以将综合逻辑相关的服务用网关路由组合到一起,还可以鉴权和限流
service:存放独立的微服务工程
service_API:对应工程的JavaBean、Feign、以及Hystrix配置,该工程主要对外提供依赖
transaction_fascar:分布式事务模块,将分布式事务抽取到该工程中,任何工程如需要使用分布式事务,只需依赖该工程即可。
web:web服务工程,如需调用多个微服务,可以将他们写入该模块,
4.商品微服务-品牌增删改查
创建商品微服务,实现对品牌表的增删改查功能。
总结:
1.restful风格
前后端交互中使用json传输数据,数据的增删改查使用http的不同方法(get,post,put,delete)
2.crud
使用tk.mybatis包实现自动操作类似jpa
3.分页
使用pageHelper工具实现分页
4.分布式文件存储
4.1跨域解决方案CORS
问题:什么是跨域?
浏览器的同源策略,导致不能向其他域发送异步请求
同源策略:具有相同的协议,主机和端口号
出于浏览器的同源策略限制。同源策略是一种约定,他是浏览器最核心的安全功能,如果缺少同源策略,则浏览器的正常功能可能会收到影响,同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(同一个域)就是两个页面具有相同的协议,主机和端口号
跨域问题:浏览器的同源策略限制,会报错,由于我们采用前后端分离的编程方式,前后端必定存在跨域问题,解决跨域问题可以采用CORS。
BV12Y411x7zX:P29集
4.2CORS(跨域资源共享)简介
条件:IE10以上
本质:请求头增加一个参数(Access-Control-Request-Handers:access-control-allow-origin),开启跨域请求。实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨域通信。
怎么做:SpringMVC4.2版本后,只需在Controller类上添加注解@CrossOrigin就可以了
4.3.规格参数与分类管理、表关系
分类:衣物-裙子-细项
模板:裙子模板
参数:胸围、腰围、臀围
规格:大小、颜色
品牌:etc
4.3.1规格参数管理及需求分析
略
4.5.通用mapper自定义方法
4.5.1根据商品分类名称查询品牌列表(需求)
4.5.2根据商品分类名称查询规格列表(需求)
5.分布式文件存储FastDFS(大文件使用HDFS)
5.1FastDFS简介(适合小又多的小文件)
开源轻量级分布式文件系统,对文件进行管理,功能有:文件存储、文件同步、文件访问(上传,下载)等。
FastDFS架构包括Tracker Server和Storage Server。客户端Tracker进行上传下载,通过调度最终由Storage Server完成
tracker:元数据信息,文件上传时间,地址,大小,记录storage集群信息。负责文件管理、负载均衡操作,即控制中心(注册中心)
storage:存数据。负责文件操作,文件上传、下载、修改、删除等
5.2上传流程
客户端上传文件后存储服务器将文件id返回,此文件id用于之后索引信息。文件索引包括:组名,虚拟磁盘路径,数据两级目录,文件名。
eg:field:group1/M00/00/00assadsadasdasd.png
组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存
虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01
数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件
文件名:与文件上传时不同,由于存储服务器根据特定信息生成,文件名包括:源存储服务器IP地址,文件创建时间戳,文件大小,随机数和文件扩展名等信息。
5.3FastDFS搭建
百度
拉取tracker:
docker pull morunchang/fastdfs
运行tracker:
docker run -d --name tracker --net=host morunchang/fasfdfs sh tracker.sh
运行storage:
docker run -d --name storage --net=host -e TRACKER_IP=>{ip:port} -e GROUP_NAME={group name} morunchang/fastdfs sh storage.sh
修改nginx配置
进入storage,修改nginx.conf
docker exec -it storage /bin/storage /bin/bash
进入后
vi /data/nginx/conf/nginx.conf
location /group1/M00{
proxt_next_upstream http+502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key $uri$is_args$args;
proxy_pass http://fdfs_group1;
expires 30d;
}
退出:exit
重启: docker restart storage
5.4文件存储微服务
6.微服务网关限流&鉴权
导航:gateway系统搭,限流实现,BCrypt实现加密与验证,JWT微服务鉴权
6.1微服务网关Gateway过滤+路由
不使用网关架构:
缺点:
客户端多次请求不同的微服务,增加复杂性
存在跨域请求,在一定场景下相对复杂
认证复杂,每个服务都要独立认证
难以重构,项目迭代可能需要重新划分微服务。
使用网关架构:
优点:
安全,只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护
易于监控,可以在网关手机监控数据并将其推送到外部系统进行分析
易于统一认证鉴权。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无需在每个微服务中进行认证。
减少客户端与各个微服务之间的交互次数。
总结:微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控相关功能。
实现微服务网关的技术有很多:
nginx:一个高性能的HTTP和反向代理web服务器,同时提供了IMAP/POP3/SMTP服务
zuul:基于JVM路由和服务端负载均衡器(不维护)
spring-cloud-gateway:spring的网关项目,集成断路器,路径重写,性能比zuul好。
6.2网关微服务搭建
路由功能:
网关访问:http://localhost:9101/goods/brand/category/手机
uri:/goods/brand/category/手机
商品微服务:http://localhost:9001/brand/category/手机
6.3微服务网关跨域
修改application.yml,在springcloudgateway节点添置配置:
spring:
application:
name: sysgateway
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': #匹配所有请求
allowedOrigins: "*" #跨域处理,允许所有的域
allowedMethods: #支持的方法
- GET
- POST
- PUT
- DELETE
routes:
- id: goods
uri: lb://goods #转发请求至lb(loadbalance:负载均衡)goods微服务
predicates:
- Path=/goods/** #拦截请求
fliter:
- StripPrefix = 1 #截取 /goods
- id: system
uri: lb://system
predicates:
-Path=/system/**
fliter:
- StripPrefix = 1
redis:
host: 127.0.0.1
server:
port: 9101
eureka:
client:
server-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true
6.4微服务网关过滤器
我们可以通过网关过滤器,实现一些逻辑处理,实现ip黑白名单拦截,特定地址拦截,实现两个过滤器,并设定先后顺序。
需求:1.IP拦截 2.危险操作日志打印
6.5网关限流
限流:当系统被频繁的请求时候,就有可能崩溃,需在每一个微服务中做限流操作
网关限流:在网关系统做限流
未达到限流:放行
到达限流:过载(CODE:429)
限流算法:
1.令牌桶算法
所有的请求在处理之前都需要拿到一个可用的令牌才能被处理;根据限流大小,设置按照一定的速率往桶里添加令牌;桶设置最大的放置令牌限制,当桶满时,新添加的令牌就被丢弃或者拒绝;请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;令牌桶有最低限制,当令牌桶中的令牌达到最低限额时,请求处理完之后将不会删除令牌,以此保证足够的限流。
实现:Guava、redis
2.漏桶算法
3.计数算法
需求:使用redis实现,每个ip地址1s内只能发送1次请求goods,多出来的请求返回429错误。
导包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
添加
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
/**
* 令牌桶限制逻辑
*/
@Bean
public KeyResolver ipKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
//限制ip
String ip = exchange.getRequest().getRemoteAddress().getHostString();
return Mono.just(ip);
}
};
}
}
配置
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': #匹配所有请求
allowedOrigins: "*" #跨域处理,允许所有的域
allowedMethods: #支持的方法
- GET
- POST
- PUT
- DELETE
routes:
- id: goods
uri: lb://goods #转发请求至lb(loadbalance:负载均衡)goods微服务
predicates:
- Path=/goods/** #拦截请求
fliter:
- StripPrefix = 1 #截取 /goods
- name: RequestRateLimit #请求数限流 名字不能随便写
args:
key-resolver: "#{@ipKeyResolver}" #启动类中桶方法的名称
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
redis-rate-limiter.burstCapacity: 1 #令牌桶总容量
6.6加密算法
BCrypt快速入门:比MD5安全,内部引入加盐机制。
//把明文转密文
String password = admin.getPassword();
String hashpw = BCrypt.hashpw(password, BCrypt.gensalt());
admin.setPassword(hashpw);
6.7管理员登录密码验证
String password = admin.getPassword();
//1.查询数据库对象
Admin queryAdmin = new Admin();
queryAdmin.setLoginName(admin.getLoginName());
queryAdmin.setStatus(1);
Admin dbAdmin = adminMapper.selectOne(queryAdmin);
//2.对比
if (dbAdmin == null) {
return false;
} else {
return BCrypt.checkpw(password, dbAdmin.getPassword());
}
6.8加密算法进阶
6.8.1可逆加密算法
加密后,密文可以反向解密得到密码原文。
6.8.1.1对称加密
数据发送方将明文和加密密钥一起加密成加密密文后发给接收方,接收方收到后需使用加密时的密钥以及相同的解密算法的逆算法对密文进行解密,才能使其恢复成可读明文。
对称加密算法中,使用的密钥只有一个,收发双方都使用这个密钥。就需要双方都知道加密密钥。
优点:对称加密算法的优点是算法公开,计算量小,加密速度快,加密效率高
缺点:没有非对称加密安全
用途:一般用于保存用户手机号,身份证等敏感但能解密的信息
常见的对称加密算法有:AES、DES、3DES、Blowfish、IDEA、RA4、RC5、HS256
6.8.1.2非对称加密
两把钥匙,公钥和私钥,公钥加密,私钥解密。私钥秘密保存,公钥下发给信任客户端。
加密与解密:
私钥加密:持有私钥或公钥才能解密
公钥加密:持有私钥才可解密
签名:私钥签名,持有公钥进行验证是否被篡改过
优点:非对称加密与对称加密相比,安全性更好
缺点:加密时间长,速度慢,适合少量数据加密
用途:一般用于签名和认证。私钥服务器保存,用来加密,公钥客户拿着用于对于令牌或者签名的解密或者校验使用
常见的非对称加密:RSA、DSA、ECC、RS256
6.8.2不可逆加密算法
一旦加密就不能反向解密得到密码原文
种类:Hash加密算法、散列算法、摘要算法
用途:一般用于校验下载文件的正确性,一般在网站上下载文件都能见到;存储用户敏感信息,如密码,卡号等不可解密的信息。
常见不可逆加密算法:MD5 SHA HMAC
6.8.3Base64编码
Base64编码是传出8Bit字节代码的编码方式之一。Base64编码可用于在HTTP环境下传递比较长的标识信息,采用Base64编码解码具有不可读性,即所编码的数据不会被人看到。
注意:Base64只是一种编码方式,不算加密方法
7.JWT实现微服务鉴权
7.1什么是微服务鉴权
使用网关在系统中比较适合进行权限校验。
单点登录的特点:
1.认证系统为独立系统
2.各子系统通过Http或其他协议与认证系统通信,完成用户认证
3.用户身份信息存储在Redis集群
Java中有很多用户认证的框架实现单点登录:Apache Shiro、CAS、Spring Sercurity CAS
7.2JWT
JSON Web Token 规范,允许用户在服务器间传递安全可靠信息。
JWT由三部分组成:头,载荷和签名
头部:用于描述基本信息,例如签名算法等。
载荷:存放有效信息地方。
签名:签证信息,这个部分需要base64加密后的header和base64加密后的payload使用.链接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
将三部分用.连接成一个完整的字符串,构成了最终的jwt
7.3JJWT签发与验证token
JJWT是一个提供端到端的JWT创建和验证的java库。
创建token
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
Date date = new Date(System.currentTimeMillis());
JwtBuilder jwtBuilder = Jwts.builder()
.setId("1") //设置令牌id
.setSubject("topic") //主题
.setIssuedAt(new Date()) //签发时间
.setExpiration(date)
.signWith(SignatureAlgorithm.HS256, "mall"); //签名与密钥
String jwt = jwtBuilder.compact();
解析token
Claims claims = Jwts.parser().setSigningKey("mall").parseClaimsJws(jwt).getBody(); //加上密钥
System.out.println(claims);
自定义claims
Date date = new Date(System.currentTimeMillis());
JwtBuilder jwtBuilder = Jwts.builder()
.setId("1") //设置令牌id
.setSubject("topic") //主题
.setIssuedAt(new Date()) //签发时间
.setExpiration(date) //过期时间
.claim("name","xuy") //自定义信息
.signWith(SignatureAlgorithm.HS256, "mall"); //签名与密钥
String jwt = jwtBuilder.compact();
Claims claims = Jwts.parser().setSigningKey("mall").parseClaimsJws(jwt).getBody();
System.out.println(claims);
7.4微服务鉴权代码实现
7.4.1分析
1.用户进入网关开始登录,网关过滤器进行判断,如果是登陆,则路由到后台管理微服务进行登录
2.用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
3.用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN
4.网关过滤器解析TOKEN,判断是否有权限,有放行,没有返回未认证错误
7.4.2系统微服务签发token
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
public class JwtUtil {
//有效期
public static final Long JWT_TTL = 3600000L; //60*60*1000
//设置密钥明文
public static final String JWT_KEY = "mall";
/**
* 创建token
*/
public static String createJwt(String id, String subject, Long ttlMillis){
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if(ttlMillis == null){
ttlMillis = JwtUtil.JWT_TTL; //不传 默认1小时
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
SecretKey secreKey = generalKey();
JwtBuilder builder = Jwts.builder()
.setId(id) //唯一id
.setSubject(subject) //主题 可以是JSON数据
.setIssuer("admin") //签发者
.setIssuedAt(now) //签发时间
.signWith(signatureAlgorithm, secreKey) //使用HS256对称加密算法签名,第二个参数为密钥
.setExpiration(expDate);//设置过期时间
return builder.compact();
}
/**
* 生成加密后的密钥 secretKey
*/
private static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
}
@PostMapping("/login")
public Result login(@RequestBody Admin admin) { //@RequestBody把请求体的数据转成对象
boolean flag = adminService.login(admin);
if (flag) {
//返回前端map username token
Map<String, String> map = new HashMap<>();
map.put("username",admin.getLoginName());
String jwt = JwtUtil.createJwt(UUID.randomUUID().toString(),admin.getLoginName(),null);
map.put("token",jwt);
return new Result(true, StatusCode.OK, "success",map);
} else {
return new Result(true, StatusCode.LOGINERROR, "flase");
}
}
7.4.3网关过滤器验证token
/**
* Jwt校验工具类
*/
public class JwtUtil {
//有效期
public static final Long JWT_TTL = 3600000L; //60*60*1000
//设置密钥明文
public static final String JWT_KEY = "mall";
/**
* 生成加密后的密钥 secretKey
*/
public static SecretKey generalKey() {
byte[] decode = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKeySpec key = new SecretKeySpec(decode, 0, decode.length, "AES");
return key;
}
/**
* 解析
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
}
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
private static final String AUTHORIZE_TOKEN = "token";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//登录
String path = request.getURI().getPath();
if (path.contains("admin/login")){
//放行
return chain.filter(exchange);
}
//访问资源
HttpHeaders headers = request.getHeaders();
String jwt = headers.getFirst(AUTHORIZE_TOKEN);
//没有jwt 拒绝
if(StringUtils.isEmpty(jwt)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//有jwt 校验
try {
JwtUtil.parseJWT(jwt);
}catch (Exception e){
//logger.warn("");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
8.商品管理
导航:snowflake生成id,商品增删改查实现,审核上下架
8.1分布式ID生成解决方案
多模块微服务高并发的情况下采用分库分表时主键生成成为问题,所以需要生成唯一ID。
8.1.1UUID方式(注意是String类型,占256位)
常见方式,可以利用数据库也可以利用程序生成,一般来说全球唯一。
优点:简单,生成id性能好,全球唯一,适合数据合并迁移
缺点:没有排序,UUID使用字符串存储,查询效率低,存储空间大,传输数据量大,不可读
8.1.2Redis方式
依赖Redis单线程,生成全局唯一的ID。用Redis的原子操作INCR和INCRBY来实现。
优点:不依赖于数据库,性能优与数据库,数字ID天然排序,对分页或者需要排序的结果有帮助
缺点:引入redis组件增加系统复杂度,需要编码工作量,网络传输造成性能下降
8.1.3开源snowflake 64bit算法方式
twitter开源的分布式ID生成算法,64位bit数字,结果是Long类型的ID,其核心是使用41bit作为毫秒数,10bit作为机器的ID(5bit是数据中心,5bit是机器ID),12bit作为毫秒内的流水号,最后一位符号位是0
优点:毫秒数在高位,自增序列在低位,整个ID趋势递增,不依赖数据库第三方系统,以服务的方式部署,稳定性高,生成ID性能也高,可以根据自身业务分配bit位。
缺点:强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
8.2snowflake快速入门
common下导入IdWorker工具类。
8.3新增和修改商品
8.3.1SPU和SKU
概念:SPU(Standard Product Unit标准产品单位)是商品信息聚合的最小单位,是一组可复用,易检索的标准化信息的集合,该集合描述了一个产品的特性。
描述:属性值,特性相同的货品就是一个SPU eg:华为P50就是一个SPU
概念:SKU(Stock keeping unit 库存量单位)即库存进出计量的单位,可以是件,盒,物理上不可分割的最小存货单元,在使用时要根据不同状态,不同管理模式来处理。
描述:在服装类普遍使用 eg:华为P50 红色64G
8.3.2新增修改接口开发
8.3.3品牌与分类关联
将分类Id与SPU的品牌ID一起插入到tb_category_brand中
8.3.4根据ID查询是商品
根据id查询SPU和SKU列表
8.3.5保存修改
8.4商品审核与上下架
需求:
1.新增商品审核状态为0,默认为下架状态
2.审核商品,需要校验是否被删除的商品,如果未删除则修改审核状态为1,并自动上架
3.下架商品,需要校验是否是被删除的商品,如果未删除则修改上架状态为0
4.上架商品,需要审核状态为1,如果为1,则更改上下架状态为1
8.5删除与还原商品
需求:
商品列表中删除并非真正删除,而是设置删除字段为1,在回收站中有还原商品的功能,并将删除字段为0,在回收站有真正删除的方法,将数据从数据库删除