Skip to content

Commit

Permalink
feat($gateway): integrate Spring Security
Browse files Browse the repository at this point in the history
  • Loading branch information
Johnny Miller (锺俊) committed Dec 18, 2020
1 parent 2e29759 commit f092c6b
Show file tree
Hide file tree
Showing 16 changed files with 990 additions and 0 deletions.
32 changes: 32 additions & 0 deletions gateway/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
Expand All @@ -90,6 +94,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down Expand Up @@ -142,5 +150,29 @@
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

<!-- https://github.com/jwtk/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.jmsoftware.maf.gateway.universal.configuration;

import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
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;

/**
* Description: AuthenticationManager, change description here.
*
* @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/18/2020 3:40 PM
**/
@Component
@RequiredArgsConstructor
public class AuthenticationManager implements ReactiveAuthenticationManager {
private final JwtService jwtService;

@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String authToken = authentication.getCredentials().toString();
String username;
try {
username = jwtService.getUsernameFromJwt(authToken);
} catch (Exception e) {
username = null;
}
// 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<GrantedAuthority> authorities = Lists.newLinkedList();
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, username,
authorities);
return Mono.just(auth);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.jmsoftware.maf.gateway.universal.configuration;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
* <h1>Constants</h1>
* <p>
* Change description here.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com
* @date 5/2/20 11:41 PM
**/
@Slf4j
@Component
public class Constants {
public Constants(ProjectProperty projectProperty) {
REDIS_JWT_KEY_PREFIX = String.format("%s:jwt:", projectProperty.getProjectParentArtifactId());
log.warn("Initiated 'REDIS_JWT_KEY_PREFIX': {}", REDIS_JWT_KEY_PREFIX);
}

/**
* Key prefix of JWT stored in Redis.
*/
public static String REDIS_JWT_KEY_PREFIX;
/**
* Token key of request header.
*/
public static final String REQUEST_TOKEN_KEY = "Authorization";
/**
* Prefix of JWT.
*/
public static final String JWT_PREFIX = "Bearer ";
/**
* Star sign
*/
public static final String ASTERISK = "*";
/**
* At sign
*/
public static final String AT_SIGN = "@";
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import java.util.List;

Expand All @@ -16,11 +17,33 @@
* @date 2019-03-23 14:24
**/
@Data
@Validated
@Component
@ConfigurationProperties(prefix = "custom.configuration")
public class CustomConfiguration {
/**
* The Allowed application list. If it's empty, gateway will allow all request to any applications (microservices)
*/
private List<String> allowedApplicationList = Lists.newLinkedList();
/**
* <p>The username of super user who has no restriction to access any system&#39;s resources.</p>
* <p><strong>ATTENTION</strong>: The value of username of super user must be equal to the value that is
* persistent in database.</p>
*/
private String superUser;
/**
* Ignore URLs
*/
private IgnoredRequest ignoredRequest;
/**
* <p>Web security feature switch. Default is false.</p>
* true - disable web security; false - enable web security.
*/
private Boolean webSecurityDisabled = false;
/**
* Web request log switch. Default is false.
* <p>
* true - disable web request log; false - enable web request log.
*/
private Boolean webRequestLogDisabled = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.jmsoftware.maf.gateway.universal.configuration;

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;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
* Description: SecurityContextRepository, change description here.
*
* @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/18/2020 3:38 PM
**/
@Slf4j
@Component
public class CustomServerSecurityContextRepository implements ServerSecurityContextRepository {
private static final String TOKEN_PREFIX = "Bearer ";

@Autowired
private ReactiveAuthenticationManager authenticationManager;

@Override
public Mono save(ServerWebExchange swe, SecurityContext sc) {
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, "");
}else {
log.warn("couldn't find bearer string, will ignore the header.");
}
if (authToken != null) {
Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
return this.authenticationManager.authenticate(auth).map((authentication) -> new SecurityContextImpl(authentication));
} else {
return Mono.empty();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.jmsoftware.maf.gateway.universal.configuration;

import com.google.common.collect.Lists;
import lombok.Data;

import java.util.List;

/**
* <h1>IgnoredRequest</h1>
* <p>
* Ignored request configuration.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com
* @date 5/2/20 11:41 PM
**/
@Data
public class IgnoredRequest {
/**
* Ignored URL pattern.
*/
private List<String> pattern = Lists.newArrayList();
/**
* Ignored GET request.
*/
private List<String> get = Lists.newArrayList();
/**
* Ignored POST request.
*/
private List<String> post = Lists.newArrayList();
/**
* Ignored DELETE request.
*/
private List<String> delete = Lists.newArrayList();
/**
* Ignored PUT request.
*/
private List<String> put = Lists.newArrayList();
/**
* Ignored HEAD request.
*/
private List<String> head = Lists.newArrayList();
/**
* Ignored PATCH request.
*/
private List<String> patch = Lists.newArrayList();
/**
* Ignored OPTIONS request.
*/
private List<String> options = Lists.newArrayList();
/**
* Ignored TRACE request.
*/
private List<String> trace = Lists.newArrayList();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.jmsoftware.maf.gateway.universal.configuration;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

/**
* <h1>JwtConfiguration</h1>
* <p>
* Ignored request configuration.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com
* @date 5/2/20 11:41 PM
**/
@Data
@Slf4j
@Component
@ConfigurationProperties(prefix = "jwt.configuration")
public class JwtConfiguration {
public JwtConfiguration(ProjectProperty projectProperty) {
this.signingKey = String.format("%s %s", projectProperty.getProjectParentArtifactId(), projectProperty.getVersion());
log.info("Initiated JWT signing key: {}. The specified key byte array is {} bits", this.signingKey,
this.signingKey.getBytes(StandardCharsets.UTF_8).length * 8);
}

/**
* JWT signing key, which is equal to the string value of group id of project.
*/
private String signingKey;
/**
* Time to live of JWT. Default: 3 * 600000 milliseconds (30 min).
*/
private Long ttl = 3 * 600000L;
/**
* Time to live of JWT for remember me, Default: 7 * 86400000 milliseconds (7 day)
*/
private Long ttlForRememberMe = 7 * 86400000L;
}
Loading

0 comments on commit f092c6b

Please sign in to comment.