Skip to content

Commit

Permalink
feat-增加了对jwt授权的支持并准备了对应的单元测试
Browse files Browse the repository at this point in the history
  • Loading branch information
aruis committed Feb 5, 2025
1 parent 41887c6 commit 7e220ae
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 44 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
implementation("io.quarkus:quarkus-rest-jackson")
implementation("io.quarkus:quarkus-reactive-routes")
implementation("io.vertx:vertx-web-client")
implementation("io.vertx:vertx-auth-jwt")

implementation(libs.commons.codes)

Expand Down
39 changes: 37 additions & 2 deletions src/main/java/net/ximatai/muyun/gateway/GatewayServer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package net.ximatai.muyun.gateway;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
Expand All @@ -9,6 +13,10 @@
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.net.PemKeyCertOptions;
import io.vertx.ext.auth.JWTOptions;
import io.vertx.ext.auth.PubSecKeyOptions;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.auth.jwt.JWTAuthOptions;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
Expand Down Expand Up @@ -58,6 +66,11 @@ public class GatewayServer {
private SessionStore store;
private SessionHandler sessionHandler;

private JWTOptions jwtOption;
private JWTAuth jwtAuth;
private ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory())
.setPropertyNamingStrategy(PropertyNamingStrategies.KEBAB_CASE);

