Skip to content

Commit 71d21b2

Browse files
committed
Add auth layer handling for API Key
1 parent 15e8e24 commit 71d21b2

File tree

6 files changed

+88
-10
lines changed

6 files changed

+88
-10
lines changed

server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/util/CookieHelper.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,6 @@ public String getCookieToken(ServerWebExchange exchange) {
5353
return getCookieValue(exchange, getCookieName(), "");
5454
}
5555

56-
@Nullable
57-
public String getJWT(ServerWebExchange exchange) {
58-
return getCookieValue(exchange, "JWT", null);
59-
}
60-
6156
public String getCookieValue(ServerWebExchange exchange, String cookieName, String defaultValue) {
6257
MultiValueMap<String, HttpCookie> cookies = exchange.getRequest().getCookies();
6358
return ofNullable(cookies.getFirst(cookieName))

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/util/JWTUtils.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
import io.jsonwebtoken.Jwts;
66
import io.jsonwebtoken.SignatureAlgorithm;
77
import jakarta.annotation.PostConstruct;
8-
import jakarta.servlet.http.HttpServletRequest;
8+
import lombok.extern.slf4j.Slf4j;
99
import org.lowcoder.domain.user.model.User;
1010
import org.lowcoder.sdk.config.AuthProperties;
1111
import org.springframework.beans.factory.annotation.Autowired;
1212
import org.springframework.stereotype.Component;
13+
import org.springframework.web.server.ServerWebExchange;
14+
1315
import java.util.Random;
1416

1517
import java.util.Date;
1618

1719
@Component
20+
@Slf4j(topic = "JWTUtils")
1821
public class JWTUtils {
1922

2023
@Autowired
@@ -43,13 +46,18 @@ public String createToken(User user) {
4346
.compact();
4447
}
4548

46-
private Claims parseJwtClaims(String token) {
47-
return jwtParser.parseClaimsJws(token).getBody();
49+
public Claims parseJwtClaims(String token) {
50+
try {
51+
return jwtParser.parseClaimsJws(token).getBody();
52+
} catch (Exception e) {
53+
log.warn("Failed to validate token. Exception: ", e);
54+
return null;
55+
}
4856
}
4957

50-
public String resolveToken(HttpServletRequest request) {
58+
public String resolveToken(ServerWebExchange exchange) {
5159

52-
String bearerToken = request.getHeader(TOKEN_HEADER);
60+
String bearerToken = exchange.getRequest().getHeaders().getFirst(TOKEN_HEADER);
5361
if (bearerToken != null && bearerToken.startsWith(TOKEN_PREFIX)) {
5462
return bearerToken.substring(TOKEN_PREFIX.length());
5563
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.lowcoder.api.framework.filter;
2+
3+
import io.jsonwebtoken.Claims;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.lowcoder.api.authentication.util.JWTUtils;
6+
import org.lowcoder.api.home.SessionUserService;
7+
import org.lowcoder.sdk.util.CookieHelper;
8+
import org.springframework.web.server.ServerWebExchange;
9+
import org.springframework.web.server.WebFilter;
10+
import org.springframework.web.server.WebFilterChain;
11+
import reactor.core.publisher.Mono;
12+
13+
import javax.annotation.Nonnull;
14+
15+
import static org.lowcoder.api.authentication.util.AuthenticationUtils.toAuthentication;
16+
import static org.springframework.security.core.context.ReactiveSecurityContextHolder.withAuthentication;
17+
18+
@Slf4j
19+
public class APIKeyAuthFilter implements WebFilter {
20+
21+
private final SessionUserService service;
22+
23+
private final CookieHelper cookieHelper;
24+
private final JWTUtils jwtUtils;
25+
26+
public APIKeyAuthFilter(SessionUserService service, CookieHelper cookieHelper, JWTUtils jwtUtils) {
27+
this.service = service;
28+
this.cookieHelper = cookieHelper;
29+
this.jwtUtils = jwtUtils;
30+
}
31+
32+
@Nonnull
33+
@Override
34+
public Mono<Void> filter(@Nonnull ServerWebExchange exchange, WebFilterChain chain) {
35+
String cookieToken = cookieHelper.getCookieToken(exchange);
36+
if(cookieToken.isEmpty()) {
37+
String jwtToken = jwtUtils.resolveToken(exchange);
38+
if(jwtToken == null || jwtToken.isEmpty()) {
39+
return chain.filter(exchange);
40+
} else {
41+
Claims claims = jwtUtils.parseJwtClaims(jwtToken);
42+
if(claims == null) {
43+
return chain.filter(exchange);
44+
} else {
45+
return service.resolveSessionUserForJWT(claims, jwtToken)
46+
.flatMap(user -> chain.filter(exchange).contextWrite(withAuthentication(toAuthentication(user))));
47+
}
48+
49+
}
50+
} else {
51+
return chain.filter(exchange);
52+
}
53+
}
54+
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import org.lowcoder.api.authentication.request.AuthRequestFactory;
55
import org.lowcoder.api.authentication.service.AuthenticationApiServiceImpl;
6+
import org.lowcoder.api.authentication.util.JWTUtils;
7+
import org.lowcoder.api.framework.filter.APIKeyAuthFilter;
68
import org.lowcoder.api.framework.filter.UserSessionPersistenceFilter;
79
import org.lowcoder.api.home.SessionUserService;
810
import org.lowcoder.domain.authentication.AuthenticationService;
@@ -66,6 +68,9 @@ public class SecurityConfig {
6668
@Autowired
6769
AuthRequestFactory<AuthRequestContext> authRequestFactory;
6870

71+
@Autowired
72+
JWTUtils jwtUtils;
73+
6974
@Bean
7075
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
7176

@@ -149,6 +154,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
149154
);
150155

151156
http.addFilterBefore(new UserSessionPersistenceFilter(sessionUserService, cookieHelper, authenticationService, authenticationApiService, authRequestFactory), SecurityWebFiltersOrder.AUTHENTICATION);
157+
http.addFilterBefore(new APIKeyAuthFilter(sessionUserService, cookieHelper, jwtUtils), SecurityWebFiltersOrder.AUTHENTICATION);
152158

153159
return http.build();
154160
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/SessionUserService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.lowcoder.api.home;
22

3+
import io.jsonwebtoken.Claims;
34
import org.lowcoder.domain.organization.model.OrgMember;
45
import org.lowcoder.domain.user.model.User;
56
import org.lowcoder.infra.annotation.NonEmptyMono;
@@ -29,5 +30,7 @@ public interface SessionUserService {
2930

3031
Mono<User> resolveSessionUserFromCookie(String token);
3132

33+
Mono<User> resolveSessionUserForJWT(Claims claims, String token);
34+
3235
Mono<Boolean> tokenExist(String token);
3336
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/SessionUserServiceImpl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.time.Duration;
99
import java.util.Objects;
1010

11+
import io.jsonwebtoken.Claims;
1112
import org.apache.commons.lang3.StringUtils;
1213
import org.lowcoder.api.usermanagement.UserApiService;
1314
import org.lowcoder.domain.organization.model.OrgMember;
@@ -139,6 +140,17 @@ public Mono<User> resolveSessionUserFromCookie(String token) {
139140
.filter(user -> user.getState() != UserState.DELETED);
140141
}
141142

143+
@Override
144+
public Mono<User> resolveSessionUserForJWT(Claims claims, String token) {
145+
String userId = claims.get("userId").toString();
146+
return userService.findById(userId)
147+
.filter(user -> user.getState() != UserState.DELETED)
148+
.filter(user -> {
149+
long apiKeyFound = user.getApiKeysList().stream().filter(apiKey -> apiKey.getToken().equals(token)).count();
150+
return apiKeyFound > 0;
151+
});
152+
}
153+
142154
@Override
143155
public Mono<Boolean> tokenExist(String token) {
144156
return reactiveTemplate.hasKey(token);

0 commit comments

Comments
 (0)