<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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.sfac</groupId>
<artifactId>spring_cloud_alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring_cloud_alibaba</name>
<packaging>pom</packaging>
<url>http://www.example.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/>
</parent>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.boot.version>2.6.3</spring.boot.version>
<spring.cloud.version>2021.0.1</spring.cloud.version>
<spring.cloud.alibaba.version>2021.0.1.0</spring.cloud.alibaba.version>
</properties>
<!-- 父工程版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- spring web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Apache 工具组件 -->
<!-- 字符串、对象、日期等工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 摘要运算、编码 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- IO -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- spring test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<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">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring_cloud_alibaba_entity</artifactId>
<name>spring_cloud_alibaba_entity</name>
<packaging>jar</packaging>
<url>http://www.example.com</url>
<parent>
<artifactId>spring_cloud_alibaba</artifactId>
<groupId>org.sfac</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<!-- 为 spring-boot-maven-plugin 插件指定入口 -->
<start-class>org.sfac.App</start-class>
</properties>
<dependencies>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- javax.persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--
spring-boot-maven-plugin 打出的包是不可依赖的
于是设置 classifier 属性,意思是打包的时候打两个,例如
依赖包:jianghu_account-0.0.1-SNAPSHOT.jar
可执行包:jianghu_account-0.0.1-SNAPSHOT-exec.jar
-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
</project>
<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>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba_test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_cloud_alibaba_test</name>
<description>Demo project for Spring Cloud Test</description>
<parent>
<artifactId>spring_cloud_alibaba</artifactId>
<groupId>org.sfac</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- entity -->
<dependency>
<groupId>org.sfac</groupId>
<artifactId>spring_cloud_alibaba_entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.47</version>-->
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
# for server
server.port=8761
# for nacos discovery
spring.application.name=alibaba-service-test
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
<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>
<groupId>com.sfac</groupId>
<artifactId>spring_cloud_alibaba_account</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_cloud_alibaba_account</name>
<description>Demo project for Spring Boot</description>
<parent>
<artifactId>spring_cloud_alibaba</artifactId>
<groupId>org.sfac</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- entity -->
<dependency>
<groupId>org.sfac</groupId>
<artifactId>spring_cloud_alibaba_entity</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.47</version>-->
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
# for server
server.port=8763
# for nacos discovery
spring.application.name=alibaba-service-account
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
<!-- nacos config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- bootstrap for spring cloud 2020+ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
# for bootstrap
spring.cloud.bootstrap.enabled=true
# for server
server.port=8004
# for spring
spring.application.name=alibaba-service-account
spring.profiles.active=dev
# for nacos discovery
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.file-extension=properties
# for test
sfac.name=HymanHu
sfac.age=18
sfac.random=${random.value}
package com.sfac.alibabaServiceAccount.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description: Config Controller
* @author HymanHu
* @date 2022-03-05 21:51:19
*/
"/api") (
public class ConfigController {
("${server.port}")
private int port;
("${sfac.name}")
private String name;
("${sfac.age}")
private int age;
("${sfac.random}")
private String random;
/**
* 127.0.0.1:8004/api/config ---- get
*/
("/config")
public String nacosConfig() {
StringBuffer sb = new StringBuffer();
sb.append(port).append("----")
.append(name).append("----")
.append(age).append("----")
.append(random);
return sb.toString();
}
}
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- loadbalancer, spring cloud 高版本中, OpenFeign 使用的负载均衡器,需单独引入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
package com.sfac.alibabaServiceAccount.service;
import org.alibabaEntity.entity.test.City;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* Description: City Feign Client
* @author HymanHu
* @date 2022-03-05 10:57:19
*/
name = "alibaba-service-test") (
public interface CityFeignClient {
"/api/city/{id}") (
public City getCityById( int id);
}
package com.sfac.alibabaServiceAccount.service.impl;
import java.time.LocalDateTime;
import org.alibabaEntity.entity.account.User;
import org.alibabaEntity.entity.test.City;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sfac.alibabaServiceAccount.service.CityFeignClient;
import com.sfac.alibabaServiceAccount.service.UserService;
/**
* Description: User Service Impl
* @author HymanHu
* @date 2022-03-04 20:53:54
*/
public class UserServiceImpl implements UserService {
private CityFeignClient cityFeignClient;
public User getUserById(int id) {
User user = new User();
user.setId(id);
user.setUserName("HymanHu");
user.setPassword("1111");
user.setCreateDate(LocalDateTime.now());
City city = cityFeignClient.getCityById(id);
user.setCity(city);
return user;
}
}
package com.sfac.alibabaServiceAccount.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* Description: Custom LoadBalancer Config
* - 该类不添加 @configuration 注解,因为该类为 @LoadBalancerClient 的属性准备
* @author HymanHu
* @date 2022-03-05 15:55:55
*/
public class CustomLoadBalancerConfig {
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 在此也可返回自定义负载均衡器
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
name = "alibaba-service-test", configuration = CustomLoadBalancerConfig.class) (
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- openfeign,不添加该依赖,网关无法转发,猜测 Nacos 负载均衡器和 Gateway 负载均衡器不一致造成 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- loadbalancer, spring cloud 高版本中, OpenFeign 使用的负载均衡器,需单独引入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
# for server
server.port=8000
nacos.server=192.168.31.13
# for nacos discovery
spring.application.name=jianghu-gateway
spring.cloud.nacos.discovery.server-addr=${nacos.server}:8848
# Gateway 项目不需要 spring web
# 方案一:创建单独 Gateway 项目,不引入 web 依赖
# 方案二:设置该项目非 Web 启动
spring.main.web-application-type=reactive
# for gateway route
# 默认 false,开启后可以通过 ip:port/服务名称/接口地址进行服务转发
#spring.cloud.gateway.discovery.locator.enabled=true
# 把服务名转换为小写,Eureka 中默认都是大写, 但 Nacos 不会自动转换,所以也可以不写。
#spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=account-service
spring.cloud.gateway.routes[0].uri=lb://jianghu-account
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/account/**
spring.cloud.gateway.routes[1].id=common-service
spring.cloud.gateway.routes[1].uri=lb://jianghu-common
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/common/**
package com.sfac.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* Description: Jianghu Gateway Application
* @author Jianghu
* @date 2022-03-25 20:43:50
*/
public class JianghuGatewayApplication {
public static void main( String[] args ) {
SpringApplication.run(JianghuGatewayApplication.class, args);
}
}
<!-- actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
# for sentinel
# sentinel dashboard 地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:9090
# sentinel dashboard 内部通信端口,默认为 8719,如果被占用会自动 +1,直到找到为止
spring.cloud.sentinel.transport.port=8719
# 开启所有监控端点
management.endpoints.web.exposure.include=*
public class UserServiceImpl implements UserService {
private UserDao userDao;
private TestFeignClient testFeignClient;
(value = "getUserVoByUserIdAndCityId", blockHandler = "blockHandler", fallback = "fallbackHandler")
public UserVo getUserVoByUserIdAndCityId(int userId, int cityId) {
if (userId < 0 ) {
throw new RuntimeException("cdsacadscdascsad");
}
UserVo userVo = new UserVo();
User user = userDao.getUserById(userId);
BeanUtils.copyProperties(user, userVo);
City city = testFeignClient.getCityByCityId(cityId);
userVo.setCity(city);
return userVo;
}
/**
* 流控或降级异常处理逻辑
*/
public UserVo blockHandler(int userId, int cityId, BlockException exception) {
if (exception instanceof FlowException) {
System.out.println("您被限流了.");
} else if (exception instanceof DegradeException) {
System.out.println("您被降级了.");
} else {
System.out.println("City 接口不可用.");
}
return new UserVo();
}
/**
* 非流控或降级异常处理逻辑
*/
public UserVo fallbackHandler(int userId, int cityId) {
System.out.println("发生了非流控、降级异常,被熔断了.");
return new UserVo();
}
}
<!-- sentinel datasource nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
# for sentinel datasource nacos
spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds.nacos.dataId=alibaba-service-account-sentinel
spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow
[
{
"resource": "/api/user/{id}",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
<!-- seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
# seata 事务分组配置
spring.cloud.alibaba.seata.tx-service-group=test_server_group
# for data source
# mysql 5
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# mysql 6 +
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/main?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.jdbc-url=jdbc:mysql://127.0.0.1:3306/main?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
# hikari pool
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=180000
spring.datasource.hikari.auto-commit=true
# for alibaba seata
spring.cloud.alibaba.seata.tx-service-group=test_server_group
# for mybatis
mybatis.configuration.map-underscore-to-camel-case=true
# for sentinel
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8005
spring.cloud.sentinel.transport.port=8719
management.endpoints.web.exposure.include=*
package com.sfac.alibabaServiceTest.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
/**
* Description: Data Source Proxy Config
* -有警告信息,Pom 导入 spring-boot-configuration-processor
* @author HymanHu
* @date 2022-03-09 16:05:58
*/
(
sqlSessionFactoryRef = "sqlSessionFactory",
sqlSessionTemplateRef="sqlSessionTemplate",
basePackages = "com.sfac.alibabaServiceTest.dao")
public class DataSourceProxyConfig {
// hikari config
("${spring.datasource.hikari.maximum-pool-size}")
private int maximumPoolSize;
("${spring.datasource.hikari.minimum-idle}")
private int minimumIdle;
("${spring.datasource.hikari.idle-timeout}")
private long idleTimeout;
("${spring.datasource.hikari.auto-commit}")
private boolean autoCommit;
// mybatis config
("${mybatis.configuration.map-underscore-to-camel-case}")
private boolean mapUnderscoreToCamelCase;
(name="hikariDataSource")
(prefix = "spring.datasource")
public HikariDataSource dataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setMaximumPoolSize(maximumPoolSize);
hikariDataSource.setMinimumIdle(minimumIdle);
hikariDataSource.setIdleTimeout(idleTimeout);
hikariDataSource.setAutoCommit(autoCommit);
return hikariDataSource;
}
(name="dataSourceProxy")
public DataSource dataSourceProxy( ("hikariDataSource") DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
(name="sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(
("dataSourceProxy") DataSource dataSourceProxy) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSourceProxy);
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(mapUnderscoreToCamelCase);
bean.setConfiguration(configuration);
return bean.getObject();
}
(name="sqlSessionTemplate")
public SqlSessionTemplate mainDbSqlSessionTemplate(
"sqlSessionFactory") SqlSessionFactory sessionfactory) { (
return new SqlSessionTemplate(sessionfactory);
}
(name = "mybatisTransactionManager")
public PlatformTransactionManager transactionManager(
("dataSourceProxy") DataSource dataSourceProxy) {
return new DataSourceTransactionManager(dataSourceProxy);
}
}
public ResultEntity<City> updateCity(City city) {
cityDao.updateCity(city);
return new ResultEntity<City>(ResultEntity.ResultStatus.SUCCESS.status, "Update success.", city);
}
# seata 事务分组配置
spring.cloud.alibaba.seata.tx-service-group=account_server_group
public ResultEntity<User> updateUser(User user) {
City city = user.getCity();
if (city != null) {
cityFeignClient.updateCity(city);
}
int i = 1 / 0;
userDao.updateUser(user);
return new ResultEntity<User>(ResultEntity.ResultStatus.SUCCESS.status, "Update success.", user);
}
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
server {
listen 8000;
server_name localhost;
location / {
root /static;
#index index.html index.htm;
autoindex on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
package com.sfac.entity.common.resource;
import java.util.Arrays;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* Description: File Type
* -不同类型的文件存放在不同的文件夹中;
* -命名规则
* -Type:文件类型区别
* -Category:同种类型(如图片分类)下功能分类(如个人头像、摘录等)
* @author JiangHu
* @date 2022-04-25 09:45:25
*/
shape = JsonFormat.Shape.OBJECT) (
public enum FileType {
IMAGE("image", "images"),
VIDEO("video", "videos"),
;
public String name;
public String typeFolder;
private FileType(String name, String typeFolder) {
this.name = name;
this.typeFolder = typeFolder;
}
public static FileType getFileTypeByName(String name) {
return Arrays.stream(FileType.values())
.filter(item -> item.name.equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
}
package com.sfac.entity.common.resource;
import java.util.Arrays;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* Description: Image Category
* -命名规则
* -Type:文件类型区别
* -Category:同种类型(如图片分类)下功能分类(如个人头像、摘录等)
* @author JiangHu
* @date 2022-03-25 14:13:40
*/
shape = JsonFormat.Shape.OBJECT) (
public enum ImageCategory {
// 个人主页
PROFILE("profile", 1000, 1000, 2048),
// 摘录
EXCERPT("excerpt", 2000, 1000, 2048),
// 测试
TEST("test", 11, 11, 11),
;
public String name;
public Integer maxWidth;
public Integer maxHeight;
public int maxSize;
private ImageCategory(String name, Integer maxWidth, Integer maxHeight, int maxSize) {
this.name = name;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.maxSize = maxSize;
}
public static ImageCategory getImageCategoryByName(String name) {
return Arrays.stream(ImageCategory.values())
.filter(item -> item.name.equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
public static void main(String[] args) {
System.out.println(ImageCategory.getImageCategoryByName("profile").maxHeight);
}
}
<!-- Apache 网络工具包,支持 FTP、SMTP、 POP3 等协议 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
<!-- SSH2 的一个纯 Java 实现,连接到一个 SSHD 服务器 -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
# for ftp
ftp.address=www.sfac.xyz
ftp.port=61234
ftp.userName=hj
ftp.password=***
ftp.basePath=/static
nginx.port=8000
package com.sfac.common.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* Description: Ftp Config
*
* @author Jianghu
* @date 2022-04-24 16:47:03
*/
"classpath:application.properties") (
public class FtpConfigBean {
("${ftp.address}")
private String address;
("${ftp.port}")
private int port;
("${ftp.userName}")
private String userName;
("${ftp.password}")
private String password;
("${ftp.basePath}")
private String basePath;
("${nginx.port}")
private int nginxPort;
// 自行添加 get、set 方法
}
package com.sfac.common.util;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.sfac.common.config.FtpConfigBean;
import com.sfac.entity.common.Result;
import com.sfac.entity.common.resource.FileType;
import com.sfac.entity.common.resource.ImageCategory;
import com.sfac.entity.util.FileUtil;
/**
* Description: Ftp Util
* @author Jianghu
* @date 2022-04-25 09:33:16
*/
public class FtpUtil {
private FtpConfigBean ftpConfigBean;
/**
* -上传文件,ftp:// 协议,使用 Apache commons-net 包
* -规则:basePath/fileTypeFolder/fileCategoryFolder/fileName
* -比如:/static/images/profile/*******.jpg
*/
public Result<String> uploadFileByFtp(MultipartFile file, String fileType,
String fileCategory, boolean useOriginalName) {
String fileCategoryFolder = "default";
FileType ft = FileType.getFileTypeByName(fileType);
switch (ft) {
case IMAGE:
ImageCategory imageCategory = ImageCategory.getImageCategoryByName(fileCategory);
fileCategoryFolder = imageCategory.name;
break;
default:
break;
}
// 目标文件夹
String destFolder = String.format("%s/%s/%s",
ftpConfigBean.getBasePath(),
ft.typeFolder,
fileCategoryFolder);
// 文件名
String fileName = useOriginalName ?
file.getOriginalFilename() :
String.format("%s.%s",
System.currentTimeMillis(),
FileUtil.getFileType(file.getOriginalFilename()));
// 目标文件
String destFile = String.format("%s/%s",
destFolder,
fileName);
// 文件访问路径
String fileUrl = String.format("%s:%s%s",
ftpConfigBean.getAddress(),
ftpConfigBean.getNginxPort(),
destFile);
FTPClient ftpClient = null;
try {
// FTP 客户端,用户名密码登陆
ftpClient = new FTPClient();
ftpClient.connect(ftpConfigBean.getAddress(), ftpConfigBean.getPort());
ftpClient.login(ftpConfigBean.getUserName(), ftpConfigBean.getPassword());
// 获取连接返回状态码
int replyCode = ftpClient.getReplyCode();
// 如果返回状态不在 200 ~ 300,则认为连接失败
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
return new Result<String>(Result.ResultStatus.FAILED.code, "Connect failed.");
}
// 判断目标文件夹是否存在,不存在则创建
replyCode = ftpClient.cwd(destFolder);
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.mkd(destFolder);
}
// 设置上传的路径
ftpClient.changeWorkingDirectory(destFolder);
// 修改上传文件的格式为二进制
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// 上传文件
if (!ftpClient.storeFile(fileName, file.getInputStream())) {
return new Result<String>(Result.ResultStatus.FAILED.code, "Upload file failed.");
}
// 退出登录
ftpClient.logout();
} catch (IOException e) {
e.printStackTrace();
return new Result<>(Result.ResultStatus.FAILED.code, "Upload file failed.");
} finally {
try {
// 关闭连接
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return new Result<String>(Result.ResultStatus.SUCCESS.code, "Upload file success.", fileUrl);
}
/**
* -上传文件,sftp:// 协议,使用 jsch 包
*/
public Result<String> uploadFileBySftp(MultipartFile file, String fileType,
String fileCategory, boolean useOriginalName) {
String fileCategoryFolder = "default";
FileType ft = FileType.getFileTypeByName(fileType);
switch (ft) {
case IMAGE:
ImageCategory imageCategory = ImageCategory.getImageCategoryByName(fileCategory);
fileCategoryFolder = imageCategory.name;
break;
default:
break;
}
// 目标文件夹
String destFolder = String.format("%s/%s/%s",
ftpConfigBean.getBasePath(),
ft.typeFolder,
fileCategoryFolder);
// 文件名
String fileName = useOriginalName ?
file.getOriginalFilename() :
String.format("%s.%s",
System.currentTimeMillis(),
FileUtil.getFileType(file.getOriginalFilename()));
// 目标文件
String destFile = String.format("%s/%s",
destFolder,
fileName);
// 文件访问路径
String fileUrl = String.format("http://%s:%s/%s/%s/%s",
ftpConfigBean.getAddress(),
ftpConfigBean.getNginxPort(),
ft.typeFolder,
fileCategoryFolder,
fileName);
ChannelSftp sftp = null;
Session sshSession = null;
try {
// 创建 SFTP、Session 实例
JSch jsch = new JSch();
sshSession = jsch.getSession(
ftpConfigBean.getUserName(),
ftpConfigBean.getAddress(),
ftpConfigBean.getPort());
// 用户名密码验证方式
// sshSession.setPassword(ftpConfigBean.getPassword());
// DSA、RSA 公私匙验证方式,证书带密码
jsch.addIdentity(FtpUtil.class.getResource("/id_rsa").getPath(), ftpConfigBean.getPassword());
// 配置
Properties sshConfig = new Properties();
// SSH 公钥检查机制 no、ask、yes
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
// 创建会话连接
sshSession.connect();
// 开启 SFTP 通道
Channel channel = sshSession.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
// 创建目录
createDir(sftp, destFolder);
// 上传文件
sftp.put(file.getInputStream(), destFile);
} catch (JSchException | SftpException | IOException e) {
e.printStackTrace();
return new Result<>(Result.ResultStatus.FAILED.code, "Upload file failed.");
} finally {
if (sftp != null) {
sftp.quit();
}
if (sshSession != null) {
sshSession.disconnect();
}
}
return new Result<String>(Result.ResultStatus.SUCCESS.code, "Upload file success.", fileUrl);
}
// 创建文件夹
private void createDir(ChannelSftp sftp, String destFolder) throws SftpException {
String[] folders = destFolder.split("/");
sftp.cd("/");
for(String folder : folders) {
if(folder.length() > 0) {
try {
sftp.cd(folder);
} catch (SftpException e) {
sftp.mkdir(folder);
sftp.cd(folder);
}
}
}
}
}
package com.sfac.common.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.sfac.common.service.ImageService;
import com.sfac.common.util.FtpUtil;
import com.sfac.entity.common.Result;
import com.sfac.entity.common.resource.FileType;
import com.sfac.entity.common.resource.ImageCategory;
import com.sfac.entity.util.FileUtil;
/**
* Description: Image Service Impl
* @author Jianghu
* @date 2022-04-24 16:33:03
*/
public class ImageServiceImpl implements ImageService {
private FtpUtil ftpUtil;
public Result<String> uploadImage(MultipartFile image, String imageCategory, boolean useOriginalName) {
// 检查图片是否为空
if (image.isEmpty()) {
return new Result<>(Result.ResultStatus.FAILED.code, "Image is null.");
}
// 检查图片类型
if (!FileUtil.isImage(image)) {
return new Result<>(Result.ResultStatus.FAILED.code, "The file is not image.");
}
// 检查 imageCategory 是否正确
ImageCategory category = ImageCategory.getImageCategoryByName(imageCategory);
if (category == null) {
return new Result<>(Result.ResultStatus.FAILED.code,
"The Image Category is error, like profile, excerpt, etc.");
}
// 检查图片尺寸大小
Integer[] imageInfo = FileUtil.getImageInfo(image);
if (imageInfo[0] > category.maxWidth ||
imageInfo[1] > category.maxHeight ||
imageInfo[2] > category.maxSize) {
return new Result<>(Result.ResultStatus.FAILED.code,
String.format("The %s image do not exceed %d*%d, %dKB.",
category.name, category.maxWidth,
category.maxHeight, category.maxSize));
}
return ftpUtil.uploadFileBySftp(image, FileType.IMAGE.name, imageCategory, useOriginalName);
}
}
package com.sfac.common.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.sfac.common.service.ImageService;
import com.sfac.entity.common.Result;
/**
* Description: Image Controller
* @author Jianghu
* @date 2022-04-25 13:40:09
*/
"/api/common") (
public class ImageController {
private ImageService imageService;
/**
* 127.0.0.1/api/common/image/profile?useOriginalName=false ---- post
*/
(value = "/image/{imageCategory}", consumes = "multipart/form-data")
public Result<String> uploadImage(
MultipartFile file,
String imageCategory,
(defaultValue = "false", required = false) boolean useOriginalName) {
return imageService.uploadImage(file, imageCategory, useOriginalName);
}
}
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.3</version>
</dependency>
package com.sfac.entity.util;
import java.util.Date;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.sfac.entity.account.User;
/**
* Description: Jwt Util
* @author JiangHu
* @date 2022-04-14 16:53:49
*/
public class JwtUtil {
// 过期时间,毫秒
private static final Long EXPIRES_TIME = 1 * 60 * 60 * 1000L;
public static final String TOKEN_SALT = "JiangHu";
/**
* -创建用户令牌
*/
public static String createUserToken(User user, long expTime) {
Date currentDate = new Date();
Date expDate = new Date(System.currentTimeMillis() + expTime);
return JWT.create().withAudience(user.getId() + "") // 签发对象
.withIssuedAt(currentDate) // 发行时间
.withExpiresAt(expDate) // 有效期
.withClaim("id", user.getId()) // Payload 载荷,可按官方推荐,也可自定义,可有多个
.sign(Algorithm.HMAC256(user.getId() + TOKEN_SALT)); // 使用 HMAC256 加密算法,生成签名
}
public static String createUserToken(User user) {
return createUserToken(user, EXPIRES_TIME);
}
/**
* - 获取令牌中的签发对象
*/
public static String getAudience(String token) {
String audience = null;
try {
audience = JWT.decode(token).getAudience().get(0);
} catch (Exception e) {
e.printStackTrace();
}
return audience;
}
/**
* - 用签发对象验证令牌
*/
public static DecodedJWT verifyToken(String token, String secret) {
DecodedJWT decodedJWT = null;
try {
JWTVerifier jWTVerifier = JWT.require(Algorithm.HMAC256(secret + TOKEN_SALT)).build();
decodedJWT = jWTVerifier.verify(token);
} catch (Exception e) {
e.printStackTrace();
}
return decodedJWT;
}
/**
* - 根据名字获取载荷内容
*/
public static Claim getClaimByName(String token, String claimName) {
return JWT.decode(token).getClaim(claimName);
}
public static void main(String[] args) {
User user = new User();
user.setId(1);
String token = JwtUtil.createUserToken(user);
System.out.println(token);
System.out.println(JwtUtil.verifyToken(token, user.getId() + "").getClaims());
System.out.println(JwtUtil.verifyToken(token, "2"));
System.out.println(JwtUtil.getAudience(token));
System.out.println(JwtUtil.getClaimByName(token, "id"));
}
}
public Result<String> login(User user) {
User temp = userDao.getUserByUserNameAndPassword(
user.getUserName(), MD5Util.getMD5(user.getPassword()));
if (temp == null) {
return new Result<String>(Result.ResultStatus.FAILED.code, "UserName or Password is error.");
}
// 创建令牌并返回
String token = JwtUtil.createUserToken(temp);
return new Result<String>(Result.ResultStatus.SUCCESS.code, "Login success", token);
}
/**
* 127.0.0.1/api/account/login ---- post
*/
value = "/login", consumes = "application/json") (
public Result<String> login( User user) {
return userService.login(user);
}
package com.sfac.gateway.filter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.sfac.entity.util.JwtUtil;
import reactor.core.publisher.Mono;
/**
* Description: Authorize Filter
* @author Jianghu
* @date 2022-04-15 14:29:49
*/
public class AuthorizeFilter implements GlobalFilter, Ordered {
public static List<String> PASS_URLS = new ArrayList<String>();
static {
PASS_URLS.add("/api/account/login");
PASS_URLS.add("/api/account/user");
PASS_URLS.add("/static/.*?");
};
/**
* -过滤器优先级,数值越小优先级越高
*/
public int getOrder() {
return 0;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取 Request、Response
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 获取 URI,并放行无需权限的接口
String uri = request.getURI().getPath();
String result = passUrls.stream()
// .filter(item -> item.equals(uri) || uri.startsWith(item))
.filter(item -> uri.matches(item))
.findFirst()
.orElse("");
if (StringUtils.isNotBlank(result)) {
return chain.filter(exchange);
}
// 从请求头中获取 token
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst("Token");
if (StringUtils.isBlank(token)) {
// 若 token 为空则返回 401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 获取 token 中签发对象并验证令牌
String id = JwtUtil.getAudience(token);
DecodedJWT decodedJWT = JwtUtil.verifyToken(token, id);
if (decodedJWT == null) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
}
// 请求头中封装 userId
ServerHttpRequest.Builder requestBuilder = request.mutate();
requestBuilder.headers(item -> item.set(SfacConstant.HEADER_USER_ID, id));
requestBuilder.headers(item -> item.set(SfacConstant.HEADER_USER_TOKEN, token));
exchange = exchange.mutate().request(requestBuilder.build()).build();
package com.sfac.entity.common.resource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description: ResourceLevel
* @author JiangHu
* @date 2023-12-13 09:39:14
*/
ElementType.METHOD) (
RetentionPolicy.RUNTIME) (
public @interface ResourceLevel {
int grade() default 1;
}
package com.sfac.entity.config;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.sfac.entity.common.SfacConstant;
import com.sfac.entity.common.resource.ResourceGrade;
import com.sfac.entity.common.resource.ResourceLevel;
/**
* Description: ResourceAop
* @author JiangHu
* @date 2023-12-13 09:54:59
*/
public class ResourceAop {
("@annotation(com.sfac.entity.common.resource.ResourceLevel)")
(1)
public void annotationPointCut(){}
/**
* - 方案二
* - 返回结果通知,获取 userId 和资源 grade,以此做分级处理
* @param joinPoint
* @param result
*/
("rawtypes")
(pointcut="annotationPointCut()", returning="result")
public Object afterMethod(JoinPoint joinPoint, Object result) {
if (result == null) {
return result;
}
// 获取 request、response
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();
// 获取 userId
String userId = request.getHeader(SfacConstant.HEADER_USER_ID);
int grade = 1;
try {
// 反射获取 result 对象的 grade 属性值
Class clazz = result.getClass();
Field field = clazz.getDeclaredField("grade");
field.setAccessible(true);
grade = field.getInt(result);
if (StringUtils.isNotBlank(userId) && !userId.equals("1") && grade > 1) {
// 设置返回状态码
response.setStatus(HttpStatus.FORBIDDEN.value());
// 改变返回值,注意不能返回新的对象,只能在原有对象上修改
Field[] fields = clazz.getDeclaredFields();
for (Field item : fields) {
item.setAccessible(true);
if ("serialVersionUID".equals(item.getName())) {
continue;
}
Class cl = item.getType();
// 判断属性是否是基本类型
if (!item.getType().isPrimitive()) {
item.set(result, null);
} else if (cl.equals(LocalDateTime.class)) {
item.set(result, null);
} else if (cl.equals("boolean")) {
item.set(result, false);
} else {
item.set(result, 0);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* - 方案一
* - 环绕注解通知,@annotation(resourceLevel) ,参数值必须和定义一致
*/
({ "unchecked", "rawtypes" })
// @Around(value="@annotation(resourceLevel)")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint, ResourceLevel resourceLevel)
throws Throwable {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
String userId = request.getHeader(SfacConstant.HEADER_USER_ID);
// 获取注解参数处理器
InvocationHandler handler = Proxy.getInvocationHandler(resourceLevel);
// memberValues 字段存储注解实例的所有属性键值对
Field memberValuesField = handler.getClass().getDeclaredField("memberValues");
memberValuesField.setAccessible(true);
// 获取该实例的所有属性键值对,并修改值
Map memberValues = (Map) memberValuesField.get(handler);
if (StringUtils.isNotBlank(userId) && userId.equals("1")) {
memberValues.put("grade", ResourceGrade.GRADE_THREE.code);
} else {
memberValues.put("grade", ResourceGrade.GRADE_ONE.code);
}
return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
}
}
"rawtypes", "unchecked" }) ({
public PageInfo<ElasticSearch> getModelsBySearch(String projectFlag, Search search) {
// 获取注解值
int grade = 1;
try {
Class clazz = ElasticSearchServiceImpl.class;
Method method = clazz.getDeclaredMethod("getModelsBySearch", String.class, Search.class);
ResourceLevel resourceLevel = method.getDeclaredAnnotation(ResourceLevel.class);
grade = resourceLevel.grade();
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
…………
}
// 判断资源权限
try {
Class clazz = FTServiceImpl.class;
Method method = clazz.getDeclaredMethod("getModelById", int.class, int.class);
ResourceLevel resourceLevel = method.getDeclaredAnnotation(ResourceLevel.class);
if (ft.getGrade() > resourceLevel.grade()) {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = servletRequestAttributes.getResponse();
response.setStatus(HttpStatus.FORBIDDEN.value());
return null;
}
} catch (Exception e) {
e.printStackTrace();
}
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>0.42</version>
</dependency>