Skip to content

Commit 3c384d3

Browse files
committed
Add security feature for the RestFULL API. Do as the following steps to use this feature. Step 1.Set server.seurity to true in properity file. Step 2.Add user accounts in security/securityFile.txt according to the format username:passwd:role Notice:This feature supports auto reloading the user accounts in the securityFile.txt
1 parent 708cc37 commit 3c384d3

File tree

8 files changed

+239
-0
lines changed

8 files changed

+239
-0
lines changed

docs/index.pdf

0 Bytes
Binary file not shown.

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@
8080
<artifactId>spring-boot-starter-web</artifactId>
8181
</dependency>
8282

83+
<dependency>
84+
<groupId>org.springframework.boot</groupId>
85+
<artifactId>spring-boot-starter-security</artifactId>
86+
</dependency>
87+
8388
<dependency>
8489
<groupId>org.springframework.boot</groupId>
8590
<artifactId>spring-boot-starter</artifactId>
@@ -243,6 +248,12 @@
243248
<artifactId>snakeyaml</artifactId>
244249
<version>${snakeyaml.version}</version>
245250
</dependency>
251+
<dependency>
252+
<groupId>net.sf.json-lib</groupId>
253+
<artifactId>json-lib</artifactId>
254+
<version>2.4</version>
255+
<classifier>jdk15</classifier>
256+
</dependency>
246257
</dependencies>
247258

248259
<build>

