品达通用权限系统(Day 7~Day 8)

2024-02-12 07:59
文章标签 系统 通用 权限 day 品达

本文主要是介绍品达通用权限系统(Day 7~Day 8),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

19. 网关服务开发

pd-gateway作为通用权限系统的网关服务,前端的http请求首先需要经过网关服务处理,再通过网关服务的路由功能转发到权限服务或者其他微服务进行业务处理。我们可以在网关服务进行统一的jwt令牌解析、鉴权相关操作。

19.1 配置文件

19.1.1 bootstrap.yml

由于我们当前使用的是Nacos作为整个项目的配置中心,所以Spring Boot的大部分配置文件都在Nacos中进行统一配置,我们的项目中只需要按照Spring Boot的要求在resources目录下提供bootstrap.yml配置文件即可,文件内容如下:

pinda:# docker部署时,需要指定, 表示运行该服务的宿主机IPlocal-ip: ${LOCAL_IP:${spring.cloud.client.ip-address}}   nacos:ip: ${NACOS_IP:@pom.nacos.ip@}port: ${NACOS_PORT:@pom.nacos.port@}namespace: ${NACOS_ID:@pom.nacos.namespace@}spring:main:allow-bean-definition-overriding: trueapplication:name: @project.artifactId@ #pd-gatewayprofiles:active: @pom.profile.name@ #devcloud:nacos:config:server-addr: ${pinda.nacos.ip}:${pinda.nacos.port}file-extension: ymlnamespace: ${pinda.nacos.namespace}shared-dataids: common.yml,redis.yml,mysql.ymlrefreshable-dataids: common.ymlenabled: truediscovery:server-addr: ${pinda.nacos.ip}:${pinda.nacos.port}namespace: ${pinda.nacos.namespace}metadata:management.context-path: ${server.servlet.context-path:}${spring.mvc.servlet.path:}${management.endpoints.web.base-path:}#http://localhost:8760/api/gate/actuator
19.1.2 logback-spring.xml

由于pd-gateway已经添加了pd-tools-log模块的依赖,所以可以在项目中使用logback记录日志信息。在resources目录下提供logback-spring.xml配置文件,Spring Boot默认就可以加载到,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration><include resource="com/itheima/pinda/log/logback/pinda-defaults.xml"/><springProfile name="test,docker,prod"><logger name="com.itheima.pinda.zuul" additivity="true" level="INFO"><appender-ref ref="ASYNC_CONTROLLER_APPENDER"/></logger></springProfile><springProfile name="dev"><logger name="com.itheima.pinda.zuul" additivity="true" level="INFO"><appender-ref ref="CONTROLLER_APPENDER"/></logger></springProfile>
</configuration>
19.1.3 j2cache配置文件

在当前pd-gateway项目中会使用到j2cache来操作缓存,在Nacos配置中心的redis.yml中已经配置了j2cache的相关配置:

j2cache:#  config-location: /j2cache.propertiesopen-spring-cache: truecache-clean-mode: passiveallow-null-values: trueredis-client: lettucel2-cache-open: true# l2-cache-open: false     # 关闭二级缓存broadcast: net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy#  broadcast: jgroups       # 关闭二级缓存L1:provider_class: caffeineL2:provider_class: net.oschina.j2cache.cache.support.redis.SpringRedisProviderconfig_section: lettucesync_ttl_to_redis: truedefault_cache_null_object: falseserialization: fst
caffeine:properties: /j2cache/caffeine.properties   # 这个配置文件需要放在项目中
lettuce:mode: singlenamespace:storage: genericchannel: j2cachescheme: redishosts: ${pinda.redis.ip}:${pinda.redis.port}password: ${pinda.redis.password}database: ${pinda.redis.database}sentinelMasterId:maxTotal: 100maxIdle: 10minIdle: 10timeout: 10000

通过上面的配置可以看到,还需要在项目中提供/j2cache/caffeine.properties,文件内容如下:

#########################################
# Caffeine configuration
# \u6682\u65F6\u6CA1\u7528
# [name] = size, xxxx[s|m|h|d]
#########################################
default=2000, 2h
resource=2000, 1h
19.1.4 密钥文件
JWT签名算法中,一般有两个选择:HS256和RS256。HS256 (带有 SHA-256 的 HMAC )是一种对称加密算法, 双方之间仅共享一个密钥。由于使用相同的密钥生成签名和验证签名, 因此必须注意确保密钥不被泄密。RS256 (采用SHA-256 的 RSA 签名) 是一种非对称加密算法, 它使用公共/私钥对: JWT的提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。

