Skip to content

Commit

Permalink
perf($SpringCloud): update Spring Cloud version to 2020.0.0
Browse files Browse the repository at this point in the history
update Spring Boot version to 2.4.2

https://docs.spring.io/spring-cloud/docs/current/reference/html/index.html

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html

BREAKING CHANGE: update Spring framework dependencies: Spring Cloud
2020.0.0, Spring Boot 2.4.2

[skip ci]
  • Loading branch information
钟俊 committed Jan 28, 2021
1 parent a6549e5 commit 18f5843
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import reactivefeign.spring.config.EnableReactiveFeignClients;

import java.time.Duration;
import java.time.Instant;
Expand All @@ -22,10 +20,8 @@
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 12/22/2020 3:39 PM
**/
@Slf4j
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
@EnableReactiveFeignClients
public class ApiGatewayApplication {
private static final String LINE_SEPARATOR = System.lineSeparator();
private static MafProjectProperty mafProjectProperty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
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 org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import reactivefeign.spring.config.ReactiveFeignClient;
import reactor.core.publisher.Mono;

import javax.validation.Valid;
Expand All @@ -26,17 +26,19 @@
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com
* @date 5/10/20 4:50 PM
*/
@Service
@Validated
@ReactiveFeignClient(name = "auth-center")
public interface AuthCenterRemoteApi {
public class AuthCenterRemoteApi {
/**
* 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);
Mono<ResponseBodyBean<GetUserByLoginTokenResponse>> getUserByLoginToken(@PathVariable String loginToken) {
return null;
}

/**
* Gets role list by user id.
Expand All @@ -45,7 +47,9 @@ public interface AuthCenterRemoteApi {
* @return the role list by user id
*/
@GetMapping("/role-remote-api/roles/{userId}")
Mono<ResponseBodyBean<GetRoleListByUserIdResponse>> getRoleListByUserId(@PathVariable Long userId);
Mono<ResponseBodyBean<GetRoleListByUserIdResponse>> getRoleListByUserId(@PathVariable Long userId) {
return null;
}

/**
* Get permission list by role id list
Expand All @@ -57,7 +61,9 @@ public interface AuthCenterRemoteApi {
@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);
@Valid @RequestParam("permissionTypeList") List<@NotNull PermissionType> permissionTypeList) {
return null;
}

/**
* Parse mono.
Expand All @@ -66,5 +72,7 @@ Mono<ResponseBodyBean<GetPermissionListByRoleIdListResponse>> getPermissionListB
* @return the mono
*/
@GetMapping("/jwt-remote-api/parse")
Mono<ResponseBodyBean<ParseJwtResponse>> parse(@RequestHeader Map<String, String> headers);
Mono<ResponseBodyBean<ParseJwtResponse>> parse(@RequestHeader Map<String, String> headers) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package com.jmsoftware.maf.apigateway.security.impl;

import cn.hutool.core.util.StrUtil;
import com.jmsoftware.maf.apigateway.remoteapi.AuthCenterRemoteApi;
import cn.hutool.json.JSONUtil;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
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.context.annotation.Lazy;
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;
Expand All @@ -29,9 +30,8 @@
@Slf4j
@RequiredArgsConstructor
public class JwtReactiveAuthenticationManagerImpl implements ReactiveAuthenticationManager {
@Lazy
@Resource
private AuthCenterRemoteApi authCenterRemoteApi;
private WebClient.Builder webClientBuilder;

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

import cn.hutool.core.util.StrUtil;
import com.jmsoftware.maf.apigateway.remoteapi.AuthCenterRemoteApi;
import cn.hutool.json.JSONUtil;
import com.jmsoftware.maf.apigateway.security.configuration.JwtConfiguration;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.common.domain.authcenter.security.ParseJwtResponse;
Expand All @@ -11,7 +11,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
Expand All @@ -20,11 +19,11 @@
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;
import java.util.HashMap;

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

@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
Expand All @@ -64,16 +62,17 @@ public Mono<SecurityContext> load(ServerWebExchange exchange) {
HttpHeaders.AUTHORIZATION, request.getMethod(), request.getURI());
return Mono.error(new SecurityException(HttpStatus.NETWORK_AUTHENTICATION_REQUIRED, "JWT Required"));
}
val headers = new HashMap<String, String>(4);
headers.put(HttpHeaders.AUTHORIZATION, authorization);
Mono<ParseJwtResponse> parseJwtResponseMono = authCenterRemoteApi
.parse(headers)
.map(ResponseBodyBean::getData)
.switchIfEmpty(Mono.error(
new SecurityException(HttpStatus.INTERNAL_SERVER_ERROR, "Got empty when parsing JWT")));
return parseJwtResponseMono.map(parseJwtResponse -> {
String username = parseJwtResponse.getUsername();
val userPrincipal = UserPrincipal.createByUsername(username);
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.jmsoftware.maf.apigateway.security.impl;

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 All @@ -13,7 +13,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
Expand All @@ -22,6 +21,7 @@
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

Expand All @@ -43,9 +43,8 @@
@RequiredArgsConstructor
public class RbacReactiveAuthorizationManagerImpl implements ReactiveAuthorizationManager<AuthorizationContext> {
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Lazy
@Resource
private AuthCenterRemoteApi authCenterRemoteApi;
private WebClient.Builder webClientBuilder;

/**
* Retrieve roles flux.
Expand All @@ -56,9 +55,14 @@ 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 -> authCenterRemoteApi.getRoleListByUserId(userPrincipal.getId())
.map(ResponseBodyBean::getData))
.map(GetRoleListByUserIdResponse::getRoleList)
.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))
.flatMapMany(Flux::fromIterable)
.switchIfEmpty(Flux.error(new SecurityException(HttpStatus.UNAUTHORIZED, "Roles not assigned!")));
}
Expand Down Expand Up @@ -90,10 +94,20 @@ private Mono<List<GetPermissionListByRoleIdListResponse.Permission>> retrievePer
val payload = new GetPermissionListByRoleIdListPayload();
payload.setRoleIdList(roleIdList);
payload.setPermissionTypeList(Lists.newArrayList(PermissionType.BUTTON));
return authCenterRemoteApi.getPermissionListByRoleIdList(payload.getRoleIdList(),
payload.getPermissionTypeList())
.map(ResponseBodyBean::getData);
}).map(GetPermissionListByRoleIdListResponse::getPermissionList)
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))
.switchIfEmpty(Mono.error(new SecurityException(HttpStatus.FORBIDDEN, "Permission not found!")));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package com.jmsoftware.maf.apigateway.universal.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.common.exception.SecurityException;
import com.jmsoftware.maf.reactivespringbootstarter.util.RequestUtil;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
Expand All @@ -23,8 +19,6 @@
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Optional;

/**
* <h1>GlobalExceptionHandler</h1>
* <p>
Expand Down Expand Up @@ -77,40 +71,6 @@ private ResponseBodyBean<?> setResponseBody(ServerHttpResponse response, Throwab
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
return ResponseBodyBean.ofStatus(((ResponseStatusException) ex).getStatus());
} else if (ex instanceof HystrixRuntimeException) {
if (ObjectUtil.isNotNull(ex.getCause()) && ex.getCause() instanceof FeignException) {
val cause = (FeignException) ex.getCause();
val httpStatus = HttpStatus.valueOf(cause.status());
response.setStatusCode(httpStatus);
String responseBodyString = null;
if (cause.responseBody().isPresent()) {
responseBodyString = new String(cause.responseBody().get().array());
}
Optional<ResponseBodyBean<?>> optionalResponseBodyBean = Optional.empty();
if (StrUtil.isNotBlank(responseBodyString)) {
try {
//noinspection unchecked
optionalResponseBodyBean = Optional.ofNullable(
objectMapper.readValue(responseBodyString, ResponseBodyBean.class));
} catch (JsonProcessingException e) {
log.error("Cannot deserialize response to {}", ResponseBodyBean.class.getSimpleName());
optionalResponseBodyBean = Optional.empty();
}
}
String errorMessage;
if (optionalResponseBodyBean.isPresent() && StrUtil.isNotBlank(
optionalResponseBodyBean.get().getMessage())) {
errorMessage = String.format("%s Feign exception: %s", ex.getMessage(),
optionalResponseBodyBean.get().getMessage());
} else {
errorMessage = String.format("%s Feign exception: %s", ex.getMessage(),
ex.getCause().getMessage());
}
return ResponseBodyBean.ofStatus(httpStatus, errorMessage);
}
response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
return ResponseBodyBean.ofStatus(HttpStatus.SERVICE_UNAVAILABLE,
String.format("%s %s", ex.getMessage(), ex.getCause().getMessage()));
} else if (ex instanceof SecurityException) {
HttpStatus status = HttpStatus.valueOf(((SecurityException) ex).getCode());
response.setStatusCode(status);
Expand Down
9 changes: 9 additions & 0 deletions auth-center/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>build-info</id>
<phase>compile</phase>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- https://github.com/GoogleContainerTools/jib/blob/master/jib-maven-plugin/README.md -->
Expand Down
Loading

0 comments on commit 18f5843

Please sign in to comment.