Expose endpoint in HTTP over HTTPS in Spring Boot application - java

In my Spring Boot app in which I've implemented Spring Security, I am currently running it on HTTPS but I want to expose a certain actuator endpoint on HTTP in order to Prometheus gets metrics from. Every tutorial I've found so far, has only the opposite. I know that the below code in my Main Class redirects all HTTP traffic to HTTPS but since I've already achieved that, I tried if I could modify the code for the opposite result only to no avail.
Then, I tried to check if somehow I could achieve my goal with,
management.server.ssl.enabled=false
management.server.port=8081
but I couldn't get any results in my browser in exposing Prometheus metrics, since for some reason the port isn't accesible.
So is it possible to have my whole application on HTTPS as it is right now, and redirect - expose just one Get Mapping mathod on HTTP? Just for testing purposes I have the Greeting Method below, in order to make my implementations.
Rest Controller Class (For Test Purposes)
package com.andrekreou.iot.control.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class Sample {
#GetMapping(value = "/greeting")
public String greeting() {
return "I am working with both HTTP and HTTPS";
}
}
Security Config Class
package com.andrekreou.iot.authentication.security;
import com.andrekreou.iot.authentication.user.ApplicationUserService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#AllArgsConstructor
#EnableWebSecurity
public class ApplicationSecurityConfig {
private final ApplicationUserService applicationUserService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.requiresChannel()
.antMatchers("/greeting","/actuator/prometheus")
.requiresInsecure()
.and()
.authorizeRequests()
.antMatchers("/api/v*/registration/**","/register*","/login","/registration","/registration-complete","/greeting").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("email")
.permitAll()
.defaultSuccessUrl("/",true)
.and()
.logout()
.logoutUrl("/logout")
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID","Idea-2e8e7cee")
.logoutSuccessUrl("/login");
return http.build();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(applicationUserService);
return provider;
}
}
Main Class
package com.andrekreou.iot;
import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories
public class IotApplication {
public static void main(String[] args) {
SpringApplication.run(IotApplication.class, args);
}
#Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
Application.properties
#Server properties for HTTPS configuration
server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:local-ssl.p12
server.ssl.key-store-password=<password>
server.ssl.key-alias=local_ssl
server.port=8443

Related

Spring Boot Security "Authentication Manager" doesn't receive username parameter

public AuthenticationResponse authenticate(AuthenticationRequest request){
authManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
return null; // returns other stuff, irrelevant
}
The result I get in the console is:
Hibernate: select u1_0.id,u1_0.email,u1_0.password,u1_0.role,u1_0.username from user u1_0 where u1_0.username=?
If I print what is inside authManager.authenticate(...) I get:
UsernamePasswordAuthenticationToken [Principal=test, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]
Principal=USERNAME, so the username is lost when authManager.authenticate() executes, and I don't understand the problem.
This is the service
package com.XX.XX.security.service;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import com.XX.XX.security.auth.AuthenticationRequest;
#Service
public class AuthenticationService {
private final AuthenticationManager authManager;
public AuthenticationService(AuthenticationManager authManager) {
this.authManager = authManager;
}
public AuthenticationResponse authenticate(AuthenticationRequest request){
authManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
return null;
}
}
Also this is the only other class where AuthenticationManager is mentioned
package com.XX.XX.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
#Configuration
public class ApplicationConfig {
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception{
return authConfig.getAuthenticationManager();
}
}
SecurityConfig:
package com.XX.XX.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter, AuthenticationProvider authenticationProvider) {
this.jwtAuthFilter = jwtAuthFilter;
this.authenticationProvider = authenticationProvider;
}
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/api/v1/auth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
I can add more information from any class if needed

Spring Boot WebSecurityConfigurerAdapter does not exists