security/securityFile.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
admin:admin1234:admin
2+
tina:tina1234:user
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.gnuhpc.bigdata.config;
2+
3+
import net.sf.json.JSONObject;
4+
import net.sf.json.JsonConfig;
5+
import net.sf.json.processors.JsonValueProcessor;
6+
import org.gnuhpc.bigdata.exception.RestErrorResponse;
7+
import org.springframework.http.HttpStatus;
8+
import org.springframework.security.core.AuthenticationException;
9+
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
10+
import org.springframework.stereotype.Component;
11+
12+
import javax.servlet.ServletException;
13+
import javax.servlet.http.HttpServletRequest;
14+
import javax.servlet.http.HttpServletResponse;
15+
import java.io.IOException;
16+
import java.time.LocalDateTime;
17+
import java.time.format.DateTimeFormatter;
18+
19+
@Component
20+
public class BasicAuthenticationPoint extends BasicAuthenticationEntryPoint {
21+
@Override
22+
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
23+
throws IOException, ServletException {
24+
response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName());
25+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
26+
String error = "Authenciation Error:" + authEx.getClass().getCanonicalName();
27+
RestErrorResponse restAuthenticationError = new RestErrorResponse(HttpStatus.UNAUTHORIZED, error, authEx);
28+
/**
29+
* Translate field LocalDateTime to uniform the response format.
30+
*/
31+
JsonConfig jsonConfig = new JsonConfig();
32+
jsonConfig.registerJsonValueProcessor(LocalDateTime.class, new JsonValueProcessor() {
33+
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
34+
@Override
35+
public Object processObjectValue(String propertyName, Object date,JsonConfig config) {
36+
return df.format((LocalDateTime)date);
37+
}
38+
39+
@Override
40+
public Object processArrayValue(Object date, JsonConfig config) {
41+
return df.format((LocalDateTime)date);
42+
}
43+
});
44+
45+
response.getWriter().print(JSONObject.fromObject(restAuthenticationError, jsonConfig).toString());
46+
}
47+
48+
@Override
49+
public void afterPropertiesSet() throws Exception {
50+
setRealmName("Contact Big Data Infrastructure Team to get available accounts.");
51+
super.afterPropertiesSet();
52+
}
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.gnuhpc.bigdata.config;
2+
3+
import org.gnuhpc.bigdata.service.UserDetailsServiceImp;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11+
import org.springframework.security.config.http.SessionCreationPolicy;
12+
import org.springframework.security.core.userdetails.UserDetailsService;
13+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14+
15+
@EnableWebSecurity
16+
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
17+
@Autowired
18+
private BasicAuthenticationPoint basicAuthenticationPoint;
19+
20+
@Bean
21+
public UserDetailsService userDetailsService() {
22+
return new UserDetailsServiceImp();
23+
};
24+
25+
@Bean
26+
public BCryptPasswordEncoder passwordEncoder() {
27+
return new BCryptPasswordEncoder();
28+
};
29+
30+
@Value("${server.security}")
31+
private boolean security;
32+
33+
@Override
34+
protected void configure(HttpSecurity http) throws Exception {
35+
http.csrf().disable();
36+
if (security) {
37+
http.authorizeRequests().antMatchers("/api", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v2/**").permitAll()
38+
.anyRequest().authenticated();
39+
http.httpBasic().authenticationEntryPoint(basicAuthenticationPoint);
40+
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
41+
} else {
42+
http.authorizeRequests().antMatchers("/**").permitAll()
43+
.anyRequest().authenticated();
44+
}
45+
}
46+
47+
@Autowired
48+
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
49+
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
50+
}
51+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.gnuhpc.bigdata.model;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
6+
@Getter
7+
@Setter
8+
public class User {
9+
private String username;
10+
private String password;
11+
private String role;
12+
13+
public User(String username, String password, String role) {
14+
this.username = username;
15+
this.password = password;
16+
this.role = role;
17+
}
18+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package org.gnuhpc.bigdata.service;
2+
3+
import com.google.common.util.concurrent.ThreadFactoryBuilder;
4+
import lombok.extern.log4j.Log4j;
5+
import org.gnuhpc.bigdata.model.User;
6+
import org.springframework.security.core.userdetails.User.UserBuilder;
7+
import org.springframework.security.core.userdetails.UserDetails;
8+
import org.springframework.security.core.userdetails.UserDetailsService;
9+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
10+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
11+
12+
import java.io.*;
13+
import java.util.ArrayList;
14+
import java.util.concurrent.Executors;
15+
import java.util.concurrent.ScheduledExecutorService;
16+
import java.util.concurrent.TimeUnit;
17+
18+
@Log4j
19+
public class UserDetailsServiceImp implements UserDetailsService {
20+
private ScheduledExecutorService securityFileChecker;
21+
private int checkInitDelay = 30;
22+
private int checkSecurityInterval = 60;
23+
private ArrayList<User> userList = null;
24+
25+
public UserDetailsServiceImp() {
26+
securityFileChecker = Executors.newSingleThreadScheduledExecutor(
27+
new ThreadFactoryBuilder().setNameFormat("securityFileChecker").build());
28+
securityFileChecker.scheduleWithFixedDelay(new securityFileCheckerRunnable(),
29+
checkInitDelay, checkSecurityInterval, TimeUnit.SECONDS);
30+
userList = fetchUserListFromSecurtiyFile();
31+
}
32+
33+
@Override
34+
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
35+
User user = findUserByUsername(username);
36+
37+
UserBuilder builder = null;
38+
if (user != null) {
39+
builder = org.springframework.security.core.userdetails.User.withUsername(username);
40+
builder.password(new BCryptPasswordEncoder().encode(user.getPassword()));
41+
builder.roles(user.getRole());
42+
} else {
43+
throw new UsernameNotFoundException("User not found.");
44+
}
45+
46+
return builder.build();
47+
}
48+
49+
private User findUserByUsername(String username) {
50+
for (User user:userList) {
51+
if (username.equals(user.getUsername())) {
52+
return user;
53+
}
54+
}
55+
return null;
56+
}
57+
58+
private ArrayList<User> fetchUserListFromSecurtiyFile() {
59+
ArrayList userList = new ArrayList();
60+
File tempFile = new File("");
61+
String securityFilePath = "";
62+
try {
63+
String projectRootPath = tempFile.getCanonicalPath();
64+
securityFilePath = projectRootPath + File.separator + "security/securityFile.txt";
65+
FileInputStream inputStream = new FileInputStream(securityFilePath);
66+
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
67+
String usernameAndPwd;
68+
int lineNum = 0;
69+
while((usernameAndPwd = bufferedReader.readLine()) != null)
70+
{
71+
lineNum++;
72+
String[] strArray = usernameAndPwd.split(":");
73+
if (strArray.length < 3) {
74+
log.error("Security file:" + securityFilePath + ",line " + lineNum + " is " +
75+
usernameAndPwd + ". The correct format should be username:passwd:role.");
76+
} else {
77+
String usernameInFile = strArray[0];
78+
String pwdInFile = strArray[1];
79+
String userRole = strArray[2];
80+
userList.add(new User(usernameInFile, pwdInFile, userRole));
81+
}
82+
}
83+
inputStream.close();
84+
bufferedReader.close();
85+
} catch (FileNotFoundException fileNotFoundException) {
86+
log.error("Security file in path: " + securityFilePath + " does not exist.", fileNotFoundException);
87+
} catch (IOException ioException) {
88+
log.error("Security file process exception.", ioException);
89+
}
90+
return userList;
91+
}
92+
93+
private class securityFileCheckerRunnable implements Runnable {
94+
@Override
95+
public void run() {
96+
try {
97+
userList = fetchUserListFromSecurtiyFile();
98+
} catch (Throwable t) {
99+
log.error("Uncaught exception in securityFileChecker thread", t);
100+
}
101+
}
102+
}
103+
}

src/main/resources/application-tina.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ server:
1919
port: 8121
2020
context-path: /
2121
debug: true
22+
security: false
2223

2324
spring:
2425
kafka:

0 commit comments

Comments
 (0)