I have scrapers running in cloud to download stock prices, and then how do I expose the service to my home pc for read only access? Spring Data provides a toolbox for it by using JPA and Rest Services. One thing to note is on data security.
Create project from “start.spring.io”, and include: web, jpa and security as the core modules; and add additional dependencies for sqlite.
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.42.0.0</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
spring.datasource.initialization-mode=always
spring.jpa.generate-ddl=true
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
spring.datasource.platform=sqlite3
spring.datasource.driver-class-name = org.sqlite.JDBC
spring.datasource.url = jdbc:sqlite:C:/scratch/hello_boot/sqlitesample.db
#spring.datasource.url = jdbc:sqlite:test
spring.datasource.dbcp2.default-auto-commit=true
spring.datasource.username =
spring.datasource.password =
Add a controller to provide static contents, and later on you can replace it to call from Jpa services.
@RestController
public class ResourceController {
@GetMapping("/home")
public String homeEndpoint() {
return "Hello Security!";
}
}
Call service from curl, and notice the error on authentication.
screenshot to be created.
Exclude default autoconfiguration for security, by either applicatoin.properties or from annotation @SpringBootApplication.
@SpringBootApplication(
exclude= {
SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class
}
)
public class HellosecurityApplication {
public static void main(String[] args) {
SpringApplication.run(HellosecurityApplication.class, args);
}
}
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
Add authentication for rest services by api-token, by building filter, authentication-service, and authentication object.
public class AuthenticationFilter extends GenericFilterBean{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest)request);
SecurityContextHolder.getContext().setAuthentication(authentication);
}catch(Exception e) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
PrintWriter writer = httpResponse.getWriter();
writer.print(e.getMessage());
writer.flush();
writer.close();
}
chain.doFilter(request, response);
}
}
public class AuthenticationService {
private static final String AUTH_TOKEN_HEADER_NAME="X-API-KEY";
private static final String AUTH_TOKEN = "mytoken12345678";
public static Authentication getAuthentication(HttpServletRequest request) {
String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME);
if(apiKey==null || !apiKey.equals(AUTH_TOKEN)) {
throw new BadCredentialsException("Invalid API Key");
}
return new ApiKeyAuthentication(apiKey, AuthorityUtils.NO_AUTHORITIES);
}
}
public class ApiKeyAuthentication extends AbstractAuthenticationToken {
private final String apiKey;
public ApiKeyAuthentication(String apiKey, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.apiKey = apiKey;
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return apiKey;
}
}
Enable SecurityFilterChain in config
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers("/**")
.authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(
new AuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
);
return http.build();
}
}
Test curl on rest service
curl –location –request GET “http://localhost:8080/home”
curl –location –request GET “http://localhost:8080/home” –header “X-API-KEY: mytoken12345678”
Reference:
https://www.baeldung.com/spring-boot-api-key-secret