I am new in spring boot, and I am trying to work with JWT.
I follwed many examples and all looks the same.
Before,15 days I make classic auth application with JWT and all works fine, but now I am facing some errors there.
https://github.com/nedim-bajric/basicJWTspring this is a repo of project that I was created before 15 days and it works fine.
Error I gets comming from SecurityConfiguration and it says that is not possible to find WebSecurityConfigurerAdapter, and it is not possilbe to #Overide configure method.
Also, there is problem with .antMatchers, it says Cannot resolve method 'antMatchers' in 'ExpressionInterceptUrlRegistry'.
There is my config file
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.unze.sis.security.jwt.AuthEntryPointJwt;
import com.unze.sis.security.jwt.AuthTokenFilter;
import com.unze.sis.security.services.UserDetailsServiceImpl;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
// securedEnabled = true,
// jsr250Enabled = true,
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
I tried few solutions that I found on stackowerflow, but it doesn't work.

The method withDefaults() is undefined for the type SecurityConfiguration [duplicate]

This question already has answers here:
Why is my method undefined for the type object?
(4 answers)
Closed 2 years ago.
I'm building a spring application with spring security. I have added basic authentication of a username and password in the code. I want to access APIs via a reactjs web app with this basic authentication. For this, I'm trying to add the cors policy.
In the security configuration of spring security, I'm getting the error:
The method withDefaults() is undefined for the type SecurityConfiguration
I have got all the necessary dependencies installed.
What can be the possible solution?
Attaching my code below.
package com.example.demo;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors(withDefaults()) //getting the error here
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").authenticated()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers(HttpMethod.GET).authenticated()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST", "OPTIONS"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Your SecurityConfiguration, nor the WebSecurityConfigurerAdapter have a method withDefaults(). You need to add a static import:
import static org.springframework.security.config.Customizer.withDefaults;

Spring Security CORS: Origin has been blocked by CORS Policy

I'm using spring boot for the first time in a project with angular, everything was working fine until I added the spring security dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Now I get this error in the client side:
Access to XMLHttpRequest at 'http://localhost:8080/api/v1/login' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I tried to change the config as the documentation suggest so I added class
src/main/java/com/example/securingweb/WebSecurityConfig.java
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer{
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
I also have this in my controller:
#CrossOrigin(origins = "http://localhost:4200")
As you have added Spring security dependency, so spring will enable Basic Auth which will validate your each and every request. And that enable CORS(Cross Origin Request Sharing) aswell. Though you have added CrossOrigin for every request that is not enough to disable the CORS.
More Details About CORS
So either you need to send the spring security generated token which will print on your console
or
you need to configure Spring security Configuration class which will validate your authetication or permit the specific url.
More about Spring Security here
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
#Configuration
public class CorsConfig {
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("GET","POST"));
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.filter.CorsFilter;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/h2-console/**"
);
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/offerTransactionCall").permitAll()
.anyRequest().authenticated();
}
}

Testing spring-boot security, Thymeleaf error

I'm using Thymeleaf with Spring-boot and I get a problem testing the security #withmockuser.
This is the code for testing the controller
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#SuppressWarnings("SpringJavaAutowiringInspection")
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SecurityConfiguration.class)
#WebMvcTest(IndexController.class)
public class IndexControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private IndexController IndexController;
#Test
#WithMockUser
public void testAuthenticated() throws Exception {
this.mvc.perform(get("/"))
.andExpect(status().is(200));
}
}
And this is the controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class IndexController {
private DemoshopService demoshopService;
#Autowired
public void setDemoshopService(DemoshopService demoshopService) {
this.demoshopService = demoshopService;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String list(Model model) {
model.addAttribute("demoshops", demoshopService.listAllDemoshops());
return "index";
}
}
It will give me the following error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception processing template ()
Does this mean Thymeleaf reads "/" as an template?
Without #withMockUser it does what I'm expecting.
Thank you for the help.
EDIT:
as requested the security configuration:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.antMatchers("/images/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("test1").password("password").roles("USER")
.and()
.withUser("test2").password("password").roles("USER")
.and()
.withUser("test3").password("password").roles("USER")
.and()
.withUser("test4").password("password").roles("USER")
.and()
.withUser("test5").password("password").roles("USER")
.and()
.withUser("test6").password("password").roles("USER");
}
}
The usernames are normally e-mail address but I changed them for now.
Without parameters #WithMockUser will run test with the username "user", the password "password", and the roles "ROLE_USER".
Use #WithMockUser(username="test1",roles={"USER"}) to run test for test1 user for example.

Categories