Skip to content

Commit b25c2b3

Browse files
Adding Spring Security, Gradlew Build tasks to build ui and Generate KeyPair
1 parent 78e6702 commit b25c2b3

38 files changed

+638
-15
lines changed

.github/workflows/gradle.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ jobs:
1313
uses: actions/setup-java@v1
1414
with:
1515
java-version: 1.8
16+
- name: Set up NodeJs 10.x
17+
uses: actions/setup-node@v1
18+
with:
19+
node-version: '10.x'
1620
- name: Grant execute permission for gradlew
1721
run: chmod +x gradlew
1822
- name: Build with Gradle

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ build/
55
services/zookeeperAndKafka
66

77
ui/node_modules
8+
node_modules
89

9-
node_modules
10+
build

README.md

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,71 @@ Password: guest
7373

7474
```yml
7575
server:
76-
port: 5757
76+
port: 5757 // Set the Server Port
77+
78+
logging.level.root: INFO // Set the root Logging Level ex.: INFO | DEBUG | TRACE
79+
logging.level.<classpath>.<name>: INFO // INFO | DEBUG ...
80+
81+
# Use When Having Spring Actuator, Expose All Endpoints {Security Concerns}
82+
management:
83+
endpoints:
84+
web:
85+
exposure:
86+
include: "*" # Expose All The Actuator Endpoints
87+
88+
# When Using spring.security.xtype: JWT ; You Can Customise The token Generation
89+
# key paths and other details.
90+
custom:
91+
security:
92+
masterUser:
93+
username: master@localhost.com
94+
password: root
95+
roles:
96+
- MASTER
97+
- DEVELOPER
98+
- CUSTOMER
99+
jwt:
100+
header:
101+
key: Authorization
102+
value:
103+
prefix: Bearer
104+
algorithm : RSA512
105+
token:
106+
aliveFor: 3600
107+
issuer: ExampleApplication@localhost
108+
audience:
109+
- banana
110+
- banana1
111+
- banana2
112+
scope:
113+
- ui
114+
keys:
115+
public:
116+
path: /banana
117+
private:
118+
path: /banana
77119

78-
logging.level.root: INFO
79120

80121
spring:
122+
# Spring Security Details
123+
security:
124+
xtype: BASIC # custom: BASIC or JWT
125+
user:
126+
name: root
127+
password: root
128+
# Spring Data Default Data Source - MariaDB
129+
datasource:
130+
url: jdbc:mariadb://localhost:3306/ExampleDatabase
131+
username: root
132+
password: root
133+
driver-class-name: org.mariadb.jdbc.Driver
134+
validationQuery: SELECT 1
81135
application:
82136
name: ExampleApp
83137
h2:
84138
console:
85139
enabled: true
140+
# Spring Cloud Streams Binder and Bindings Details
86141
cloud:
87142
stream:
88143
defaultBinder: rabbit
@@ -114,6 +169,7 @@ spring:
114169
binder:
115170
brokers: localhost
116171
defaultBrokerPort: 9092
172+
# Specific Internal Kafka Consumer and Producer Properties per Binding
117173
bindings:
118174
globalEventsOutput:
119175
producer:
@@ -130,6 +186,7 @@ spring:
130186
cloud:
131187
stream:
132188
rabbit:
189+
# Specific Internal RabbitMQ Consumer and Producer Properties per Binding
133190
bindings:
134191
paymentsReceived:
135192
consumer:
@@ -148,3 +205,32 @@ spring:
148205
```
149206
150207
208+
209+
210+
# In This Repository
211+
212+
## A Example React Application
213+
214+
#### Start React Development Server
215+
```bash
216+
cd ui
217+
npm install
218+
npm start
219+
```
220+
[GO TO UI ON DEVELOPMENT SERVER](http://localhost:3000)
221+
222+
[GO TO BUILT_UI ON SPRING BOOT SERVER](http://localhost:5757)
223+
224+
Proxyed Requests : htto://localhost:3000 : http://localhost:5757
225+
226+
227+
## A Kafka Spring Cloud Binder
228+
## A Kafka Spring Cloud Binder
229+
### With a Consumer
230+
### With a Producer
231+
232+
233+
## A RabbitMQ Spring Cloud Binder
234+
### With a Consumer
235+
### With a Producer
236+

build.gradle

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ dependencyManagement {
2929

3030
dependencies {
3131

32+
runtime 'org.springframework.boot:spring-boot-devtools'
33+
3234
/**
3335
* Adding Spring Boot Starter For Web Application
3436
*/
@@ -41,10 +43,17 @@ dependencies {
4143
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
4244
compile 'org.mariadb.jdbc:mariadb-java-client:2.3.0'
4345

44-
46+
/**
47+
* Adding Spring Boot Actuator - Registers Endpoints
48+
*
49+
*/
4550
compile("org.springframework.boot:spring-boot-starter-actuator")
51+
/**
52+
* Adding Spring Boot Security
53+
*/
54+
compile("org.springframework.boot:spring-boot-starter-security")
4655

47-
56+
compile 'io.jsonwebtoken:jjwt:0.9.1'
4857

4958
/**
5059
* Requires Cloud Stream Dependency Management
@@ -75,6 +84,28 @@ dependencies {
7584
}
7685

7786

78-
task react{
87+
task react(type:Exec){
88+
workingDir './ui'
89+
executable "sh"
90+
args "-c", "npm run build"
91+
}
92+
93+
task Generate_JWT_Security_Key_Pair(type:Exec){
94+
workingDir './'
95+
executable "sh"
96+
args "-c", "echo" +
97+
" '\n############## ROTATING JWT SECURITY KEY PAIR ###############\n' " +
98+
"&& " +
99+
"source ./scripts/utils.sh " +
100+
"&& " +
101+
"generateJWTKeyPair"
79102

80103
}
104+
105+
/**
106+
* Assemble Process Build The React Application to the src/resources/public
107+
* Generate JWT Token Key Pair In Use when spring.security.xtype: JWT
108+
* Not needed when using with BASIC
109+
*/
110+
assemble.dependsOn(react)
111+
assemble.dependsOn(Generate_JWT_Security_Key_Pair)

scripts/utils.sh

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
1-
#! /bin/bash
1+
#!/bin/bash
22

33

4-
function ui-create(){
4+
function uiCreate(){
55
npx create-react-app
66
}
7+
8+
# $1 - Name
9+
function generateKeyPair(){
10+
11+
ssh-keygen -t rsa -N '' -b 4096 -m PEM -f $1.key
12+
# Don't add passphrase
13+
openssl rsa -in $1.key -pubout -outform PEM -out $1.key.pub
14+
15+
echo "Generated Key Pair"
16+
17+
cat $1.key
18+
cat $1.key.pub
19+
20+
}
21+
22+
function generateJWTKeyPair() {
23+
rm src/main/resources/private/jwt_authentication_RS256.key
24+
rm src/main/resources/private/jwt_authentication_RS256.key.pub
25+
generateKeyPair src/main/resources/private/jwt_authentication_RS256
26+
}

src/main/java/springboot/Application.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
import org.springframework.boot.autoconfigure.SpringBootApplication;
88
import org.springframework.cloud.stream.annotation.EnableBinding;
99
import org.springframework.context.annotation.Bean;
10-
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
1110
import springboot.messaging.KafkaEventChannels;
1211
import springboot.messaging.PaymentMessagingChannel;
1312
import springboot.services.InvoicingService;
1413

1514
@SpringBootApplication
1615
@EnableBinding({PaymentMessagingChannel.class, KafkaEventChannels.class})
17-
@EnableWebMvc
16+
//@EnableWebMvc
1817
public class Application {
1918

2019
@Bean
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package springboot.config;
2+
3+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
4+
import org.springframework.security.core.userdetails.UserDetails;
5+
import org.springframework.security.core.userdetails.UserDetailsService;
6+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
7+
import org.springframework.stereotype.Service;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Optional;
12+
13+
@Service
14+
@ConditionalOnProperty(value = "spring.security.xtype", havingValue = "JWT")
15+
public class JwtInMemoryUserDetailsService implements UserDetailsService {
16+
17+
static List<JwtUserDetails> inMemoryUserList = new ArrayList<>();
18+
19+
static {
20+
inMemoryUserList.add(new JwtUserDetails(1L, "in28minutes",
21+
"$2a$10$3zHzb.Npv1hfZbLEU5qsdOju/tk2je6W6PnNnY.c1ujWPcZh4PL6e", "ROLE_USER_2"));
22+
}
23+
24+
@Override
25+
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
26+
Optional<JwtUserDetails> findFirst = inMemoryUserList.stream()
27+
.filter(user -> user.getUsername().equals(username)).findFirst();
28+
29+
if (!findFirst.isPresent()) {
30+
throw new UsernameNotFoundException(String.format("USER_NOT_FOUND '%s'.", username));
31+
}
32+
33+
return findFirst.get();
34+
}
35+
36+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package springboot.config;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
6+
import org.springframework.security.core.GrantedAuthority;
7+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
8+
import org.springframework.security.core.userdetails.UserDetails;
9+
10+
import java.util.ArrayList;
11+
import java.util.Collection;
12+
import java.util.List;
13+
14+
@ConditionalOnProperty(value = "spring.security.xtype", havingValue = "JWT")
15+
public class JwtUserDetails implements UserDetails {
16+
17+
private final Long id;
18+
private final String username;
19+
private final String password;
20+
private final Collection<? extends GrantedAuthority> authorities;
21+
22+
public JwtUserDetails(Long id, String username, String password, String role) {
23+
this.id = id;
24+
this.username = username;
25+
this.password = password;
26+
27+
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
28+
authorities.add(new SimpleGrantedAuthority(role));
29+
30+
this.authorities = authorities;
31+
}
32+
33+
@JsonIgnore
34+
public Long getId() {
35+
return id;
36+
}
37+
38+
@Override
39+
public String getUsername() {
40+
return username;
41+
}
42+
43+
@JsonIgnore
44+
@Override
45+
public boolean isAccountNonExpired() {
46+
return true;
47+
}
48+
49+
@JsonIgnore
50+
@Override
51+
public boolean isAccountNonLocked() {
52+
return true;
53+
}
54+
55+
@JsonIgnore
56+
@Override
57+
public boolean isCredentialsNonExpired() {
58+
return true;
59+
}
60+
61+
@JsonIgnore
62+
@Override
63+
public String getPassword() {
64+
return password;
65+
}
66+
67+
@Override
68+
public Collection<? extends GrantedAuthority> getAuthorities() {
69+
return authorities;
70+
}
71+
72+
@Override
73+
public boolean isEnabled() {
74+
return true;
75+
}
76+
77+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package springboot.config;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.stereotype.Component;
5+
import org.springframework.web.filter.OncePerRequestFilter;
6+
7+
import javax.servlet.FilterChain;
8+
import javax.servlet.ServletException;
9+
import javax.servlet.http.HttpServletRequest;
10+
import javax.servlet.http.HttpServletResponse;
11+
import java.io.IOException;
12+
import java.util.Optional;
13+
14+
@Component
15+
public class RequestInterceptorFilter extends OncePerRequestFilter {
16+
17+
@Value("${custom.security.jwt.header.key:Authorization}")
18+
private String jwtAuthorizationHeaderKey;
19+
20+
@Value("${custom.security.jwt.header.value.prefix:Bearer }")
21+
private String jwtHeaderValuePrefix;
22+
23+
@Override
24+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
25+
Optional<String> headerValue = Optional.ofNullable(request.getHeader(jwtAuthorizationHeaderKey));
26+
27+
28+
filterChain.doFilter(request,response);
29+
}
30+
}

0 commit comments

Comments
 (0)