第2-2-2章 常见组件与中台化-常用组件服务介绍-通用权限系统
2. 常用组件服务介绍
全套代码及资料全部完整提供,点此处下载
https://download.csdn.net/download/weixin_42208775/86960899
2.1 通用权限系统
2.1.1. 功能概述
对于企业中的项目绝大多数都需要进行用户权限管理、认证、鉴权、加密、解密、XSS防跨站攻击等。这些功能整体实现思路基本一致,但是大部分项目都需要实现一次,这无形中就形成了巨大的资源浪费。本项目就是针对这个问题,提供了一套通用的权限解决方案----品达通用权限系统。
品达通用权限系统基于SpringCloud(Hoxton.SR1) +SpringBoot(2.2.2.RELEASE) 的微服务框架,具备通用的用户管理、资源权限管理、网关统一鉴权、XSS防跨站攻击等多个模块,支持多业务系统并行开发,支持多服务并行开发,可以作为后端服务的开发脚手架。核心技术采用SpringBoot、Zuul、Nacos、Fegin、Ribbon、Hystrix、JWT Token、Mybatis Plus等主要框架和中间件。
本项目具有两个主要功能特性:
-
用户权限管理
具有用户、部门、岗位、角色、菜单管理,并通过网关进行统一的权限认证
-
微服务开发框架
本项目同时也是一个微服务开发框架,集成了基础的公共组件,包括数据库、缓存、日志、表单验证、对象转换、防注入和接口文档管理等工具。
系统架构:
技术架构:
工程结构:
项目服务有两个:网关服务和权限服务
应用 | 端口 | 说明 | 启动命令 |
---|---|---|---|
pd-gateway | 8760 | 网关服务 | java -jar pd-gateway.jar & |
pd-auth-server | 8764 | 权限服务 | java -jar pd-auth-server.jar & |
本系统是基于当前非常流行的前后端分离开发方式开发。
环境要求:
-
JDK : 1.8 +
-
Maven: 3.3 +
-
Mysql: 5.6.0 +
-
Redis: 4.0 +
-
Nacos: 1.3.0
-
Node: 11.3+(集成npm)
2.1.2. 应用场景
1、新项目开发
如果是新项目开发,可以在品达通用权限系统的基础上进行相关的业务开发,其实就是将通用权限系统当做开发脚手架在此基础之上快速开始业务开发。
2、已有项目集成
对于已经开发完成的项目,可以直接集成通用权限系统进行权限管理、认证鉴权。
2.1.3. 重点代码介绍
网关服务:
BaseFilter:基础网关过滤器
TokenContextFilter:解析token中的用户信息过滤器
AccessFilter:权限验证过滤器
权限服务:
LoginController:登录控制器
MenuController:菜单管理控制器
ResourceController:资源管理控制器
RoleController:角色管理控制器
UserController:用户管理控制器
OrgController:组织管理控制器
StationController:岗位管理控制器
2.1.4. 使用说明
2.1.4.1 新项目开发
如果是新项目开发,可以在品达通用权限系统的基础上进行相关的业务开发,其实就是将通用权限系统当做开发脚手架在此基础之上快速开始业务开发。
本小节通过一个商品服务的案例来讲解如何基于品达通用权限系统进行新业务的开发。
2.1.4.1.1 数据库环境搭建
创建数据库pd_goods并创建表pd_goods_info:
2.1.4.1.2 后端业务功能开发
2.1.4.1.2.1 创建工程
在品达通用权限系统基础上创建商品服务相关模块,如下图:
pd-goods #商品服务父工程
├── pd-goods-entity #实体
├── pd-goods-server #服务
2.1.4.1.2.2 pd-goods-entity开发
第一步:配置pom.xml
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pd-goods</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pd-goods-entity</artifactId>
<description>接口服务实体模块</description>
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-common</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-core</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
</dependency>
</dependencies>
</project>
第二步:创建商品实体类
package com.itheima.pinda.goods.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.itheima.pinda.base.entity.Entity;
import lombok.*;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("pd_goods_info")
public class GoodsInfo extends Entity<Long> {
private static final long serialVersionUID = 1L;
/**
* 商品编码
*/
private String code;
/**
* 商品名称
*/
private String name;
/**
* 国条码
*/
private String barCode;
/**
* 品牌表id
*/
private Integer brandId;
/**
* 一级分类id
*/
private Integer oneCategoryId;
/**
* 二级分类id
*/
private Integer twoCategoryId;
/**
* 三级分类id
*/
private Integer threeCategoryId;
/**
* 商品的供应商id
*/
private Integer supplierId;
/**
* 商品售价价格
*/
private BigDecimal price;
/**
* 商品加权平均成本
*/
private BigDecimal averageCost;
/**
* 上下架状态:0下架,1上架
*/
private boolean publishStatus;
/**
* 审核状态: 0未审核,1已审核
*/
private boolean auditStatus;
/**
* 商品重量
*/
private Float weight;
/**
* 商品长度
*/
private Float length;
/**
* 商品重量
*/
private Float height;
/**
* 商品宽度
*/
private Float width;
/**
* 颜色
*/
private String color;
/**
* 生产日期
*/
private LocalDateTime productionDate;
/**
* 商品有效期
*/
private Integer shelfLife;
/**
* 商品描述
*/
private String descript;
}
第三步:创建商品操作对应的多个DTO类
package com.itheima.pinda.goods.dto;
import com.itheima.pinda.goods.entity.GoodsInfo;
import lombok.*;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class GoodsInfoPageDTO extends GoodsInfo {
private LocalDateTime startCreateTime;
private LocalDateTime endCreateTime;
}
package com.itheima.pinda.goods.dto;
import com.itheima.pinda.goods.entity.GoodsInfo;
public class GoodsInfoSaveDTO extends GoodsInfo {
}
package com.itheima.pinda.goods.dto;
import com.itheima.pinda.goods.entity.GoodsInfo;
public class GoodsInfoUpdateDTO extends GoodsInfo {
}
2.1.4.1.2.3 pd-goods-server开发
第一步:配置pom.xml文件
<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pd-goods</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pd-goods-server</artifactId>
<description>接口服务启动模块</description>
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-log</artifactId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-swagger2</artifactId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-validator</artifactId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-xss</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-databases</artifactId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-tools-dozer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>pd-goods-entity</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
第二步:导入资料中提供的配置文件
第三步:在配置中心Nacos中创建pd-goods-server.yml
配置文件内容如下:
# 在这里配置 权限服务 所有环境都能使用的配置
pinda:
mysql:
database: pd_goods
swagger:
enabled: true
docket:
core:
title: 核心模块
base-package: com.itheima.pinda.goods.controller
server:
port: 8767
第四步:编写启动类
package com.itheima.pinda;
import com.itheima.pinda.validator.config.EnableFormValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.net.InetAddress;
import java.net.UnknownHostException;
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients(value = {
"com.itheima.pinda",
})
@EnableTransactionManagement
@Slf4j
@EnableFormValidator
public class GoodsServerApplication {
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext application = SpringApplication.run(GoodsServerApplication.class, args);
Environment env = application.getEnvironment();
log.info("\n----------------------------------------------------------\n\t" +
"应用 '{}' 运行成功! 访问连接:\n\t" +
"Swagger文档: \t\thttp://{}:{}/doc.html\n\t" +
"----------------------------------------------------------",
env.getProperty("spring.application.name"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"));
}
}
第五步:导入资料中提供的配置类
第六步:创建Mapper接口
package com.itheima.pinda.goods.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.pinda.goods.entity.GoodsInfo;
import org.springframework.stereotype.Repository;
/**
* Mapper 接口
*/
@Repository
public interface GoodsInfoMapper extends BaseMapper<GoodsInfo> {
}
第七步:创建Service接口和实现类
package com.itheima.pinda.goods.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.pinda.goods.entity.GoodsInfo;
public interface GoodsInfoService extends IService<GoodsInfo> {
}
package com.itheima.pinda.goods.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.pinda.goods.dao.GoodsInfoMapper;
import com.itheima.pinda.goods.entity.GoodsInfo;
import com.itheima.pinda.goods.service.GoodsInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class GoodsInfoServiceImpl extends ServiceImpl<GoodsInfoMapper, GoodsInfo> implements GoodsInfoService {
}
第八步:创建Controller
package com.itheima.pinda.goods.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.pinda.base.BaseController;
import com.itheima.pinda.base.R;
import com.itheima.pinda.base.entity.SuperEntity;
import com.itheima.pinda.database.mybatis.conditions.Wraps;
import com.itheima.pinda.database.mybatis.conditions.query.LbqWrapper;
import com.itheima.pinda.dozer.DozerUtils;
import com.itheima.pinda.goods.dto.GoodsInfoPageDTO;
import com.itheima.pinda.goods.dto.GoodsInfoSaveDTO;
import com.itheima.pinda.goods.dto.GoodsInfoUpdateDTO;
import com.itheima.pinda.goods.entity.GoodsInfo;
import com.itheima.pinda.goods.service.GoodsInfoService;
import com.itheima.pinda.log.annotation.SysLog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@Validated
@RestController
@RequestMapping("/goodsInfo")
@Api(value = "GoodsInfo", tags = "商品信息")
public class GoodsInfoController extends BaseController {
@Autowired
private DozerUtils dozer;
@Autowired
private GoodsInfoService goodsInfoService;
/**
* 分页查询商品信息
*
* @param data 分页查询对象
* @return 查询结果
*/
@ApiOperation(value = "分页查询商品信息", notes = "分页查询商品信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "current", value = "当前页", dataType = "long", paramType = "query", defaultValue = "1"),
@ApiImplicitParam(name = "size", value = "每页显示几条", dataType = "long", paramType = "query", defaultValue = "10"),
})
@GetMapping("/page")
@SysLog("分页查询商品信息")
public R<IPage<GoodsInfo>> page(GoodsInfoPageDTO data) {
Page<GoodsInfo> page = getPage();
LbqWrapper<GoodsInfo> wrapper = Wraps.lbQ();
wrapper.like(GoodsInfo::getName, data.getName())
.like(GoodsInfo::getCode, data.getCode())
.eq(GoodsInfo::getBarCode, data.getBarCode())
.geHeader(GoodsInfo::getCreateTime, data.getStartCreateTime())
.leFooter(GoodsInfo::getCreateTime, data.getEndCreateTime())
.orderByDesc(GoodsInfo::getCreateTime);
goodsInfoService.page(page, wrapper);
return success(page);
}
@ApiOperation(value = "查询商品信息", notes = "查询商品信息")
@GetMapping("/list")
@SysLog("查询商品信息")
public R<List<GoodsInfo>> list(GoodsInfoPageDTO data) {
LbqWrapper<GoodsInfo> wrapper = Wraps.lbQ();
wrapper.like(GoodsInfo::getName, data.getName())
.like(GoodsInfo::getCode, data.getCode())
.eq(GoodsInfo::getBarCode, data.getBarCode())
.geHeader(GoodsInfo::getCreateTime, data.getStartCreateTime())
.leFooter(GoodsInfo::getCreateTime, data.getEndCreateTime())
.orderByDesc(GoodsInfo::getCreateTime);
return success(goodsInfoService.list(wrapper));
}
/**
* 查询商品信息
*
* @param id 主键id
* @return 查询结果
*/
@ApiOperation(value = "查询商品信息", notes = "查询商品信息")
@GetMapping("/{id}")
@SysLog("查询商品信息")
public R<GoodsInfo> get(@PathVariable Long id) {
return success(goodsInfoService.getById(id));
}
/**
* 新增商品信息
*
* @param data 新增对象
* @return 新增结果
*/
@ApiOperation(value = "新增商品信息", notes = "新增商品信息不为空的字段")
@PostMapping
@SysLog("新增商品信息")
public R<GoodsInfo> save(@RequestBody @Validated GoodsInfoSaveDTO data) {
GoodsInfo GoodsInfo = dozer.map(data, GoodsInfo.class);
goodsInfoService.save(GoodsInfo);
return success(GoodsInfo);
}
/**
* 修改商品信息
*
* @param data 修改对象
* @return 修改结果
*/
@ApiOperation(value = "修改商品信息", notes = "修改商品信息不为空的字段")
@PutMapping
@SysLog("修改商品信息")
public R<GoodsInfo> update(@RequestBody @Validated(SuperEntity.Update.class) GoodsInfoUpdateDTO data) {
GoodsInfo GoodsInfo = dozer.map(data, GoodsInfo.class);
goodsInfoService.updateById(GoodsInfo);
return success(GoodsInfo);
}
/**
* 删除商品信息
*
* @param ids 主键id
* @return 删除结果
*/
@ApiOperation(value = "删除商品信息", notes = "根据id物理删除商品信息")
@SysLog("删除商品信息")
@DeleteMapping
public R<Boolean> delete(@RequestParam("ids[]") List<Long> ids) {
goodsInfoService.removeByIds(ids);
return success();
}
}
2.1.4.1.3 配置网关路由规则
在Nacos中的pd-gateway.yml中新增商品服务相关的路由配置,内容如下:
zuul:
# debug:
# request: true
# include-debug-header: true
retryable: false
servlet-path: / # 默认是/zuul , 上传文件需要加/zuul前缀才不会出现乱码,这个改成/ 即可不加前缀
ignored-services: "*" # 忽略eureka上的所有服务
sensitive-headers: # 一些比较敏感的请求头,不想通过zuul传递过去, 可以通过该属性进行设置
# prefix: /api #为zuul设置一个公共的前缀
# strip-prefix: false #对于代理前缀默认会被移除 故加入false 表示不要移除
routes: # 路由配置方式
authority: # authority是路由名称,可以随便定义,但是path和service-id需要一一对应
path: /authority/**
serviceId: pd-auth-server
goods:
path: /goods/**
serviceId: pd-goods-server
2.1.4.1.4 前端开发
可以将pinda-authority-ui作为前端开发脚手架,基于此工程开发商品服务相关页面。资料中已经提供了开发完成的前端工程,直接运行即可。
2.1.4.1.5 配置菜单和资源权限
启动网关服务、权限服务、商品服务、前端工程,使用管理员账号登录,配置商品服务相关的菜单和对应的资源权限。
goodsInfo:add 新增 POST /goodsInfo
goodsInfo:update 修改 PUT /goodsInfo
goodsInfo:delete 删除 DELETE /goodsInfo
goodsInfo:view 列表 GET /goodsInfo/page
2.1.4.1.6 配置角色
启动网关服务和权限服务,使用管理员账号登录。创建新角色并进行配置(菜单权限和资源权限)和授权(为用户分配角色)。
2.1.4.2 已有项目集成
本小节通过一个已经完成开发的TMS(品达物流)项目来展示如何进行已有项目集成的过程。
2.1.4.2.1 TMS调整
2.1.4.2.1.1 页面菜单
对于已经完成相关业务开发的项目,可以将其前端系统的页面通过iframe的形式内嵌到通用权限系统的前端页面中,这就需要对其前端系统的页面进行相应的修改。因为原来的TMS系统前端页面的左侧菜单和导航菜单都在自己页面中展示,现在需要将这些菜单配置到通用权限系统中,通过权限系统的前端系统来展示。
2.1.4.2.1.2 请求地址
为了能够进行鉴权相关处理,需要将TMS前端发送的请求首先经过通用权限系统的网关进行处理:
2.1.4.2.2 网关路由配置
配置通用权限系统的网关路由规则,将针对TMS的请求转发到TMS相关服务:
zuul:
retryable: false
servlet-path: /
ignored-services: "*" # 忽略eureka上的所有服务
sensitive-headers: # 一些比较敏感的请求头,不想通过zuul传递过去, 可以通过该属性进行设置
routes: # 路由配置方式
authority:
path: /authority/**
serviceId: pd-auth-server
pay:
path: /pay/**
serviceId: pd-ofpay-server
web-manager:
path: /web-manager/**
serviceId: pd-web-manager
web-xczx:
path: /xczx/api/**
url: http://xc-main-java.itheima.net:7291/api/
2.1.4.2.3 通用权限系统配置
2.1.4.2.3.1 菜单配置
登录通用权限系统,配置TMS项目相应的菜单:
2.1.4.2.3.2 资源权限配置
资源权限都是关联到某个菜单下的,所以要配置资源权限需要先选中某个菜单,然后就可以配置相关资源权限了:
2.1.4.2.3.3 角色配置
登录通用权限系统,在角色管理菜单中配置TMS项目中使用到的角色:
角色创建完成后可以为角色配置菜单权限和资源权限:
完成角色的菜单权限和资源权限配置后可以将角色授权给用户:
全套代码及资料全部完整提供,点此处下载
https://download.csdn.net/download/weixin_42208775/86960899