业务流程
在上一节中我们已经引入了权限认证组件,我们希望如下图一样达到一个针对请求的鉴权功能
所以,本节就是要利用上一节做的权限认证功能进行实现API网关的权限仍证handler.
现在我们来回顾下,在未引入权限认证功能之时,我们的http请求是直接被接收处理后,开启会话进行请求RPC服务,而现在我们要进行权限认证,那么就不能再直接处理消息调用泛化调用,而是在原有基础上,进消息的接收和权限的验证后,再调用原有的消息处理和请求RPC服务。
即将网络请求分为三段:消息接收、请求鉴权、消息处理。这里的请求鉴权由 Shrio + JWT 完成。
为了满足消息的处理和鉴权,本节需要再引入2个Netty消息处理的Handler,分别是AuthorizationHandler、ProtocolDataHandler
【新增】AuthorizationHandler 做接口的鉴权
【新增】ProtocolDataHandler 做协议数据的处理
【修改】GatewayServerHandler 原有网络协议处理类,作为第一个Netty通信管道中的处理类,只做简单参数解析,保存到管道中后即可放行。—— 后续再有一些需要优先处理的逻辑也会放到这个 GatewayServerHandler 类里完成。
除了上述本章最主要的功能模块外,还有一些其它的设计:
【新增】GatewayResultMessage 做网关最后的返回结果封装
【新增】AgreementConstants 保存协议解析的参数,供整个channal后续的handler可以看到,并且定义网关返回结果的枚举
【修改】原执行器下的GatewayResult 变为 SessionResult(也就改了个名字),以让执行器返回的结果(实际上就是会话层最后返回的东西)与网关最后封装的结果做区分。
【修改】HttpStatement 增添关于映射的请求是否需要鉴权的属性,这是因为某些网页可以不加鉴权的访问,例如淘宝的商品页面
本章节代码模块如下
流程实现
网关结果封装
首先我们先来定义最后的网关结果的返回(其实与以往做过项目的封装几乎一致)
package com.zshunbao.gateway.socket.agreement;
/**
* @program: api-gateway-core
* @ClassName GatewayResultMessage
* @description: 网关结果封装
* @author: zs宝
* @create: 2025-08-15 15:26
* @Version 1.0
**/
public class GatewayResultMessage {
private String code;
private String info;
private Object data;
protected GatewayResultMessage(String code, String info, Object data) {
this.code = code;
this.info = info;
this.data = data;
}
public static GatewayResultMessage buildSuccess(Object data) {
return new GatewayResultMessage(AgreementConstants.ResponseCode._200.getCode(), AgreementConstants.ResponseCode._200.getInfo(), data );
}
public static GatewayResultMessage buildError(String code, String info) {
return new GatewayResultMessage(code, info, null);
}
public String getCode() {
return code;
}
public String getInfo() {
return info;
}
public Object getData() {
return data;
}
}
然后是关于封装结果的code,info的枚举类
package com.zshunbao.gateway.socket.agreement;
import io.netty.util.AttributeKey;
import com.zshunbao.gateway.mapping.HttpStatement;
/**
* @program: api-gateway-core
* @ClassName AgreementConstants
* @description: 自定义网关返回结果的业务参数
* @author: zs宝
* @create: 2025-08-15 15:26
* @Version 1.0
**/
public class AgreementConstants {
public static final AttributeKey<HttpStatement> HTTP_STATEMENT = AttributeKey.valueOf("HttpStatement");
public enum ResponseCode {
// 访问成功
_200("200","访问成功"),
// 后端接收数据的数据类型不匹配 ,比如前端传送的数据时string,后端使用的是Integer数据类型接收,此时就会包以上错误
_400("400","接收数据的数据类型不匹配"),
// (禁止) 服务器拒绝请求。资源不可用,服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致,比如IIS或者apache设置了访问权限不当。
_403("403","服务器拒绝请求"),
// (未找到) 服务器找不到请求的网页;输入链接有误。第一个4表示客户端出错,第二个 0 表示你把网址打错了,最后的那个4表示 “Not Found”,即找不到网页。
_404("404","服务器找不到请求的网页,输入链接有误"),
// (服务器内部错误) 服务器遇到错误,无法完成请求。
_500("500","服务器遇到错误,无法完成请求"),
// (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。如 RPC 提供方宕机。
_502("502","服务器作为网关或代理,从上游服务器收到无效响应");
private String code;
private String info;
ResponseCode(String code, String info) {
this.code = code;
this.info = info;
}
public String getCode() {
return code;
}
public String getInfo() {
return info;
}
}
}
注意这其中有个HTTP_STATEMENT
其中定义着当前channel可以共享的有关的映射信息。
鉴权
现在我们来看下主要的鉴权功能的handler
首先是消息进入,我们要对其做接收处理
package com.zshunbao.gateway.socket.handlers;
import com.zshunbao.gateway.mapping.HttpStatement;
import com.zshunbao.gateway.session.Configuration;
import com.zshunbao.gateway.socket.BaseHandler;
import com.zshunbao.gateway.socket.agreement.AgreementConstants;
import com.zshunbao.gateway.socket.agreement.GatewayResultMessage;
import com.zshunbao.gateway.socket.agreement.RequestParser;
import com.zshunbao.gateway.socket.agreement.ResponseParser;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @program: api-gateway-core
* @ClassName GatewayServerHandler
* @description: 网络协议处理器
* @author: zs宝
* @create: 2025-08-15 15:37
* @Version 1.0
**/
public class GatewayServerHandler extends BaseHandler<FullHttpRequest> {
private final Logger logger = LoggerFactory.getLogger(GatewayServerHandler.class);
private final Configuration configuration;
public GatewayServerHandler(Configuration configuration) {
this.configuration = configuration;
}
@Override
protected void session(ChannelHandlerContext ctx, Channel channel, FullHttpRequest request) {
logger.info("网关接收请求【全局】 uri:{} method:{}", request.uri(), request.method());
try {
//1. 解析参数
RequestParser requestParser = new RequestParser(request);
String uri = requestParser.getUri();
//2、保存信息;HttpStatement、Header=token
HttpStatement httpStatement = configuration.getHttpStatement(uri);
channel.attr(AgreementConstants.HTTP_STATEMENT).set(httpStatement);
//3、放行
request.retain();
ctx.fireChannelRead(request);
} catch (Exception e) {
// 4. 封装返回结果
DefaultFullHttpResponse response = new ResponseParser().parse(GatewayResultMessage.buildError(AgreementConstants.ResponseCode._500.getCode(), "网关协议调用失败!" + e.getMessage()));
channel.writeAndFlush(response);
}
}
}
在这个地方,我们将映射信息进行保存,方便后续handler可以直接使用。
GatewayServerHandler 处理类由原来的处理数据,修改解析参数,获取 HttpStatement 操作。
因为获取 HttpStatement 后可以保存到管道的属性信息中,所有的这条通信链上都可以获取到,这样到鉴权处理中直接获取到信息就可以操作了。
这里不会获取到会话的信息(gatewaySessionFactory.openSession(uri)),避免如果鉴权都鉴权失败了,创建会话服务也是浪费资源。所以只需要在构造函数中传输 Configuration 即可,用于根据 URI 获取 HttpStatement 网关接口映射信息,方便拿到是否需要鉴权。
然后当消息接收后,就要进行鉴权处理
package com.zshunbao.gateway.socket.handlers;
import com.zshunbao.gateway.mapping.HttpStatement;
import com.zshunbao.gateway.session.Configuration;
import com.zshunbao.gateway.socket.BaseHandler;
import com.zshunbao.gateway.socket.agreement.AgreementConstants;
import com.zshunbao.gateway.socket.agreement.GatewayResultMessage;
import com.zshunbao.gateway.socket.agreement.ResponseParser;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @program: api-gateway-core
* @ClassName AuthorizationHandler
* @description: 鉴权hanlder
* @author: zs宝
* @create: 2025-08-15 15:49
* @Version 1.0
**/
public class AuthorizationHandler extends BaseHandler<FullHttpRequest> {
private final Logger logger = LoggerFactory.getLogger(AuthorizationHandler.class);
private final Configuration configuration;
public AuthorizationHandler(Configuration configuration) {
this.configuration = configuration;
}
@Override
protected void session(ChannelHandlerContext ctx, Channel channel, FullHttpRequest request) {
logger.info("网关接收请求【鉴权】 uri:{} method:{}", request.uri(), request.method());
try {
HttpStatement httpStatement = channel.attr(AgreementConstants.HTTP_STATEMENT).get();
if(httpStatement.isAuth()){
try {
//先拿到鉴权所需要的配置
String token = request.headers().get("token");
String uid = request.headers().get("uid");
//进行鉴权:shiro + jwt
boolean validate = configuration.validate(uid, token);
if(validate){
//鉴权通过,放行
request.retain();
ctx.fireChannelRead(request);
}else {
//鉴权为通过,直接返回结果
DefaultFullHttpResponse response = new ResponseParser().parse(GatewayResultMessage.buildError(AgreementConstants.ResponseCode._403.getCode(), "对不起,你无权访问此接口!"));
channel.writeAndFlush(response);
}
}catch (Exception e){
DefaultFullHttpResponse response = new ResponseParser().parse(GatewayResultMessage.buildError(AgreementConstants.ResponseCode._403.getCode(), "对不起,你的鉴权不合法!"));
channel.writeAndFlush(response);
}
}else {
//不需要鉴权,直接放行
request.retain();
ctx.fireChannelRead(request);
}
}catch (Exception e){
// 4. 封装返回结果
DefaultFullHttpResponse response = new ResponseParser().parse(GatewayResultMessage.buildError(AgreementConstants.ResponseCode._500.getCode(), "网关协议调用失败!" + e.getMessage()));
channel.writeAndFlush(response);
}
}
}
这里其实就是根据映射信息HttpStatement中的属性字段auth来判断当前访问的资源是否需要鉴权,不需要则直接放行,需要则进行鉴权。
不过这里的鉴权操作不是直接调用的上一节我们定义的组件,而是在configuration中定义,我们将对应的鉴权配置到了Configuration 配置项中,做统一的管理。
最后如果鉴权handler正常通过,那么接下来就和之前的操作一样,先解析参数,然后调用RPC服务,封装结果
/**
* @program: api-gateway-core
* @ClassName ProtocolDataHandler
* @description:
* @author: zs宝
* @create: 2025-08-04 15:33
* @Version 1.0
**/
package com.zshunbao.gateway.socket.handlers;
import com.zshunbao.gateway.bind.IGenericReference;
import com.zshunbao.gateway.executor.result.SessionResult;
import com.zshunbao.gateway.session.GatewaySession;
import com.zshunbao.gateway.session.defaults.DefaultGatewaySessionFactory;
import com.zshunbao.gateway.socket.BaseHandler;
import com.zshunbao.gateway.socket.agreement.AgreementConstants;
import com.zshunbao.gateway.socket.agreement.GatewayResultMessage;
import com.zshunbao.gateway.socket.agreement.RequestParser;
import com.zshunbao.gateway.socket.agreement.ResponseParser;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
public class ProtocolDataHandler extends BaseHandler<FullHttpRequest> {
private final Logger logger = LoggerFactory.getLogger(ProtocolDataHandler.class);
private final DefaultGatewaySessionFactory gatewaySessionFactory;
public ProtocolDataHandler(DefaultGatewaySessionFactory gatewaySessionFactory) {
this.gatewaySessionFactory = gatewaySessionFactory;
}
@Override
protected void session(ChannelHandlerContext ctx, Channel channel, FullHttpRequest request) {
logger.info("网关接收请求 uri:{} method:{}", request.uri(), request.method());
try {
//1、解析请求参数
RequestParser requestParser = new RequestParser(request);
String uri= requestParser.getUri();
if (uri==null || uri.equals("/favicon.ico")) return;
Map<String, Object> args = requestParser.parse();
//2、创建会话
GatewaySession gatewaySession = gatewaySessionFactory.openSession(uri);
//根据uri获得对应的mapper映射,即本地动态创建的代理类
IGenericReference reference = gatewaySession.getMapper();
//执行代理类的方法,这个方法会被拦截,然后执行Dubbo引用缓存到本地真正的泛化代理对象,从而调用远程方法执行,得到结果
SessionResult result = reference.$invoke(args);
//3、封装返回结果:注意由sessionResult到网关定义的返回结果的转化
DefaultFullHttpResponse response = new ResponseParser().parse("0000".equals(result.getCode()) ? GatewayResultMessage.buildSuccess(result.getData()) : GatewayResultMessage.buildError(AgreementConstants.ResponseCode._404.getCode(), "网关协议调用失败!"));
channel.writeAndFlush(response);
} catch (Exception e) {
//4封装错误的结果
DefaultFullHttpResponse response=new ResponseParser().parse(GatewayResultMessage.buildError(AgreementConstants.ResponseCode._502.getCode(),"网关调用失败"+e.getMessage()));
channel.writeAndFlush(response);
}
}
}
注意这里最后的返回结果做了封装(这是唯一与之前不同的地方)
到此其实本节的主要内容已经写完了
修改部分
这一部分主要展示一些修改了的部分代码,都是小修改
Configuration增减鉴权封装
/**
* @program: api-gateway-core
* @ClassName Configuration
* @description:
* @author: zs宝
* @create: 2025-08-04 14:56
* @Version 1.0
**/
package com.zshunbao.gateway.session;
import com.zshunbao.gateway.authorization.IAuth;
import com.zshunbao.gateway.authorization.auth.AuthService;
import com.zshunbao.gateway.bind.IGenericReference;
import com.zshunbao.gateway.bind.MapperRegistry;
import com.zshunbao.gateway.datasource.Connection;
import com.zshunbao.gateway.executor.Executor;
import com.zshunbao.gateway.executor.SimpleExecutor;
import com.zshunbao.gateway.mapping.HttpStatement;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericService;
import java.util.HashMap;
import java.util.Map;
public class Configuration {
//对应的注册器
private final MapperRegistry mapperRegistry=new MapperRegistry(this);
private final Map<String, HttpStatement> httpStatements = new HashMap<>();
//根据官方文档的示例,dubbo的泛化调用的API调用方式主要有3个东西需要配置
//分别是ApplicationConfig,RegistryConfig,ReferenceConfig<GenericService>
//但是我们这是一个网关,被调用的服务方可能不止一个,因此,我们需要按照对应的服务名和配置名,将其对应起来保存
//RPC 应用服务配置项
//注意键为应用名称;值为应用服务配置项
private final Map<String, ApplicationConfig> applicationConfigMap=new HashMap<>();
//RPC 注册中心配置项
//键为应用名称;值为注册中心配置项
private final Map<String, RegistryConfig> registryConfigMap=new HashMap<>();
//RPC 泛化服务配置项
//注意这里的键:远程服务接口的全限定类名 ; 值为泛化引用配置对象
private final Map<String, ReferenceConfig<GenericService>> referenceConfigMap=new HashMap<>();
//配置鉴权
private IAuth auth=new AuthService();
public Configuration(){
//TODO 后期从配置中获取,本节主要内容是泛化服务的调用
ApplicationConfig applicationConfig=new ApplicationConfig();
applicationConfig.setName("api-gateway-test");
applicationConfig.setQosEnable(false);
RegistryConfig registryConfig=new RegistryConfig();
//配置应用在那个注册中心可以被调用
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
registryConfig.setRegister(false);
//对应的泛化服务配置
ReferenceConfig<GenericService> reference=new ReferenceConfig<>();
//配置泛化调用的服务方接口
reference.setInterface("cn.bugstack.gateway.rpc.IActivityBooth");
reference.setVersion("1.0.0");
reference.setGeneric("true");
//加入缓存中去
applicationConfigMap.put("api-gateway-test",applicationConfig);
registryConfigMap.put("api-gateway-test",registryConfig);
referenceConfigMap.put("cn.bugstack.gateway.rpc.IActivityBooth",reference);
}
//对应的服务配置项对外get方法
public ApplicationConfig getApplicationConfig(String applicationName) {
return applicationConfigMap.get(applicationName);
}
//对应的对外注册中心的get方法
public RegistryConfig getRegistryConfig(String applicationName) {
return registryConfigMap.get(applicationName);
}
//对应的对外泛化调用配置项get方法
public ReferenceConfig<GenericService> getReferenceConfig(String interfaceName) {
return referenceConfigMap.get(interfaceName);
}
//添加泛化调用方法
public void addMapper(HttpStatement httpStatement) {
mapperRegistry.addMapper(httpStatement);
}
public IGenericReference getMapper(String uri, GatewaySession gatewaySession) {
return mapperRegistry.getMapper(uri, gatewaySession);
}
public void addHttpStatement(HttpStatement httpStatement) {
httpStatements.put(httpStatement.getUri(), httpStatement);
}
public HttpStatement getHttpStatement(String uri) {
return httpStatements.get(uri);
}
public Executor newExecutor(Connection connection) {
return new SimpleExecutor(this,connection);
}
public boolean validate(String uid, String token) {
return auth.validate(uid,token);
}
}
HttpStatement映射信息增加是否鉴权字段
/**
* @program: api-gateway-core
* @ClassName HttpStatement
* @description: 网关接口映射信息
* @author: zs宝
* @create: 2025-08-04 14:40
* @Version 1.0
**/
package com.zshunbao.gateway.mapping;
public class HttpStatement {
/** 应用名称; */
private String application;
/** 服务接口;RPC、其他 */
private String interfaceName;
/** 服务方法;RPC#method */
private String methodName;
/** 参数类型(RPC 限定单参数注册);new String[]{"java.lang.String"}、new String[]{"cn.bugstack.gateway.rpc.dto.XReq"} */
private String parameterType;
/** 网关接口 */
private String uri;
/** 接口类型;GET、POST、PUT、DELETE */
private HttpCommandType httpCommandType;
/** 是否需要鉴权处理 true=是 false=否 */
private boolean auth;
public HttpStatement(String application, String interfaceName, String methodName, String parameterType, String uri, HttpCommandType httpCommandType, boolean auth){
this.application = application;
this.interfaceName = interfaceName;
this.methodName = methodName;
this.parameterType = parameterType;
this.uri = uri;
this.httpCommandType = httpCommandType;
this.auth = auth;
}
public String getApplication() {
return application;
}
public String getInterfaceName() {
return interfaceName;
}
public String getMethodName() {
return methodName;
}
public String getUri() {
return uri;
}
public HttpCommandType getHttpCommandType() {
return httpCommandType;
}
public String getParameterType() {
return parameterType;
}
public boolean isAuth() {
return auth;
}
}
SessionChannelInitializer增加新定义的handler,注意GatewayServerHandler和AuthorizationHandler都只加载了Configuration配置类,没有加载会话工厂gatewaySessionFactory,因为鉴权可能不通过,就没有必要浪费资源加载会话,因为鉴权不通过根本不会有开启会话的机会
/**
* @program: api-gateway-core
* @ClassName SessionChannelInitializer
* @description:自定义netty服务端链接的childHandler的初始化工具
* @author: zs宝
* @create: 2025-07-25 16:56
* @Version 1.0
**/
package com.zshunbao.gateway.socket;
import com.zshunbao.gateway.session.Configuration;
import com.zshunbao.gateway.session.defaults.DefaultGatewaySessionFactory;
import com.zshunbao.gateway.socket.handlers.AuthorizationHandler;
import com.zshunbao.gateway.socket.handlers.GatewayServerHandler;
import com.zshunbao.gateway.socket.handlers.ProtocolDataHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
public class SessionChannelInitializer extends ChannelInitializer<SocketChannel>{
private DefaultGatewaySessionFactory gatewaySessionFactory;
private final Configuration configuration;
public SessionChannelInitializer(DefaultGatewaySessionFactory gatewaySessionFactory, Configuration configuration) {
this.gatewaySessionFactory = gatewaySessionFactory;
this.configuration = configuration;
}
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//得到处理的流水线
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpObjectAggregator(1024*1024));
pipeline.addLast(new GatewayServerHandler(configuration));
pipeline.addLast(new AuthorizationHandler(configuration));
pipeline.addLast(new ProtocolDataHandler(gatewaySessionFactory));
}
}
其次之前的鉴权组件,做了一点点小修改
package com.zshunbao.gateway.authorization;
import io.jsonwebtoken.Claims;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @program: api-gateway-core
* @ClassName GatewayAuthorizingRealm
* @description: 验证领域
* @author: zs宝
* @create: 2025-08-12 15:59
* @Version 1.0
**/
public class GatewayAuthorizingRealm extends AuthorizingRealm {
@Override
public Class<?> getAuthenticationTokenClass(){
return GatewayAuthorizingToken.class;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
try {
//验证解析是否报错
Claims claims = JwtUtil.decode(((GatewayAuthorizingToken) token).getJwt());
//验证签发人是否匹配
if(!token.getPrincipal().equals(claims.getSubject())){
throw new AuthenticationException("无效令牌");
}
}catch (Exception e){
throw new AuthenticationException("无效令牌");
}
return new SimpleAuthenticationInfo(token.getPrincipal(),token.getCredentials(),this.getName());
}
}
最后,对外统一的的有关动态代理方法执行的接口返回结果做了小修改
/**
* @program: api-gateway-core
* @ClassName IGenericReference
* @description: 统一泛化调用接口,无论怎样的HTTP请求,我们暴露出去的都是这个接口,一致
* @author: zs宝
* @create: 2025-07-29 15:53
* @Version 1.0
**/
package com.zshunbao.gateway.bind;
import com.zshunbao.gateway.executor.result.SessionResult;
import java.util.Map;
public interface IGenericReference {
SessionResult $invoke(Map<String, Object>params);
}
测试
测试代码
/**
* @program: api-gateway-core
* @ClassName ApiTest
* @description:
* @author: zs宝
* @create: 2025-08-09 17:21
* @Version 1.0
**/
package com.zshunbao.gateway.test;
import com.zshunbao.gateway.mapping.HttpCommandType;
import com.zshunbao.gateway.mapping.HttpStatement;
import com.zshunbao.gateway.session.Configuration;
import com.zshunbao.gateway.session.defaults.DefaultGatewaySessionFactory;
import com.zshunbao.gateway.socket.GatewaySocketServer;
import io.netty.channel.Channel;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ApiTest {
private final Logger logger = LoggerFactory.getLogger(ApiTest.class);
/**
* 测试:
* http://localhost:7397/wg/activity/sayHi
* 参数:
* {
* "str": "10001"
* }
*
* http://localhost:7397/wg/activity/insert
* 参数:
* {
* "name":"zshunbao",
* "uid":"10001"
* }
*/
@Test
public void test_gateway() throws InterruptedException, ExecutionException {
// 1. 创建配置信息加载注册
Configuration configuration = new Configuration();
HttpStatement httpStatement01 = new HttpStatement(
"api-gateway-test",
"cn.bugstack.gateway.rpc.IActivityBooth",
"sayHi",
"java.lang.String",
"/wg/activity/sayHi",
HttpCommandType.GET,
false
);
HttpStatement httpStatement02 = new HttpStatement(
"api-gateway-test",
"cn.bugstack.gateway.rpc.IActivityBooth",
"insert",
"cn.bugstack.gateway.rpc.dto.XReq",
"/wg/activity/insert",
HttpCommandType.POST,
true
);
configuration.addMapper(httpStatement01);
configuration.addMapper(httpStatement02);
// 2. 基于配置构建会话工厂
DefaultGatewaySessionFactory gatewaySessionFactory = new DefaultGatewaySessionFactory(configuration);
// 3. 创建启动网关网络服务
GatewaySocketServer server = new GatewaySocketServer(gatewaySessionFactory,configuration);
Future<Channel> future = Executors.newFixedThreadPool(2).submit(server);
Channel channel = future.get();
if (null == channel) throw new RuntimeException("netty server start error channel is null");
while (!channel.isActive()) {
logger.info("netty server gateway start Ing ...");
Thread.sleep(500);
}
logger.info("netty server gateway start Done! {}", channel.localAddress());
Thread.sleep(Long.MAX_VALUE);
}
}
注意由于本次测试主要时测试鉴权功能
因此在需要有自己的token
package com.zshunbao.gateway.test;
import com.zshunbao.gateway.authorization.IAuth;
import com.zshunbao.gateway.authorization.JwtUtil;
import com.zshunbao.gateway.authorization.auth.AuthService;
import io.jsonwebtoken.Claims;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class ShiroTest {
private final static Logger logger = LoggerFactory.getLogger(ShiroTest.class);
@Test
public void test_auth_service() {
IAuth auth = new AuthService();
boolean validate = auth.validate("zshunbao", "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4aWFvZnVnZSIsImV4cCI6MTY2NjQwNDAxMiwiaWF0IjoxNjY1Nzk5MjEyLCJrZXkiOiJ4aWFvZnVnZSJ9.Vs-ObO5OF2pYr7jkt0N4goq0hErOZNdyqfacHzbkfHM");
System.out.println(validate ? "验证成功" : "验证失败");
}
@Test
public void test_awt() {
String issuer = "zshunbao";
long ttlMillis = 7 * 24 * 60 * 60 * 1000L;
Map<String, Object> claims = new HashMap<>();
claims.put("key", "zshunbao");
// 编码
String token = JwtUtil.encode(issuer, ttlMillis, claims);
System.out.println(token);
// 解码
Claims parser = JwtUtil.decode(token);
System.out.println(parser.getSubject());
}
}
注意下面使用api_post测试时token由于时间过期的原因最好在测试时执行test_awt()
方法生成一个新的
token正确
token错误
不需要token的请求