diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebSecurityConfiguration.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebSecurityConfiguration.java
new file mode 100644
index 00000000..810d07a6
--- /dev/null
+++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebSecurityConfiguration.java
@@ -0,0 +1,40 @@
+package com.jmsoftware.maf.authcenter.universal.configuration;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+/**
+ *
WebSecurityConfiguration
+ *
+ * Security handler configuration.
+ *
+ * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com
+ * @date 5/2/20 11:41 PM
+ **/
+@Slf4j
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
+ @Bean
+ public BCryptPasswordEncoder encoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManager() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // Disable Web Security.
+ http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();
+ }
+}
diff --git a/auth-center/src/test/java/com/jmsoftware/maf/authcenter/AuthCenterApplicationTests.java b/auth-center/src/test/java/com/jmsoftware/maf/authcenter/AuthCenterApplicationTests.java
index 0b998518..c9f39213 100644
--- a/auth-center/src/test/java/com/jmsoftware/maf/authcenter/AuthCenterApplicationTests.java
+++ b/auth-center/src/test/java/com/jmsoftware/maf/authcenter/AuthCenterApplicationTests.java
@@ -1,13 +1,41 @@
package com.jmsoftware.maf.authcenter;
+import cn.hutool.core.util.StrUtil;
+import com.jmsoftware.maf.authcenter.universal.domain.UserPO;
+import com.jmsoftware.maf.authcenter.universal.domain.UserPrincipal;
+import com.jmsoftware.maf.authcenter.universal.service.JwtService;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import lombok.val;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import java.util.ArrayList;
+
+/**
+ * Description: AuthCenterApplicationTests.
+ *
+ * @author 钟俊 (zhongjun), email: zhongjun@toguide.cn, date: 12/21/2020 3:08 PM
+ */
+@Slf4j
@SpringBootTest
class AuthCenterApplicationTests {
+ @Autowired
+ private JwtService jwtService;
@Test
- void contextLoads() {
+ @SneakyThrows
+ void mockLogin() {
+ UserPO userPO = new UserPO();
+ userPO.setId(1L);
+ userPO.setUsername("ijohnnymiller");
+ val authenticationToken = new UsernamePasswordAuthenticationToken(
+ UserPrincipal.create(userPO, new ArrayList<>(), new ArrayList<>()), 12345678);
+ String jwt = jwtService.createJwt(authenticationToken, false);
+ log.info("Generated JWT: {}", jwt);
+ Assertions.assertTrue(StrUtil.isNotBlank(jwt));
}
-
}
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/remoteapi/AuthCenterRemoteApi.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/remoteapi/AuthCenterRemoteApi.java
index ce296e2c..c5a095a7 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/remoteapi/AuthCenterRemoteApi.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/remoteapi/AuthCenterRemoteApi.java
@@ -2,6 +2,7 @@
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.common.domain.authcenter.role.GetRoleListByUserIdResponse;
+import com.jmsoftware.maf.common.domain.authcenter.user.GetUserByLoginTokenResponse;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -19,6 +20,15 @@
@Validated
@ReactiveFeignClient(name = "auth-center")
public interface 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> getUserByLoginToken(@PathVariable String loginToken);
+
/**
* Gets role list by user id.
*
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/AuthenticationManager.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/AuthenticationManager.java
index 16226d0d..8622c3f3 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/AuthenticationManager.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/AuthenticationManager.java
@@ -1,50 +1,57 @@
package com.jmsoftware.maf.gateway.universal.configuration;
-import com.google.common.collect.Lists;
+import cn.hutool.core.util.StrUtil;
+import com.jmsoftware.maf.common.bean.ResponseBodyBean;
+import com.jmsoftware.maf.common.domain.authcenter.user.GetUserByLoginTokenResponse;
+import com.jmsoftware.maf.common.exception.BusinessException;
+import com.jmsoftware.maf.gateway.remoteapi.AuthCenterRemoteApi;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import lombok.val;
+import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
-import java.util.List;
+import javax.annotation.Resource;
/**
* Description: AuthenticationManager, change description here.
*
* @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/18/2020 3:40 PM
**/
+@Slf4j
@Component
@RequiredArgsConstructor
public class AuthenticationManager implements ReactiveAuthenticationManager {
private final JwtService jwtService;
+ @Lazy
+ @Resource
+ private AuthCenterRemoteApi authCenterRemoteApi;
@Override
public Mono authenticate(Authentication authentication) {
- String authToken = authentication.getCredentials().toString();
+ val jwt = authentication.getCredentials().toString();
String username;
try {
- username = jwtService.getUsernameFromJwt(authToken);
+ username = jwtService.getUsernameFromJwt(jwt);
} catch (Exception e) {
- username = null;
+ log.error("Exception occurred when authenticating", e);
+ return Mono.empty();
}
-// if (username != null && !tokenProvider.isTokenExpired(authToken)) {
-// Claims claims = tokenProvider.getAllClaimsFromToken(authToken);
-// List roles = claims.get(AUTHORITIES_KEY, List.class);
-// List authorities = roles.stream().map(role -> new SimpleGrantedAuthority(role)).collect(
-// Collectors.toList());
-// UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, username,
-// authorities);
-// SecurityContextHolder.getContext().setAuthentication(new UserPrincipal(username, authorities));
-// return Mono.just(auth);
-// } else {
-// return Mono.empty();
-// }
- List authorities = Lists.newLinkedList();
- UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, username,
- authorities);
- return Mono.just(auth);
+ if (StrUtil.isBlank(username)) {
+ log.warn("Authentication failed! Cause: the username mustn't be blank");
+ return Mono.empty();
+ }
+ val response = authCenterRemoteApi.getUserByLoginToken(username);
+ Mono responseMono = response.map(ResponseBodyBean::getData)
+ .switchIfEmpty(Mono.error(new BusinessException("Authentication failed! Cause: User not found")));
+ return responseMono.map(getUserByLoginTokenResponse -> {
+ log.info("Authentication success. Username: {}", getUserByLoginTokenResponse.getUsername());
+ UserPrincipal userPrincipal = UserPrincipal.create(getUserByLoginTokenResponse, null, null);
+ return new UsernamePasswordAuthenticationToken(userPrincipal, null);
+ });
}
}
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerSecurityContextRepository.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerSecurityContextRepository.java
index 114381d2..32329c91 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerSecurityContextRepository.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerSecurityContextRepository.java
@@ -1,7 +1,8 @@
package com.jmsoftware.maf.gateway.universal.configuration;
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
@@ -21,31 +22,27 @@
**/
@Slf4j
@Component
+@RequiredArgsConstructor
public class CustomServerSecurityContextRepository implements ServerSecurityContextRepository {
private static final String TOKEN_PREFIX = "Bearer ";
-
- @Autowired
- private ReactiveAuthenticationManager authenticationManager;
+ private final ReactiveAuthenticationManager authenticationManager;
@Override
- public Mono save(ServerWebExchange swe, SecurityContext sc) {
+ public Mono save(ServerWebExchange exchange, SecurityContext context) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
- public Mono load(ServerWebExchange swe) {
- ServerHttpRequest request = swe.getRequest();
- String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
- String authToken = null;
- if (authHeader != null && authHeader.startsWith(TOKEN_PREFIX)) {
- authToken = authHeader.replace(TOKEN_PREFIX, "");
- }
- if (authToken != null) {
- Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
- return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
- } else {
+ public Mono load(ServerWebExchange exchange) {
+ ServerHttpRequest request = exchange.getRequest();
+ String authorization = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+ if (StrUtil.isBlank(authorization) || !authorization.startsWith(TOKEN_PREFIX)) {
+ log.warn("{} in HTTP headers not found! [{}] {}", HttpHeaders.AUTHORIZATION, request.getMethod(),
+ request.getURI());
return Mono.empty();
}
+ String jwt = authorization.replace(TOKEN_PREFIX, "");
+ Authentication authentication = new UsernamePasswordAuthenticationToken(null, jwt);
+ return this.authenticationManager.authenticate(authentication).map(SecurityContextImpl::new);
}
-
}
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/JwtServiceImpl.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/JwtServiceImpl.java
index 0b9644a6..161d875c 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/JwtServiceImpl.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/JwtServiceImpl.java
@@ -91,7 +91,7 @@ public Claims parseJwt(String jwt) throws SecurityException {
.orElseThrow(() -> new SecurityException(HttpStatus.TOKEN_PARSE_ERROR,
"The JWT Claims Set is null", null));
} catch (ExpiredJwtException e) {
- log.error("JWT is expired. Message: {} JWT: {}", e.getMessage(), jwt);
+ log.error("JWT was expired. Message: {} JWT: {}", e.getMessage(), jwt);
throw new SecurityException(HttpStatus.TOKEN_EXPIRED);
} catch (UnsupportedJwtException e) {
log.error("JWT is unsupported. Message: {} JWT: {}", e.getMessage(), jwt);
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/RbacReactiveAuthorizationManager.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/RbacReactiveAuthorizationManager.java
new file mode 100644
index 00000000..2e3cc81e
--- /dev/null
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/RbacReactiveAuthorizationManager.java
@@ -0,0 +1,40 @@
+package com.jmsoftware.maf.gateway.universal.configuration;
+
+import com.jmsoftware.maf.gateway.remoteapi.AuthCenterRemoteApi;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.ReactiveAuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.server.authorization.AuthorizationContext;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.Resource;
+
+/**
+ * Description: ReactiveAuthorizationManagerImpl, change description here.
+ *
+ * @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/21/2020 12:38 PM
+ **/
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class RbacReactiveAuthorizationManager implements ReactiveAuthorizationManager {
+ @Lazy
+ @Resource
+ private AuthCenterRemoteApi authCenterRemoteApi;
+
+ @Override
+ public Mono check(Mono authentication, AuthorizationContext object) {
+ return authentication.map(auth -> {
+ ServerHttpRequest request = object.getExchange().getRequest();
+ UserPrincipal userPrincipal = (UserPrincipal) auth.getPrincipal();
+ log.info("Checking authorization for user: {}, resource: [{}] {}", userPrincipal.getUsername(),
+ request.getMethod(), request.getURI());
+ return new AuthorizationDecision(true);
+ }).defaultIfEmpty(new AuthorizationDecision(false));
+ }
+}
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java
index e99887e6..50fd47c7 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java
@@ -21,6 +21,7 @@ public class ServerAuthenticationEntryPointImpl implements ServerAuthenticationE
public Mono commence(ServerWebExchange serverWebExchange, AuthenticationException e) {
log.error("Exception occurred when authenticating! Exception message: {}. Request URL: [{}] {}", e.getMessage(),
serverWebExchange.getRequest().getMethod(), serverWebExchange.getRequest().getURI());
+ log.error("{}", e.getMessage(), e);
return ResponseUtil.renderJson(serverWebExchange, HttpStatus.NETWORK_AUTHENTICATION_REQUIRED, null);
}
}
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java
index 39d22e65..fb4be27a 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java
@@ -3,7 +3,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
-import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/UserPrincipal.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/UserPrincipal.java
index 3c9f6a87..96d879ca 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/UserPrincipal.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/UserPrincipal.java
@@ -13,10 +13,7 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
import java.util.stream.Collectors;
/**
@@ -96,11 +93,12 @@ public class UserPrincipal implements UserDetails {
*/
public static UserPrincipal create(GetUserByLoginTokenResponse user, List roleNameList,
List permissionList) {
- val authorities =
- permissionList.stream()
- .filter(permission -> StrUtil.isNotBlank(permission.getPermissionExpression()))
- .map(permission -> new SimpleGrantedAuthority(permission.getPermissionExpression()))
- .collect(Collectors.toList());
+ val permissions =
+ Optional.ofNullable(permissionList).orElse(new LinkedList<>());
+ val authorities = permissions.stream()
+ .filter(permission -> StrUtil.isNotBlank(permission.getPermissionExpression()))
+ .map(permission -> new SimpleGrantedAuthority(permission.getPermissionExpression()))
+ .collect(Collectors.toList());
return new UserPrincipal(user.getId(),
user.getUsername(),
diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java
index e13f77b8..906f957a 100644
--- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java
+++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java
@@ -34,6 +34,7 @@
public class WebFluxSecurityConfiguration {
private final CustomConfiguration customConfiguration;
private final ReactiveAuthenticationManager reactiveAuthenticationManager;
+ private final RbacReactiveAuthorizationManager reactiveAuthorizationManager;
private final ServerSecurityContextRepository securityContextRepository;
private final ServerAuthenticationEntryPointImpl serverAuthenticationEntryPointImpl;
private final CustomServerAccessDeniedHandler customServerAccessDeniedHandler;
@@ -49,12 +50,15 @@ SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
.accessDeniedHandler(customServerAccessDeniedHandler)
.and()
.addFilterBefore(requestFilter, SecurityWebFiltersOrder.AUTHENTICATION)
+ // Authentication
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers(flattenIgnoredUrls()).permitAll()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
- .anyExchange().authenticated()
+// .anyExchange().authenticated()
+ // Authorization
+ .anyExchange().access(reactiveAuthorizationManager)
.and()
.build();
}