@PostConstruct
void init() {
if (management.checkToken()) {
Expand All @@ -69,6 +82,13 @@ void init() {
}

public Future<Integer> register(GatewayConfig config) {

try {
logger.info("Config content is:\n {}", objectMapper.writeValueAsString(config));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

Promise<Integer> promise = Promise.promise();
routes.clear();

Expand Down Expand Up @@ -98,10 +118,25 @@ public Future<Integer> register(GatewayConfig config) {
router.route().handler(sessionHandler);
}

GatewayConfig.JwtConfig jwtConfig = config.getJwt();
if (jwtConfig.use()) {
jwtOption = new JWTOptions()
.setIgnoreExpiration(!jwtConfig.checkExpiration())
.setExpiresInMinutes(jwtConfig.expiresMin());

JWTAuthOptions jwtAuthOptions = new JWTAuthOptions()
.setJWTOptions(jwtOption)
.addPubSecKey(new PubSecKeyOptions()
.setAlgorithm("HS256")
.setBuffer("MuYun"));

jwtAuth = JWTAuth.create(vertx, jwtAuthOptions);
}

router.route("/").handler(this::indexHandler);
router.route(config.getLogin().path())
.handler(BodyHandler.create())
.handler(new LoginHandler(config, vertx));
.handler(new LoginHandler(config, vertx, jwtAuth));

router.route("/logout").handler(this::logoutHandler);

Expand Down Expand Up @@ -135,7 +170,7 @@ public Future<Integer> register(GatewayConfig config) {
}).toList();

handlers.add(new AllowListHandler(allowList));
handlers.add(new AuthHandler(config));
handlers.add(new AuthHandler(config, jwtAuth));
}

route.mountTo(router, handlers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public class ConfigService {
@PostConstruct
void init() throws JsonProcessingException {
logger.info("Loading config file: {}", configFilePath);
logger.info("Config content is:\n {}", objectMapper.writeValueAsString(loadConfig()));
}

@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public GatewayConfig(IGatewayConfig IGatewayConfig) {
this.jwt = new JwtConfig(
IGatewayConfig.jwt().use(),
IGatewayConfig.jwt().checkExpiration(),
IGatewayConfig.jwt().expiresMin().orElse(null)
IGatewayConfig.jwt().expiresMin().orElse(60)
);
this.session = new SessionConfig(
IGatewayConfig.session().use().orElse(true),
Expand Down
56 changes: 43 additions & 13 deletions src/main/java/net/ximatai/muyun/gateway/handler/AuthHandler.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package net.ximatai.muyun.gateway.handler;

import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.authentication.TokenCredentials;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.Session;
import net.ximatai.muyun.gateway.RoutingContextKeyConst;
Expand All @@ -16,34 +21,59 @@

public class AuthHandler implements Handler<RoutingContext> {

GatewayConfig config;
private final GatewayConfig config;
private final JWTAuth jwtAuth;

public AuthHandler(GatewayConfig config) {
public AuthHandler(GatewayConfig config, JWTAuth jwtAuth) {
this.config = config;
this.jwtAuth = jwtAuth;
}

@Override
public void handle(RoutingContext routingContext) {
boolean allowFlag = routingContext.get(ALLOW_FLAG, false)
|| Objects.equals(VAL_REROUTE_REASON_FRONTEND, routingContext.get(REROUTE_REASON));

JsonObject user = null;
Future.any(
loadUserFromSession(routingContext),
loadUserFromJwt(routingContext)
).onSuccess(compositeFuture -> {
JsonObject user = compositeFuture.resultAt(0);
if (user == null) {
user = compositeFuture.resultAt(1);
}
routingContext
.put(RoutingContextKeyConst.USER, user)
.next();
}).onFailure((throwable) -> {
if (allowFlag) {
routingContext.next();
} else {
reject(routingContext);
}
});

if (config.getSession().use()) {
Session session = routingContext.session();
user = session.get(USER_SESSION);
}
}

if (user != null) {
routingContext.put(RoutingContextKeyConst.USER, user);
private Future<JsonObject> loadUserFromJwt(RoutingContext routingContext) {
String authorizationHeader = routingContext.request().getHeader(HttpHeaders.AUTHORIZATION);

if (jwtAuth == null || authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
return Future.failedFuture("JWT authentication failed");
}

if (allowFlag || user != null) {
routingContext.next();
} else {
reject(routingContext);
String token = authorizationHeader.substring(7);
return jwtAuth.authenticate(new TokenCredentials(token))
.map(User::principal);
}

private Future<JsonObject> loadUserFromSession(RoutingContext routingContext) {
if (!config.getSession().use()) {
return Future.failedFuture("Session is disabled");
}

JsonObject user = routingContext.session().get(USER_SESSION);
return user != null ? Future.succeededFuture(user) : Future.failedFuture("Session does not exist");
}

private void reject(RoutingContext routingContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
Expand All @@ -19,12 +21,14 @@ public class LoginHandler implements Handler<RoutingContext> {
private final GatewayConfig gatewayConfig;
private final String loginApi;
private final WebClient webClient;
private final JWTAuth jwtAuth;

public static final String USER_SESSION = "gateway.user";

public LoginHandler(GatewayConfig gatewayConfig, Vertx vertx) {
public LoginHandler(GatewayConfig gatewayConfig, Vertx vertx, JWTAuth jwtAuth) {
this.gatewayConfig = gatewayConfig;
this.loginApi = gatewayConfig.getLogin().api();
this.jwtAuth = jwtAuth;

WebClientOptions options = new WebClientOptions()
.setIdleTimeout(10)
Expand Down Expand Up @@ -57,6 +61,11 @@ public void handle(RoutingContext routingContext) {
routingContext.session().put(USER_SESSION, user);
}

if (jwtAuth != null) { // 说明开启了 jwt 验证
String token = jwtAuth.generateToken(user);
response.putHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
}

response
.putHeader("content-type", "application/json")
.send(user.encode());
Expand Down
52 changes: 26 additions & 26 deletions src/main/resources/config/gateway-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,35 @@ gateway:
jwt:
use: true
check-expiration: false
expires-min: null
expires-min: 60
session:
use: true
timeout-hour: 24
redirects:
- from: "/api/sso/logout"
to: "/logout"
- from: "/api/sso/logout"
to: "/logout"
frontends:
- path: "/web"
dir: "/Users/aruis/develop/workspace-bsy/project_keeper/dist"
not-found-reroute: "/"
secured: true
regex: false
comment: null
no-cache:
- "/"
- "/index.html"
allowlist:
- ".*\\..*"
- "/login"
- path: "/web"
dir: "/Users/aruis/develop/workspace-bsy/project_keeper/dist"
not-found-reroute: "/"
secured: true
regex: false
comment: null
no-cache:
- "/"
- "/index.html"
allowlist:
- ".*\\..*"
- "/login"
upstreams:
- path: "/api"
secured: true
regex: false
comment: null
no-cache: []
allowlist:
- "/sso/kaptcha"
- "/platform/conf/get"
backends:
- url: "http://127.0.0.1:8080/api/"
weight: 1
- path: "/api"
secured: true
regex: false
comment: null
no-cache: [ ]
allowlist:
- "/sso/kaptcha"
- "/platform/conf/get"
backends:
- url: "http://127.0.0.1:8080/api/"
weight: 1
Loading

0 comments on commit 7e220ae

Please sign in to comment.