本项目中使用RS256非对称加密算法进行签名,这就需要使用RSA生成一对公钥和私钥。在授课资料中已经提供了一对公钥和私钥,其中pub.key为公钥,pri.key为私钥。

前面我们已经提到,在当前网关服务中我们需要对客户端请求中携带的jwt token进行解析,只需要公钥就可以。将授课资料中的pub.key文件复制到pd-gateway项目的resources/client下。

在这里插入图片描述

19.2 启动类

package com.itheima.pinda;import com.itheima.pinda.auth.client.EnableAuthClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.openfeign.EnableFeignClients;
/***网关启动类*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients({"com.itheima.pinda"})
@EnableZuulProxy//开启网关代理
@EnableAuthClient//开启授权客户端,开启后就可以使用pd-tools-jwt提供的工具类进行jwt token解析了
public class ZuulServerApplication {public static void main(String[] args) {SpringApplication.run(ZuulServerApplication.class, args);}
}

19.3 配置类

package com.itheima.pinda.zuul.config;import com.itheima.pinda.common.config.BaseConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/*** 解决跨域问题*/
@Configuration
public class ZuulConfiguration extends BaseConfig {@Beanpublic CorsFilter corsFilter() {final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();final org.springframework.web.cors.CorsConfiguration config = new org.springframework.web.cors.CorsConfiguration();// 允许cookies跨域config.setAllowCredentials(true);// #允许向该服务器提交请求的URI,*表示全部允许config.addAllowedOrigin("*");// #允许访问的头信息,*表示全部config.addAllowedHeader("*");// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了config.setMaxAge(18000L);// 允许提交请求的方法,*表示全部允许config.addAllowedMethod("OPTIONS");config.addAllowedMethod("HEAD");// 允许Get的请求类型config.addAllowedMethod("GET");config.addAllowedMethod("PUT");config.addAllowedMethod("POST");config.addAllowedMethod("DELETE");config.addAllowedMethod("PATCH");source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}

19.4 API接口和熔断器

在网关服务中会通过Feign来调用权限服务获取相关信息,所以需要定义API接口和对应的熔断器类

package com.itheima.pinda.zuul.api;import com.itheima.pinda.authority.dto.auth.ResourceQueryDTO;
import com.itheima.pinda.authority.entity.auth.Resource;
import com.itheima.pinda.base.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;@FeignClient(name = "${pinda.feign.authority-server:pd-auth-server}", fallback = ResourceApiFallback.class)
public interface ResourceApi {//获取所有需要鉴权的资源@GetMapping("/resource/list")public R<List> list();//查询当前登录用户拥有的资源权限@GetMapping("/resource")public R<List<Resource>> visible(ResourceQueryDTO resource);
}
package com.itheima.pinda.zuul.api;import com.itheima.pinda.authority.dto.auth.ResourceQueryDTO;
import com.itheima.pinda.authority.entity.auth.Resource;
import com.itheima.pinda.base.R;
import org.springframework.stereotype.Component;
import java.util.List;
/*** 资源API熔断*/
@Component
public class ResourceApiFallback implements ResourceApi {public R<List> list() {return null;}public R<List<Resource>> visible(ResourceQueryDTO resource) {return null;}
}

19.5 过滤器

在网关服务中我们需要通过过滤器来实现jwt token解析鉴权相关处理。

19.5.1 BaseFilter

BaseFilter作为基础过滤器,统一抽取一些公共属性和方法。

package com.itheima.pinda.zuul.filter;
import javax.servlet.http.HttpServletRequest;
import com.itheima.pinda.base.R;
import com.itheima.pinda.common.adapter.IgnoreTokenConfig;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
/*** 基础 网关过滤器*/
@Slf4j
public abstract class BaseFilter extends ZuulFilter {@Value("${server.servlet.context-path}")protected String zuulPrefix;/*** 判断当前请求uri是否需要忽略*/protected boolean isIgnoreToken() {HttpServletRequest request = RequestContext.getCurrentContext().getRequest();String uri = request.getRequestURI();uri = StrUtil.subSuf(uri, zuulPrefix.length());uri = StrUtil.subSuf(uri, uri.indexOf("/", 1));boolean ignoreToken = IgnoreTokenConfig.isIgnoreToken(uri);return ignoreToken;}/*** 网关抛异常* @param errMsg* @param errCode* @param httpStatusCode*/protected void errorResponse(String errMsg, int errCode, int httpStatusCode) {R tokenError = R.fail(errCode, errMsg);RequestContext ctx = RequestContext.getCurrentContext();// 返回错误码ctx.setResponseStatusCode(httpStatusCode);ctx.addZuulResponseHeader("Content-Type", "application/json;charset=UTF-8");if (ctx.getResponseBody() == null) {// 返回错误内容ctx.setResponseBody(tokenError.toString());// 过滤该请求,不对其进行路由ctx.setSendZuulResponse(false);}}
}
19.5.2 TokenContextFilter

TokenContextFilter过滤器主要作用就是解析请求头中的jwt token并将解析出的用户信息放入zuul的header中供后面的程序使用。

package com.itheima.pinda.zuul.filter;
import javax.servlet.http.HttpServletRequest;
import com.itheima.pinda.auth.client.properties.AuthClientProperties;
import com.itheima.pinda.auth.client.utils.JwtTokenClientUtils;
import com.itheima.pinda.auth.utils.JwtUserInfo;
import com.itheima.pinda.base.R;
import com.itheima.pinda.context.BaseContextConstants;
import com.itheima.pinda.exception.BizException;
import com.itheima.pinda.utils.StrHelper;
import com.netflix.zuul.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/*** 解析token中的用户信息 过滤器*/
@Component
public class TokenContextFilter extends BaseFilter {@Autowiredprivate AuthClientProperties authClientProperties;@Autowiredprivate JwtTokenClientUtils jwtTokenClientUtils;@Overridepublic String filterType() {// 前置过滤器return PRE_TYPE;}/*** filterOrder:通过int值来定义过滤器的执行顺序,数字越大,优先级越低*/@Overridepublic int filterOrder() {/*一定要在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter过滤器之后执行,因为这个过滤器做了路由,而我们需要这个路由信息来鉴权这个过滤器会将我们鉴权需要的信息放置在请求上下文中*/return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;}/*** 返回一个boolean类型来判断该过滤器是否要执行*/@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() {// 不进行拦截的地址if (isIgnoreToken()) {return null;}RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();//获取token, 解析,然后将信息放入 header//1, 获取tokenString userToken = request.getHeader(authClientProperties.getUser().getHeaderName());//2, 解析tokenJwtUserInfo userInfo = null;try {userInfo = jwtTokenClientUtils.getUserInfo(userToken);} catch (BizException e) {errorResponse(e.getMessage(), e.getCode(), 200);return null;} catch (Exception e) {errorResponse("解析token出错", R.FAIL_CODE, 200);return null;}//3, 将信息放入headerif (userInfo != null) {addHeader(ctx, BaseContextConstants.JWT_KEY_ACCOUNT, userInfo.getAccount());addHeader(ctx, BaseContextConstants.JWT_KEY_USER_ID, userInfo.getUserId());addHeader(ctx, BaseContextConstants.JWT_KEY_NAME, userInfo.getName());addHeader(ctx, BaseContextConstants.JWT_KEY_ORG_ID, userInfo.getOrgId());addHeader(ctx, BaseContextConstants.JWT_KEY_STATION_ID, userInfo.getStationId());}return null;}private void addHeader(RequestContext ctx, String name, Object value) {if (StringUtils.isEmpty(value)) {return;}ctx.addZuulRequestHeader(name, StrHelper.encode(value.toString()));}
}
19.5.3 AccessFilter

AccessFilter过滤器主要进行的是鉴权相关处理。具体的处理逻辑如下:

第1步:判断当前请求uri是否需要忽略
第2步:获取当前请求的请求方式和uri,拼接成GET/user/page这种形式,称为权限标识符
第3步:从缓存中获取所有需要进行鉴权的资源(同样是由资源表的method字段值+url字段值拼接成),如果没有获取到则通过Feign调用权限服务获取并放入缓存中
第4步:判断这些资源是否包含当前请求的权限标识符,如果不包含当前请求的权限标识符,则返回未经授权错误提示
第5步:如果包含当前的权限标识符,则从zuul header中取出用户id,根据用户id取出缓存中的用户拥有的权限,如果没有取到则通过Feign调用权限服务获取并放入缓存,判断用户拥有的权限是否包含当前请求的权限标识符
第6步:如果用户拥有的权限包含当前请求的权限标识符则说明当前用户拥有权限,直接放行
第7步:如果用户拥有的权限不包含当前请求的权限标识符则说明当前用户没有权限,返回未经授权错误提示
package com.itheima.pinda.zuul.filter;import cn.hutool.core.util.StrUtil;
import com.itheima.pinda.authority.dto.auth.ResourceQueryDTO;
import com.itheima.pinda.authority.entity.auth.Resource;
import com.itheima.pinda.base.R;
import com.itheima.pinda.common.constant.CacheKey;
import com.itheima.pinda.context.BaseContextConstants;
import com.itheima.pinda.exception.code.ExceptionCode;
import com.itheima.pinda.zuul.api.ResourceApi;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import net.oschina.j2cache.CacheChannel;
import net.oschina.j2cache.CacheObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/*** 权限验证过滤器*/
@Component
@Slf4j
public class AccessFilter extends BaseFilter {@Autowiredprivate CacheChannel cacheChannel;@Autowiredprivate ResourceApi resourceApi;@Overridepublic String filterType() {return PRE_TYPE;}@Overridepublic int filterOrder() {return FilterConstants.PRE_DECORATION_FILTER_ORDER + 10;}@Overridepublic boolean shouldFilter() {return true;}/*** 验证当前用户是否拥有某个URI的访问权限*/@Overridepublic Object run() {// 不进行拦截的地址if (isIgnoreToken()) {return null;}RequestContext requestContext = RequestContext.getCurrentContext();String requestURI = requestContext.getRequest().getRequestURI();requestURI = StrUtil.subSuf(requestURI, zuulPrefix.length());requestURI = StrUtil.subSuf(requestURI, requestURI.indexOf("/", 1));String method = requestContext.getRequest().getMethod();String permission = method + requestURI;//从缓存中获取所有需要进行鉴权的资源CacheObject resourceNeed2AuthObject = cacheChannel.get(CacheKey.RESOURCE, CacheKey.RESOURCE_NEED_TO_CHECK);List<String> resourceNeed2Auth = (List<String>) resourceNeed2AuthObject.getValue();if(resourceNeed2Auth == null){resourceNeed2Auth = resourceApi.list().getData();if(resourceNeed2Auth != null){cacheChannel.set(CacheKey.RESOURCE,CacheKey.RESOURCE_NEED_TO_CHECK,resourceNeed2Auth);}}if(resourceNeed2Auth != null){long count = resourceNeed2Auth.stream().filter((String r) -> {return permission.startsWith(r);}).count();if(count == 0){//未知请求errorResponse(ExceptionCode.UNAUTHORIZED.getMsg(),ExceptionCode.UNAUTHORIZED.getCode(), 200);return null;}}String userId = requestContext.getZuulRequestHeaders().get(BaseContextConstants.JWT_KEY_USER_ID);CacheObject cacheObject = cacheChannel.get(CacheKey.USER_RESOURCE, userId);List<String> userResource = (List<String>) cacheObject.getValue();// 如果从缓存获取不到当前用户的资源权限,需要查询数据库获取,然后再放入缓存if(userResource == null){ResourceQueryDTO resourceQueryDTO = new ResourceQueryDTO();resourceQueryDTO.setUserId(new Long(userId));//通过Feign调用服务,查询当前用户拥有的权限R<List<Resource>> result = resourceApi.visible(resourceQueryDTO);if(result.getData() != null){List<Resource> userResourceList = result.getData();userResource = userResourceList.stream().map((Resource r) -> {return r.getMethod() + r.getUrl();}).collect(Collectors.toList());cacheChannel.set(CacheKey.USER_RESOURCE,userId,userResource);}}long count = userResource.stream().filter((String r) -> {return permission.startsWith(r);}).count();if(count > 0){//有访问权限return null;}else{log.warn("用户{}没有访问{}资源的权限",userId,method + requestURI);errorResponse(ExceptionCode.UNAUTHORIZED.getMsg(),ExceptionCode.UNAUTHORIZED.getCode(), 200);}return null;}
}

20. 通用权限系统企业应用指南

20.1 新项目开发

如果是新项目开发,可以在品达通用权限系统的基础上进行相关的业务开发,其实就是将通用权限系统当做开发脚手架在此基础之上快速开始业务开发。

本小节通过一个商品服务的案例来讲解如何基于品达通用权限系统进行新业务的开发。

20.1.1 数据库环境搭建

创建数据库pd_goods并创建表pd_goods_info,可以使用资料中提供的建表脚本pd_goods_info.sql进行创建。

在这里插入图片描述

20.1.2 后端业务功能开发
20.1.2.1 创建工程

在品达通用权限系统基础上创建商品服务相关模块,如下图:

在这里插入图片描述

pd-goods              #商品服务父工程
├── pd-goods-entity	  #实体
├── pd-goods-server	  #服务
20.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 {
}
20.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-SNAPSHOT</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_goodsswagger:enabled: truedocket:core:title: 核心模块base-package: com.itheima.pinda.goods.controllerserver: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 {@Autowiredprivate DozerUtils dozer;@Autowiredprivate 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("删除商品信息")@DeleteMappingpublic R<Boolean> delete(@RequestParam("ids[]") List<Long> ids) {goodsInfoService.removeByIds(ids);return success();}
}
20.1.3 配置网关路由规则

在Nacos中的pd-gateway.yml中新增商品服务相关的路由配置,内容如下:

zuul:#  debug:#    request: true#  include-debug-header: trueretryable: falseservlet-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-servergoods:path: /goods/**serviceId: pd-goods-server
20.1.4 前端开发

可以将pinda-authority-ui作为前端开发脚手架,基于此工程开发商品服务相关页面。资料中已经提供了开发完成的前端工程,直接运行即可。

下载安装node js: https://blog.csdn.net/qq_51929833/article/details/123694846

解压前端工程并删除文件夹node-sass
在这里插入图片描述
重新安装并运行

npm install node-sass
npm run dev 

在这里插入图片描述

在这里插入图片描述
出现以下页面就是运行成功了

在这里插入图片描述输入管理员账号和密码,验证码登录查看

user:pinda
password:pinda123

在这里插入图片描述

20.1.5 配置菜单和资源权限

启动网关服务、权限服务、商品服务、前端工程,使用管理员账号登录,配置商品服务相关的菜单和对应的资源权限。

在这里插入图片描述

在这里插入图片描述

20.1.6 配置角色

启动网关服务和权限服务,使用管理员账号登录。创建新角色并进行配置(菜单权限和资源权限)和授权(为用户分配角色)。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

20.2 已有项目集成

本小节通过一个已经完成开发的TMS(品达物流)项目来展示如何进行已有项目集成的过程。

20.2.1 TMS调整
20.2.1.1 页面菜单

对于已经完成相关业务开发的项目,可以将其前端系统的页面通过iframe的形式内嵌到通用权限系统的前端页面中,这就需要对其前端系统的页面进行相应的修改。因为原来的TMS系统前端页面的左侧菜单和导航菜单都在自己页面中展示,现在需要将这些菜单配置到通用权限系统中,通过权限系统的前端系统来展示。

在这里插入图片描述

20.2.1.2 请求地址

为了能够进行鉴权相关处理,需要将TMS前端发送的请求首先经过通用权限系统的网关进行处理:

在这里插入图片描述

20.2.2 网关路由配置

配置通用权限系统的网关路由规则,将针对TMS的请求转发到TMS相关服务:

zuul:retryable: falseservlet-path: /ignored-services: "*"   # 忽略eureka上的所有服务sensitive-headers:  # 一些比较敏感的请求头,不想通过zuul传递过去, 可以通过该属性进行设置routes:  # 路由配置方式authority: path: /authority/**serviceId: pd-auth-serverpay:path: /pay/**serviceId: pd-ofpay-serverweb-manager:path: /web-manager/**serviceId: pd-web-managerweb-xczx:path: /xczx/api/**url: http://xc-main-java.itheima.net:7291/api/
20.2.3 通用权限系统配置
20.2.3.1 菜单配置

登录通用权限系统,配置TMS项目相应的菜单:

在这里插入图片描述

20.2.3.2 资源权限配置

资源权限都是关联到某个菜单下的,所以要配置资源权限需要先选中某个菜单,然后就可以配置相关资源权限了:

在这里插入图片描述

20.2.3.3 角色配置

登录通用权限系统,在角色管理菜单中配置TMS项目中使用到的角色:

在这里插入图片描述

角色创建完成后可以为角色配置菜单权限和资源权限:

在这里插入图片描述

完成角色的菜单权限和资源权限配置后可以将角色授权给用户:

在这里插入图片描述

在这里插入图片描述

这篇关于品达通用权限系统(Day 7~Day 8)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/702098

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识