SpringCloud GateWay 转发功能,熔断功能,限流功能机制详细介绍
通过官方提供的列子和网上找的资料学习,总结一下springcloud gateway 限流使用的关键点。官网地址:https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html以下主要通过Demo来介绍,如何通过gateway进行限流工作第一步,简单的搭建一个springcloud注册中心,服务端,客户端,g...
前言:通过官方提供的列子和网上找的资料学习,总结一下springcloud gateway使用的关键点。
官网地址:https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html
以下主要通过Demo来介绍,如何通过gateway进行转发功能,熔断功能,限流功能工作
源码:git@github.com:Joe192168/springcloud-sso.git
环境:
- idea2019
- jdk1.8
- maven3.6.2
- springboot2.3.3.RELEASE
- Redis3.0
- mybaties-plus 3.1.1
工程目录结束
springcloud-sso
--springcloud-api 接口服务
--springcloud-authorization 授权服务
--springcloud-commons 公共服务
--springcloud-eureka 注册中心
--springcloud-gateway 网关
springcloud-api工程 pom
<?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>springcloud-sso</artifactId>
<groupId>com.joe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-api</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.joe</groupId>
<artifactId>springcloud-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8081
#tomcat设置
tomcat:
uri-encoding: UTF-8
accesslog:
enabled: false
spring:
application:
name: api-service
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
#配置向认证服务器认证权限
security:
oauth2:
client:
client-id: c1
client-secret: 123
access-token-uri: http://localhost:8082/oauth/token
user-authorization-uri: http://localhost:8082/oauth/authorize
resource:
token-info-uri: http://localhost:8082/oauth/check_token
#远程调用
feign:
hystrix:
enabled: true
compression:
request:
enabled: true
mime-types[0]: text/xml
mime-types[1]: application/xml
mime-types[2]: application/json
min-request-size: 2048
response:
enabled: true
配置api的资源服务器,在这块api服务也相当是资源服务
package com.joe.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.joe.commons.Result;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
/**
* @description: 配置资源服务器
* @author: Joe
**/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private ObjectMapper objectMapper = new ObjectMapper();
/**
* 资源服务器安全配置
* @param resources
* @throws Exception
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//设置资源服务器id,需要与认证服务器对应
resources.resourceId("api-service");
//当权限不足时返回
resources.accessDeniedHandler((request, response, e) -> {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter()
.write(objectMapper.writeValueAsString(Result.from("0001", "权限不足", null)));
});
//当token不正确时返回
resources.authenticationEntryPoint((request, response, e) -> {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter()
.write(objectMapper.writeValueAsString(Result.from("0002", "access_token错误", null)));
});
}
/**
* 配置uri拦截策略
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint((req, resp, exception) -> {
resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
resp.getWriter()
.write(objectMapper.writeValueAsString(Result.from("0002", "没有携带token", null)));
})
.and()
//无需登陆
.authorizeRequests().antMatchers("/test/noauth").permitAll()
.and()
//拦截所有请求,并且检查sope
.authorizeRequests().anyRequest().access("isAuthenticated() && #oauth2.hasScope('ROLE_API')");
}
}
testController代码
package com.joe.controller;
import com.joe.commons.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: Joe
* @Description: 测试接口
*/
@RestController
@RequestMapping("/test")
public class TestController {
@Value("${server.port}")
private String port;
@GetMapping(value = "/r1",produces = "application/json; charset=utf-8")
@PreAuthorize("hasAnyAuthority('ROLE_USER')")
public Result test1() {
return new Result("200","请求成功","订单1");
}
@GetMapping(value = "/r2",produces = "application/json; charset=utf-8")
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
public Result test2() {
return new Result("200","请求成功","订单2");
}
//无需登录
@GetMapping("/noauth")
public Result noauth() {
return new Result("200","请求成功","noauth");
}
@RequestMapping("/name")
public String name(String name){
return "My name is " + name + ". aaa";
}
@RequestMapping("/hello")
public String hello(){
return "Hello!I'm a. port:" + port;
}
@RequestMapping("/age")
public String age(String age){
return "I am " + age + " years old this year. bbb";
}
@RequestMapping("/routeAll")
public String routeAll(String pass) {
return "Can I pass? " + pass + "! port:" + port;
}
}
springcloud-authorization工程 pom
<?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>springcloud-sso</artifactId>
<groupId>com.joe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-authorization</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
application.yml
server:
port: 8082
spring:
application:
name: auth-service
#Redis配置
redis:
#ip地址
host: localhost
#端口
port: 6379
#默认从0使用数据库
database: 0
# Redis服务器连接密码(默认为空)
password:
#数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
#初始化大小
initialSize: 5
#最小值
minIdle: 5
#最大值
maxActive: 20
#最大等待时间,配置获取连接等待超时,时间单位都是毫秒ms
maxWait: 60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接
timeBetweenEvictionRunsMillis: 60000
removeAbandoned: true
removeAbandonedTimeout: 1800
#配置一个连接在池中最小生存的时间
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,
#'wall'用于防火墙,SpringBoot中没有log4j,我改成了log4j2
filters: stat,wall,log4j2
#最大PSCache连接
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 配置StatFilter
web-stat-filter:
#默认为false,设置为true启动
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
#配置StatViewServlet
stat-view-servlet:
url-pattern: "/druid/*"
#允许那些ip
allow: 127.0.0.1
login-username: admin
login-password: admin
#禁止那些ip
deny: 192.168.1.102
#是否可以重置
reset-enable: true
#启用
enabled: true
#支持名称相同的bean被覆盖
main:
allow-bean-definition-overriding: true
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
mybatis-plus:
# 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
# 如果是放在resource目录 classpath:/mapper/*Mapper.xml
mapper-locations: classpath:/mappers/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.joe.uaa.uaa.entity
global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 1
#驼峰下划线转换
db-column-underline: true
#刷新mapper 调试神器
#refresh-mapper: true
#数据库大写下划线转换
capital-mode: true
# Sequence序列接口实现类配置
#key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
#逻辑删除配置(下面3个配置)
#logic-delete-value: 1
#logic-not-delete-value: 0
#sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#自定义填充策略接口实现
#meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
#去掉表前缀
db-config:
table-prefix: t_
configuration:
map-underscore-to-camel-case: false
cache-enabled: false
#配置JdbcTypeForNull
jdbc-type-for-null: 'null'
认证服务器配置
package com.joe.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.sql.DataSource;
import java.util.Arrays;
/**
* @description: 认证服务器配置
* @author: Joe
**/
@Order(2)
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private DruidConfig druidConfig;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
//这块jwt的key,使用这个key就可以进行解密操作
private String SIGGNING_KEY = "sso123";
/**
* 数据源配置
* @return
*/
@Bean
public DataSource dataSource() {
return druidConfig.dataSource();
}
/**
* 配置jwt
* @return
*/
@Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGGNING_KEY);//对称秘钥,资源服务器使用该秘钥解密
return converter;
}
/**
* 将客户端信息从数据库获取
* @return
*/
@Bean
public ClientDetailsService clientDetailsService(){
ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource());
((JdbcClientDetailsService)clientDetailsService).setPasswordEncoder(passwordEncoder);
return clientDetailsService;
}
/**
* 令牌存储方式
* @return
*/
@Bean
public TokenStore tokenStore(){
//存储缓存中
//return new InMemoryTokenStore();
//存储数据库
//return new JwtTokenStore(accessTokenConverter());
//存储Redis
return new RedisTokenStore(redisConnectionFactory);
}
/**
* 授权码服务
* @return
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices(){
//设置授权码模式的授权码如何存取,暂时采用内存方式
//return new InMemoryAuthorizationCodeServices();
//采用数据方式存储授权码
return new JdbcAuthorizationCodeServices(dataSource());
}
/**
* 令牌管理服务
* @return
*/
@Bean
public AuthorizationServerTokenServices tokenServices(){
DefaultTokenServices services = new DefaultTokenServices();
services.setClientDetailsService(clientDetailsService());//客户端服务信息
services.setSupportRefreshToken(true);//是否产生刷新令牌
services.setTokenStore(tokenStore());//令牌存储策略
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter()));
services.setTokenEnhancer(tokenEnhancerChain);
services.setAccessTokenValiditySeconds(7200);//令牌默认有效期2小时
services.setRefreshTokenValiditySeconds(259200);//刷新令牌默认的有效期3天
return services;
}
/**
* 令牌访问端点的安全约束
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//配置允许认证的权限
// 允许通过form提交客户端认证信息(client_id,client_secret),默认为basic方式认证
security.allowFormAuthenticationForClients();
// "/oauth/check_token"端点默认不允许访问
security.checkTokenAccess("isAuthenticated()");
// "/oauth/token_key"断点默认不允许访问
security.tokenKeyAccess("isAuthenticated()");
}
/**
* 客户端配置
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService());
/*clients.inMemory()//使用in‐memory存储
.withClient("c1") // client_id
.secret(bCryptPasswordEncoder.encode("123")) // client_secret
.authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token") // 该client允许的授权类型
.scopes("all") // 允许的授权范围
.redirectUris("https://www.baidu.com")//回调地址
.resourceIds("order-service")//资源服务器id,需要与资源服务器对应
.autoApprove(false);//登录后绕过批准询问(/oauth/confirm_access)*/
}
/**
* 令牌配置访问端点和令牌服务
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)//密码模式
.authorizationCodeServices(authorizationCodeServices())//授权码模式
.tokenServices(tokenServices())//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);//允许POST提交
}
}
安全验证用户身份配置
package com.joe.config;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Joiner;
import com.joe.entity.Users;
import com.joe.mapper.RoleMapper;
import com.joe.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.nio.channels.AcceptPendingException;
import java.util.List;
/**
* @description: 安全验证用户身份配置
* @author: Joe
**/
@Component
public class UserDetailsServiceConfig implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
/**
* 生产环境使用数据库进行验证
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<Users> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
Users user = userMapper.selectOne(wrapper);
//该账号为空
if (user == null) {
throw new UsernameNotFoundException(username);
}
//该账号不正确
if (!username.equals(user.getUserName())) {
throw new AcceptPendingException();
}
//根据用户id查询用户角色
List<String> roles = roleMapper.findRolesByUserId(user.getId());
//将roles转成字符串
String result = Joiner.on(",").join(roles);
return new User(username, user.getPassWord(),
AuthorityUtils.commaSeparatedStringToAuthorityList(result));
}
}
身份认证拦截配置
package com.joe.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @description: 身份认证拦截
* @author: Joe
**/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public UserDetailsServiceConfig userDetailsServiceConfig;
/**
* 密码模式
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 用户密码和认证服务器客户端密码都需要加密算法
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//验证用户权限
auth.userDetailsService(userDetailsServiceConfig);
}
/**
* uri权限拦截,生产可以设置为启动动态读取数据库
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 登陆页
.formLogin().permitAll()
// 登出页
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
// 其余所有请求全部需要鉴权认证
.and().authorizeRequests().anyRequest().authenticated()
// 关闭csrf
.and().csrf().disable();
}
/**
* 网络安全设置
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/check_token");
}
}
这块使用的RBAC(角色授权),其它mapper就不贴了,这块RoleMapper的代码
package com.joe.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.joe.entity.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
@Select("select tr.role_name from t_role tr,t_user_role tur,t_user tu where tr.id = tur.role_id and tu.id = #{userId}")
List<String> findRolesByUserId(@Param("userId") int userId);
}
springcloud-commons工程 pom
<?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>springcloud-sso</artifactId>
<groupId>com.joe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-commons</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
公共返回json实体消息代码
package com.joe.commons;
import lombok.Data;
/**
* @description: 公共返回json实体消息
* @author: Joe
**/
@Data
public class Result<T> {
private String code;
private String msg;
private T data;
public Result(String code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Result from(String code, String msg, T data) {
return new Result(code, msg, data);
}
}
springcloud-eureka工程 pom
<?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>springcloud-sso</artifactId>
<groupId>com.joe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-eureka</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
启动类
package com.joe;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @description: 注册中心启动类
* @author: Joe
**/
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
springcloud-gateway工程 pom
<?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>springcloud-sso</artifactId>
<groupId>com.joe</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-gateway</artifactId>
<dependencies>
<!-- 提供者消费者 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- ali json依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<!-- 插件依赖 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
网关启动类 GateWayApplication
package com.joe;
import com.joe.filter.TokenFilter;
import com.joe.filter.UriKeyResolver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
/**
* @description: 网关启动类
* @author: Joe
**/
@SpringBootApplication
@EnableEurekaClient
public class GateWayApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayApplication.class,args);
}
/**
* 配置限流 bean
* @return
*/
@Bean(name = "uriKeyResolver")
public UriKeyResolver uriKeyResolver() {
return new UriKeyResolver();
}
/**
* 配置认证过滤器 Bean
* @return
*/
@Bean(name = "tokenFilter")
public TokenFilter tokenFilter() {
return new TokenFilter();
}
}
application.yml 详细说明
- 注意配置注册中心地址的端口都为 8080 也就是上面注册中心工程配置的端口
- 每一对 '---' 符号中的配置文件都是单独的,使用 spring.profiles.active 指定
- 每一对 '---' 符号中的配置文件都只配置了一个 route(路由)
- route(路由)由四部分组成,其中 filters 不是必须参数
- 唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)
简单模式
---
#简单尝试
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_simple
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 简单尝试
- id: route_simple
# 目标服务地址(uri:地址,请求转发后的地址)
uri: http://localhost:8081
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 转发地址格式为 uri/test/r1
- Path=/test/r1
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 gateway-service- application.yml 配置文件中最上面的 spring.profiles.active 的值更改为 route_simple
3. 上面配置文件内容意思是当访问 http://localhost:8000/test/r1 (网关地址/archive)
会被转发到 http://localhost:8081/test/r1/ (uri/test/r1)
4. 项目启动成功后访问:http://localhost:8000/test/r1
5. 发现页面会自动被跳转到:http://localhost:8081/test/r1/(这块其实是不发生变化的)
6. 证明服务转发成功
截取请求
---
#截取请求
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_stripPrefix
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 截取请求
- id: route_stripPrefix
# 目标服务地址(uri:地址,请求转发后的地址)
uri: http://localhost:8081
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 转发地址格式为 uri/archive
- Path=/str/test/r1
filters:
## 截取路径位数
- StripPrefix=1
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 gateway-service - application.yml 配置文件中最上面的 spring.profiles.active 的值更改为 route_stripPrefix
3. 上面配置文件内容意思是访问的路径 http://localhost:8000/str/test/r1 (网关地址/str/test/r1)截取 /str 部分,
截取后被转发到 http://localhost:8081/test/r1 (uri/test/r1)
4. 启动注册中心工程(eureka-server)和网关工程(gateway-service)
5. 项目启动成功后访问:http://localhost:8000/str/test/r1
6. 发现页面会自动被跳转到:http://localhost:8081/test/r1 (地址栏是感觉不到变化)
7. 证明路径被截取并服务转发成功
转发指定地址并传入参数
---
#转发指定地址并传入参数
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_uri
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 转发指定地址并传入参数
- id: route_uri
# 目标服务地址(uri:地址,请求转发后的地址)
uri: http://localhost:8081
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=name, zwc
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 gateway-service- application.yml 配置文件中最上面的 spring.profiles.active 的值更改为 route_uri
3. 上面配置文件内容意思是访问的路径 http://localhost:8000/test/name (网关地址/name)
会被转发到 http://localhost:8081/test/name(uri/name),并传入 'name=zwc' 参数(注意为 Get 请求)
4. 项目启动成功后访问:http://localhost:8000/test/name
5. 输出内容:'My name is zwc'(通过网关转发 - 参数有值)
6. 证明转发指定地址并传入参数成功
转发指定服务并传入参数
---
#转发指定服务并传入参数
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_addRequestParameter
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 转发指定服务并传入参数
- id: route_addRequestParameter
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=age, three
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 master-service - application.yml 配置文件中最上面的 spring.profiles.active 的值更改为 route_addRequestParameter
3. 上面配置文件内容意思是访问的路径 http://localhost:8000/test/age (网关地址/age)
会被转发到 http://gateway-service/test/age(uri/test/age),并传入 'age=three' 参数(注意为 Get 请求)
4. 注意此处的配置 uri: lb://api-service 与之前都有所不同,之前都是指定了明确的转发地址,可以满足
单个服务转发的需求,但是一般情况都会有多个服务,所以这里是指定的服务名称,格式为:lb://应用注册服务名。
5. 项目启动成功后访问:http://localhost:8000/testage
6. 这时可能会报错 500.错误信息为 'Unable to find instance for gateway-service'
7. 这种情况不要慌张,只是服务还没有被注册到注册中心,稍等片刻再访问
8. 多次访问:http://localhost:8000/test/age
9. 轮流输出内容:'I am three years old this year'
10. 证明转发指定服务并传入参数成功
熔断
#熔断
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 熔断
profiles: route_hystrix
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 熔断
- id: route_hystrix
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=age, three
## 熔断
- name: Hystrix
args:
name: fallbackcmd
### fallback 时调用的方法 http://localhost:8000/fallback
fallbackUri: forward:/fallback
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 master-service - application.yml 配置文件中最上面的 spring.profiles.active 的值更改为 route_hystrix
3. 上面配置文件内容意思是访问的路径 http://localhost:8000/test/age (网关地址/test/age)
会被转发到 http://api-service/test/age(uri/age),并传入 'age=three' 参数(注意为 Get 请求)
4. 注意此处的配置 uri: lb://api-service 与之前都有所不同,之前都是指定了明确的转发地址,可以满足
单个服务转发的需求,但是一般情况都会有多个服务,所以这里是指定的服务名称,格式为:lb://应用注册
服务名。
5. 此处还多配置了一个过滤器 '- name: Hystrix'(熔断)
6. 当请求服务出错时,会调用 fallback,路径为:http://localhost:8000/fallback (网关地址/fallback)
7. 此时就需要如下前端控制器
gateway-service - 熔断 - controller
package com.joe.hystrix;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName FallbackController
* @Desc TODO 网关断路器
* @Version 1.0
*/
@RestController
public class FallbackController {
@RequestMapping("/fallback")
public String fallback() {
return "I'm Spring Cloud Gateway fallback.";
}
}
8. 项目启动成功后访问:http://localhost:8000/test/age
9. 输出内容:'I'm Spring Cloud Gateway fallback.'
10. 证明熔断成功
限流
---
#限流
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 熔断
profiles: route_requestRateLimiter
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 限流
- id: route_requestRateLimiter
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=age, three
## 限流
- name: RequestRateLimiter
args:
### 限流过滤器的 Bean 名称
key-resolver: '#{@uriKeyResolver}'
### 希望允许用户每秒处理多少个请求
redis-rate-limiter.replenishRate: 1
### 用户允许在一秒钟内完成的最大请求数
redis-rate-limiter.burstCapacity: 3
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 gateway-service - application.yml 配置文件中最上面的 spring.profiles.active 的值
更改为 route_requestRateLimiter
3. 上面配置文件内容意思是访问的路径 http://localhost:8000/test/age (网关地址/age)
会被转发到 http://api-service/test/age(uri/test/age),并传入 'age=three' 参数(注意为 Get 请求)
4. 注意此处还需要配置 redis 的连接信息
5. 注意此处是结合 redis 实现的限流,所以 filter 过滤器的 name 必须为 RequestRateLimiter
6. 并且通过实现 KeyResolver 类来自定义限流策略,如下
gateway-service - 限流 - 策略
package com.joe.filter;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @ClassName UriKeyResolver
* @Desc TODO Spring Cloud Gateway 网关限流过滤器
* @Version 1.0
*/
public class UriKeyResolver implements KeyResolver {
/**a
* 根据请求的 uri 限流
* @param exchange
* @return
*/
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getURI().getPath());
}
}
7. 启动本地 redis(redis-server.exe) 服务
8 项目启动成功后访问:http://localhost:8000/test/age
9. 此时限流却无论如何都不生效,原因有如下两点
① redis-server 版本过低!我 Windows 本地是 redis-2.4.2 版本的,要用 3 以上的版本!!!
② 数据在 redis 中存储的时间只有几秒,所以得使用 monitor 指令来动态的观察!!!
10. 打开 redis-cli.exe,输入命令 monitor
11. 快速刷新地址:http://localhost:8000/test/age
12. 页面上会出现 429,redis-cli.exe 中会出现很多数据交互(request_rate_limiter.xxx 开头的 key)
13. 证明限流成功
gateway-service - 综合
# 端口
server:
port: 8000
spring:
profiles:
# 指定配置
# route_simple:简单尝试
# route_stripPrefix:截取请求
# route_uri:转发指定地址并传入参数
# route_addRequestParameter:转发指定服务并传入参数
# route_hystrix:熔断
# route_requestRateLimiter:限流
# route_all:综合
active: route_all
---
#简单尝试
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_simple
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 简单尝试
- id: route_simple
# 目标服务地址(uri:地址,请求转发后的地址)
uri: http://localhost:8081
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 转发地址格式为 uri/test/r1
- Path=/test/r1
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
---
#截取请求
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_stripPrefix
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 截取请求
- id: route_stripPrefix
# 目标服务地址(uri:地址,请求转发后的地址)
uri: http://localhost:8081
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 转发地址格式为 uri/archive
- Path=/str/test/r1
filters:
## 截取路径位数
- StripPrefix=1
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
---
#转发指定地址并传入参数
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_uri
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 转发指定地址并传入参数
- id: route_uri
# 目标服务地址(uri:地址,请求转发后的地址)
uri: http://localhost:8081
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=name, zwc
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
---
#转发指定服务并传入参数
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 简单尝试
profiles: route_addRequestParameter
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 转发指定服务并传入参数
- id: route_addRequestParameter
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=age, three
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
---
#熔断
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 熔断
profiles: route_hystrix
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 熔断
- id: route_hystrix
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=age, three
## 熔断
- name: Hystrix
args:
name: fallbackcmd
### fallback 时调用的方法 http://localhost:8000/fallback
fallbackUri: forward:/fallback
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
---
#限流
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 熔断
profiles: route_requestRateLimiter
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 限流
- id: route_requestRateLimiter
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 添加指定参数
- AddRequestParameter=age, three
## 限流
- name: RequestRateLimiter
args:
### 限流过滤器的 Bean 名称
key-resolver: '#{@uriKeyResolver}'
### 希望允许用户每秒处理多少个请求
redis-rate-limiter.replenishRate: 1
### 用户允许在一秒钟内完成的最大请求数
redis-rate-limiter.burstCapacity: 3
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
---
#综合
spring:
# 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。
## 综合
profiles: route_all
redis:
host: localhost
port: 6379
database: 0
application:
# 应用名称
name: gateway-service
cloud:
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
enabled: true
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
# 路由标识(id:标识,具有唯一性) 综合
- id: route_all
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://api-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 转发地址格式为 uri/test/routeAll,/all 部分会被下面的过滤器给截取掉
- Path=/all/test/routeAll
## 匹配 GET 请求
- Method=GET
# 过滤器(filters:过滤器,过滤规则)
filters:
## 截取路径位数
- StripPrefix=1
## 添加指定参数
- AddRequestParameter=pass, yes
## 熔断
- name: Hystrix
args:
name: fallbackcmd
### fallback 时调用的方法 http://localhost:8000/fallback
fallbackUri: forward:/fallback
## 限流
- name: RequestRateLimiter
args:
### 限流过滤器的 Bean 名称
key-resolver: '#{@uriKeyResolver}'
### 希望允许用户每秒处理多少个请求
redis-rate-limiter.replenishRate: 1
### 用户允许在一秒钟内完成的最大请求数
redis-rate-limiter.burstCapacity: 3
eureka:
instance:
# 使用 ip 代替实例名
prefer-ip-address: true
# 实例的主机名
hostname: ${spring.cloud.client.ip-address}
# 实例的 ID 规则
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
client:
serviceUrl:
# 注册中心地址
defaultZone: http://${eureka.instance.hostname}:8080/eureka/
logging:
level:
# log 级别
org.springframework.cloud.gateway: debug
1. 启动注册中心工程(eureka-server)、API服务工程(api-server),授权服务工程(auth-service),网关服务工程(gateway-service)
2. 把 gateway-service - application.yml 配置文件中最上面的 spring.profiles.active 的值更改为 route_all
3. 上面配置文件内容意思是访问的路径 http://localhost:8000/all/test/routeAll (网关地址/all/test/routeAll)截取 /all 部分,
会被转发到 http://api-service/test/routeAll(uri/test/routeAll),并传入 'pass=yes' 参数(注意为 Get 请求)
5. 项目启动成功后访问:http://localhost:8000/all/test/routeAll
6. 首先会返回 'I'm Spring Cloud Gateway fallback.',因为服务还未被注册到注册中心
7. 然后会返回 '{"msg":"缺少凭证","code":-1}',因为配置了全局过滤器,如下
package com.joe.filter;
import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* @ClassName TokenFilter
* @Desc TODO 请求认证过滤器
*/
public class TokenFilter implements GlobalFilter{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 请求对象
ServerHttpRequest request = exchange.getRequest();
// 响应对象
ServerHttpResponse response = exchange.getResponse();
// Headers对象
HttpHeaders httpHeaders = request.getHeaders();
// 只有综合路由才添加这个全局过滤器(routesId:route_all)
// 如果请求路径中不存在 routeAll 字符串
/*if(request.getURI().toString().indexOf("routeAll") == -1){
System.out.println("filter -> return");
// 直接跳出
return chain.filter(exchange);
}*/
// 从请求中获取 token 参数
//String token = exchange.getRequest().getQueryParams().getFirst("token");
// 从请求headers中获取token参数
String token = httpHeaders.getFirst("Authorization");
// 如果为空,那么将返回 401
if (token == null || token.isEmpty()) {
// 响应消息内容对象
JSONObject message = new JSONObject();
// 响应状态
message.put("code", -1);
// 响应内容
message.put("msg", "缺少token凭证");
// 转换响应消息内容对象为字节
byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
// 设置响应对象状态码 401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 设置响应对象内容并且指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
// 返回响应对象
return response.writeWith(Mono.just(buffer));
}
// 获取请求地址
String beforePath = request.getPath().pathWithinApplication().value();
// 获取响应状态码
HttpStatus beforeStatusCode = response.getStatusCode();
System.out.println("响应码:" + beforeStatusCode + ",请求路径:" + beforePath);
// 请求前
System.out.println("filter -> before");
// 如果不为空,就通过
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 获取请求地址
String afterPath = request.getPath().pathWithinApplication().value();
// 获取响应状态码
HttpStatus afterStatusCode = response.getStatusCode();
System.out.println("响应码:" + afterStatusCode + ",请求路径:" + afterPath);
// 响应后
System.out.println("filter -> after");
}));
}
}
8. 全局过滤器,不需要配置在配置文件中,作用于所有路由;只是这里在处理前做了判断,只有路径中存在
routeAll 字符串才到后续处理;并且处理分为请求前的处理,和响应后的处理 (这块代码已经注释,需要测试可以解除即可,此次直接使用Headers中获取token)
9. 此时在地址:http://localhost:8000/all/test/routeAll 中Headers中添加 Authorization参数值为token
10. 访问:http://localhost:8000/all/test/routeAll
11. 轮流输出内容:'Can I pass? yes! port:8081'
12. 观察 gateway 工程的控制台,会有如下内容输出
响应码:200 OK,请求路径:/test/routeAll
filter -> before
响应码:200 OK,请求路径:/test/routeAll
filter -> after
13. 证明全局过滤器过滤成功
说明:
这块gateway参考 https://blog.csdn.net/qq_41402200/article/details/94333830
其它是使用oauth2结合springcloud实现单点登陆以及授权操作。
更多推荐
所有评论(0)