Skip to content

Commit

Permalink
perf($APIGateway): refine remote API calling
Browse files Browse the repository at this point in the history
  • Loading branch information
钟俊 committed Jan 28, 2021
1 parent 18f5843 commit 4f5a20d
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
package com.jmsoftware.maf.apigateway.remoteapi;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListPayload;
import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListResponse;
import com.jmsoftware.maf.common.domain.authcenter.permission.PermissionType;
import com.jmsoftware.maf.common.domain.authcenter.role.GetRoleListByUserIdResponse;
import com.jmsoftware.maf.common.domain.authcenter.security.ParseJwtResponse;
import com.jmsoftware.maf.common.domain.authcenter.user.GetUserByLoginTokenResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

/**
* <h1>AuthCenterRemoteApi</h1>
Expand All @@ -26,18 +31,29 @@
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com
* @date 5/10/20 4:50 PM
*/
@Slf4j
@Service
@Validated
@RequiredArgsConstructor
public class AuthCenterRemoteApi {
private static final String SERVICE_NAME = "auth-center";
private final WebClient.Builder webClientBuilder;

/**
* Gets user by login token.
*
* @param loginToken the login token, e.q. username, email or phone number
* @return the user by login token
*/
@GetMapping("/user-remote-api/users/{loginToken}")
Mono<ResponseBodyBean<GetUserByLoginTokenResponse>> getUserByLoginToken(@PathVariable String loginToken) {
return null;
public Mono<GetUserByLoginTokenResponse> getUserByLoginToken(@PathVariable String loginToken) {
return webClientBuilder
.build()
.get()
.uri(String.format("http://%s/user-remote-api/users/{loginToken}", SERVICE_NAME), loginToken)
.retrieve()
.bodyToMono(ResponseBodyBean.class)
.map(ResponseBodyBean::getData)
.map(data -> JSONUtil.toBean(JSONUtil.parseObj(data), GetUserByLoginTokenResponse.class));
}

/**
Expand All @@ -46,33 +62,56 @@ Mono<ResponseBodyBean<GetUserByLoginTokenResponse>> getUserByLoginToken(@PathVar
* @param userId the user id
* @return the role list by user id
*/
@GetMapping("/role-remote-api/roles/{userId}")
Mono<ResponseBodyBean<GetRoleListByUserIdResponse>> getRoleListByUserId(@PathVariable Long userId) {
return null;
public Mono<List<GetRoleListByUserIdResponse.Role>> getRoleListByUserId(@NotNull Long userId) {
return webClientBuilder
.build()
.get()
.uri(String.format("http://%s/role-remote-api/roles/{userId}", SERVICE_NAME), userId)
.retrieve()
.bodyToMono(ResponseBodyBean.class)
.map(ResponseBodyBean::getData)
.map(data -> JSONUtil.toList(JSONUtil.parseObj(data).getJSONArray("roleList"),
GetRoleListByUserIdResponse.Role.class));
}

/**
* Get permission list by role id list
*
* @param roleIdList the role id list
* @param permissionTypeList the permission type list
* @param payload the payload
* @return the response body bean
*/
@RequestMapping(value = "/permission-remote-api/permissions", method = GET)
Mono<ResponseBodyBean<GetPermissionListByRoleIdListResponse>> getPermissionListByRoleIdList(
@Valid @RequestParam("roleIdList") List<@NotNull Long> roleIdList,
@Valid @RequestParam("permissionTypeList") List<@NotNull PermissionType> permissionTypeList) {
return null;
public Mono<List<GetPermissionListByRoleIdListResponse.Permission>> getPermissionListByRoleIdList(@Valid GetPermissionListByRoleIdListPayload payload) {
return webClientBuilder
.build()
.get()
.uri(uriBuilder -> uriBuilder
.host("auth-center")
.path("/permission-remote-api/permissions")
.queryParam("roleIdList", StrUtil.join(",", payload.getRoleIdList()))
.queryParam("permissionTypeList", StrUtil.join(",", payload.getPermissionTypeList()))
.build())
.retrieve()
.bodyToMono(ResponseBodyBean.class)
.map(ResponseBodyBean::getData)
.map(data -> JSONUtil.toList(JSONUtil.parseObj(data).getJSONArray("permissionList"),
GetPermissionListByRoleIdListResponse.Permission.class));
}

/**
* Parse mono.
* Parse JWT.
*
* @param headers the HTTP headers
* @param authorization the authorization
* @return the mono
*/
@GetMapping("/jwt-remote-api/parse")
Mono<ResponseBodyBean<ParseJwtResponse>> parse(@RequestHeader Map<String, String> headers) {
return null;
public Mono<ParseJwtResponse> parse(@NotEmpty String authorization) {
return webClientBuilder
.build()
.get()
.uri("http://auth-center/jwt-remote-api/parse")
.headers(httpHeaders -> httpHeaders.set(HttpHeaders.AUTHORIZATION, authorization))
.retrieve()
.bodyToMono(ResponseBodyBean.class).map(ResponseBodyBean::getData)
.map(data -> JSONUtil.toBean(JSONUtil.parseObj(data), ParseJwtResponse.class));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jmsoftware.maf.apigateway.security.configuration;

import com.jmsoftware.maf.apigateway.remoteapi.AuthCenterRemoteApi;
import com.jmsoftware.maf.apigateway.security.impl.*;
import com.jmsoftware.maf.reactivespringbootstarter.configuration.MafConfiguration;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -36,6 +37,7 @@
@RequiredArgsConstructor
public class WebFluxSecurityConfiguration {
private final MafConfiguration mafConfiguration;
private final AuthCenterRemoteApi authCenterRemoteApi;

@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http,
Expand Down Expand Up @@ -89,16 +91,17 @@ public PasswordEncoder passwordEncoder() {

@Bean
public ReactiveAuthorizationManager<AuthorizationContext> reactiveAuthorizationManager() {
return new RbacReactiveAuthorizationManagerImpl();
return new RbacReactiveAuthorizationManagerImpl(authCenterRemoteApi);
}

@Bean
public ServerSecurityContextRepository serverSecurityContextRepository(ReactiveAuthenticationManager reactiveAuthenticationManager) {
return new JwtReactiveServerSecurityContextRepositoryImpl(mafConfiguration, reactiveAuthenticationManager);
return new JwtReactiveServerSecurityContextRepositoryImpl(mafConfiguration, reactiveAuthenticationManager,
authCenterRemoteApi);
}

@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return new JwtReactiveAuthenticationManagerImpl();
return new JwtReactiveAuthenticationManagerImpl(authCenterRemoteApi);
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
package com.jmsoftware.maf.apigateway.security.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.apigateway.remoteapi.AuthCenterRemoteApi;
import com.jmsoftware.maf.common.domain.authcenter.security.UserPrincipal;
import com.jmsoftware.maf.common.domain.authcenter.user.GetUserByLoginTokenResponse;
import com.jmsoftware.maf.common.exception.BusinessException;
import com.jmsoftware.maf.common.exception.SecurityException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
* Description: JwtReactiveAuthenticationManagerImpl
* <p>
Expand All @@ -30,8 +24,7 @@
@Slf4j
@RequiredArgsConstructor
public class JwtReactiveAuthenticationManagerImpl implements ReactiveAuthenticationManager {
@Resource
private WebClient.Builder webClientBuilder;
private final AuthCenterRemoteApi authCenterRemoteApi;

private final UserDetailsChecker preAuthenticationChecks = user -> {
if (!user.isAccountNonLocked()) {
Expand Down Expand Up @@ -63,19 +56,11 @@ private Mono<UserDetails> retrieveUser(String username) {
return Mono.error(
new SecurityException(HttpStatus.NETWORK_AUTHENTICATION_REQUIRED, "Username mustn't be blank"));
}
val response = webClientBuilder
.build()
.get()
.uri("http://auth-center/user-remote-api/users/{loginToken}", username)
.retrieve()
.bodyToMono(ResponseBodyBean.class);
return response.map(ResponseBodyBean::getData)
return authCenterRemoteApi.getUserByLoginToken(username)
.switchIfEmpty(Mono.error(new BusinessException("Authentication failure! Cause: User not found")))
.map(data -> {
log.info("Authentication success! Found {}", data);
val getUserByLoginTokenResponse = JSONUtil.toBean(JSONUtil.parseObj(data),
GetUserByLoginTokenResponse.class);
return UserPrincipal.create(getUserByLoginTokenResponse, null, null);
return UserPrincipal.create(data, null, null);
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.jmsoftware.maf.apigateway.security.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.jmsoftware.maf.apigateway.remoteapi.AuthCenterRemoteApi;
import com.jmsoftware.maf.apigateway.security.configuration.JwtConfiguration;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.common.domain.authcenter.security.ParseJwtResponse;
import com.jmsoftware.maf.common.domain.authcenter.security.UserPrincipal;
import com.jmsoftware.maf.common.exception.SecurityException;
import com.jmsoftware.maf.reactivespringbootstarter.configuration.MafConfiguration;
Expand All @@ -19,12 +17,9 @@
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
* Description: JwtReactiveServerSecurityContextRepositoryImpl
* <p>
Expand All @@ -38,8 +33,7 @@ public class JwtReactiveServerSecurityContextRepositoryImpl implements ServerSec
private final MafConfiguration mafConfiguration;
private final ReactiveAuthenticationManager authenticationManager;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Resource
private WebClient.Builder webClientBuilder;
private final AuthCenterRemoteApi authCenterRemoteApi;

@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
Expand All @@ -62,21 +56,14 @@ public Mono<SecurityContext> load(ServerWebExchange exchange) {
HttpHeaders.AUTHORIZATION, request.getMethod(), request.getURI());
return Mono.error(new SecurityException(HttpStatus.NETWORK_AUTHENTICATION_REQUIRED, "JWT Required"));
}
val parseJwtResponseMono = webClientBuilder
.build()
.get()
.uri("http://auth-center/jwt-remote-api/parse")
.headers(httpHeaders -> httpHeaders.set(HttpHeaders.AUTHORIZATION, authorization))
.retrieve()
.bodyToMono(ResponseBodyBean.class).map(ResponseBodyBean::getData);
return parseJwtResponseMono.map(data -> {
val parseJwtResponse = JSONUtil.toBean(JSONUtil.parseObj(data), ParseJwtResponse.class);
log.info("parseJwtResponse: {}", parseJwtResponse);
val userPrincipal = UserPrincipal.createByUsername(parseJwtResponse.getUsername());
val authentication = new UsernamePasswordAuthenticationToken(userPrincipal, null);
log.warn("About to authenticate… Authentication is created. {}", authentication);
return authentication;
}).flatMap(authentication -> this.authenticationManager
.authenticate(authentication).map(SecurityContextImpl::new));
return authCenterRemoteApi.parse(authorization)
.map(parseJwtResponse -> {
log.info("parseJwtResponse: {}", parseJwtResponse);
val userPrincipal = UserPrincipal.createByUsername(parseJwtResponse.getUsername());
val authentication = new UsernamePasswordAuthenticationToken(userPrincipal, null);
log.warn("About to authenticate… Authentication is created. {}", authentication);
return authentication;
}).flatMap(authentication -> this.authenticationManager.authenticate(authentication)
.map(SecurityContextImpl::new));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import com.jmsoftware.maf.apigateway.remoteapi.AuthCenterRemoteApi;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListPayload;
import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListResponse;
Expand Down Expand Up @@ -43,8 +44,7 @@
@RequiredArgsConstructor
public class RbacReactiveAuthorizationManagerImpl implements ReactiveAuthorizationManager<AuthorizationContext> {
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Resource
private WebClient.Builder webClientBuilder;
private final AuthCenterRemoteApi authCenterRemoteApi;

/**
* Retrieve roles flux.
Expand All @@ -55,14 +55,7 @@ public class RbacReactiveAuthorizationManagerImpl implements ReactiveAuthorizati
private Flux<GetRoleListByUserIdResponse.Role> retrieveRoles(Mono<UserPrincipal> userPrincipalMono) {
// Get role list by user ID, and then convert to Flux<?>
return userPrincipalMono
.flatMap(userPrincipal -> webClientBuilder
.build()
.get()
.uri("http://auth-center/role-remote-api/roles/{userId}", userPrincipal.getId())
.retrieve()
.bodyToMono(ResponseBodyBean.class)).map(ResponseBodyBean::getData)
.map(data -> JSONUtil.toList(JSONUtil.parseObj(data).getJSONArray("roleList"),
GetRoleListByUserIdResponse.Role.class))
.flatMap(userPrincipal -> authCenterRemoteApi.getRoleListByUserId(userPrincipal.getId()))
.flatMapMany(Flux::fromIterable)
.switchIfEmpty(Flux.error(new SecurityException(HttpStatus.UNAUTHORIZED, "Roles not assigned!")));
}
Expand Down Expand Up @@ -94,20 +87,8 @@ private Mono<List<GetPermissionListByRoleIdListResponse.Permission>> retrievePer
val payload = new GetPermissionListByRoleIdListPayload();
payload.setRoleIdList(roleIdList);
payload.setPermissionTypeList(Lists.newArrayList(PermissionType.BUTTON));
return webClientBuilder
.build()
.get()
.uri(uriBuilder -> uriBuilder
.host("auth-center")
.path("/permission-remote-api/permissions")
.queryParam("roleIdList", StrUtil.join(",", payload.getRoleIdList()))
.queryParam("permissionTypeList",
StrUtil.join(",", payload.getPermissionTypeList()))
.build())
.retrieve()
.bodyToMono(ResponseBodyBean.class).map(ResponseBodyBean::getData);
}).map(data -> JSONUtil.toList(JSONUtil.parseObj(data).getJSONArray("permissionList"),
GetPermissionListByRoleIdListResponse.Permission.class))
return authCenterRemoteApi.getPermissionListByRoleIdList(payload);
})
.switchIfEmpty(Mono.error(new SecurityException(HttpStatus.FORBIDDEN, "Permission not found!")));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* @date 5/10/20 12:45 PM
**/
@Data
public class GetUserByLoginTokenResponse {
public class GetUserByLoginTokenResponse {
/**
* Primary key
*/
Expand Down

0 comments on commit 4f5a20d

Please sign in to comment.