Spring 2 security: 403 Forbidden [duplicate] - java

This question already has answers here:
Spring Security configuration: HTTP 403 error
(8 answers)
Closed 1 year ago.
I have an app that previously was running tests using Spring Boot 1 and which I have updated to 2.0.9.RELEASE.
There are now issues with Spring Security. I know this is the case because if I remove
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>test</scope>
</dependency>
(and any new WebSecurityConfigurerAdapter classes) the tests still succeed. The tests are essentially going to a project controller 'HomeController' and from there going to a service and using a RestTemplate to perform various operations. In reality this is a different app, and if I were writing it from scratch, I would probably do a wiremock but for now and the purposes of this question there is a controller in the test package simulating the required behaviour.
By adding this simple class, I have got past the 403 on the local controller
#TestConfiguration
#EnableWebSecurity
#Order(500)
public class DefaultSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure( HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/").permitAll();
}
}
However, I'm struggling to get past the security on using the RestTemplate. The restTemplate bean is created with basic user and password (simplified a little here)
#Bean
public RestTemplate restTemplate( RestTemplateBuilder restTemplateBuilder ) {
return restTemplateBuilder
.basicAuthorization( "user", "password" )
.build();
}
I can tell by debugging and looking at the interceptors that these are being set.
Now I have added a new configuration class
#TestConfiguration
#EnableWebSecurity
#Order (50)
public class RestTemplateSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure( AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication()
.withUser("user")
.password(encoder.encode("password"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/otherpath/**")
.authenticated()
.and()
.httpBasic();
}
}
But this seems to have no effect on the RestTemplate calls which are consistently called with basic security of user and password and consistently return 403 Forbidden.
The test class is annotated like this
#AutoConfigureMockMvc
#RunWith( SpringRunner.class )
#SpringBootTest( classes = { DefaultSecurityConfiguration.class, ResttemplateSecurityConfiguration.class }, webEnvironment = DEFINED_PORT )
#TestPropertySource( "classpath:test.properties" )
public class HomeControllerTest {
...
And triggered with a 'standard' mockMvc.perform( ...
With the DefaultSecurityConfiguration class in place these fire without issue it is the later calls in the app code using the restTemplate that fail
Is there anything obvious?

Replace:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/otherpath/**")
.authenticated()
.and()
.httpBasic();
}
By this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/otherpath/**")
.authenticated()
.and()
.httpBasic();
http.csrf().disable();
}

Related

spring security, and basic auth only one entryPoint : how manage error on the other endpoint

I need to add a basic auth in only one of my controller method
Other method don't have an auth, but still have a #PreAuthorize in order to check if the parameters are valid.
My issue is , httpbasic transform all the exception throwed by preAuthorize in 401 ( some should be 403, etc).
I have the feeling that all my endpoint are under a basic auth, not just the ones I have use #Secured
How can I avoid that?
My code:
#Controller
class MyController {
#Secured("BASIC_AUTHENTIFIED")
public void someMethodOnlyForAuthentified(){...}
#PreAuthorize("check(parameters)")
public void someMethodForEveryone(List<String> parameters){...}
}
And in my security config:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(login).password(password).roles("BASIC_AUTHENTIFIED");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.authorizeRequests().antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().csrf().disable();
}

Spring security giving 401 on all api end points

I have seen a lot of posts on the same problem, but no solution worked for me.
I have an API secured with spring security as below.
#EnableWebSecurity
#Component
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired private UserService userService;
public SecurityConfiguration() {
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/actuator/shutdown", "/api/register");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I need endpoints /api/register and /actuator/shutdown available without authentication. But, as it turned out, all the endpoints are returning the same 401 status code.
try with this.
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
//no authentication needed for these context paths
.authorizeRequests()
.antMatchers("/your Urls that dosen't need security/**").permitAll()
We implemented a similar approach as mentioned by Supun Above,
http
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
.and().httpBasic()
You can keep 'AUTH_WHITELIST' as below to keep adding multiple Endpoints
private static final String[] AUTH_WHITELIST = {
// -- swagger ui
"/api/register",
"/actuator/shutdown"
};

JWT authentication with fallback to SAML2 for the same path

I'm using spring-security-saml2-service-provider for authentication in one of my spring boot applications and I'm using a custom JwtAuthorizationFilter (via a http Authentication header) in a different spring boot application.
They both work perfectly on their own.
Now I need to write a spring boot application that uses both of them. If the JWT token is available (Authentication header), then use the JwtAuthorizationFilter, otherwise use saml2Login.
The SAML2 configuration looks like this: (There is no filter, just the saml2Login)
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/saml2/service-provider-metadata/**").permitAll()
.antMatchers("/**").authenticated().and()
// use SAML2
.saml2Login()
.addObjectPostProcessor(new ObjectPostProcessor<OpenSamlAuthenticationProvider>() {
public <O extends OpenSamlAuthenticationProvider> O postProcess(O samlAuthProvider) {
samlAuthProvider.setAuthoritiesExtractor(authoritiesExtractor());
samlAuthProvider.setAuthoritiesMapper(authoritiesMapper());
return samlAuthProvider;
}
})
;
}
The JWT configuration looks like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/**").authenticated().and()
// use JWT
.addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtUtil))
;
}
I think I need something like a JwtOrSaml2AuthenticationFilter but don't know how to do that.
The solution is to
Duplicate the configuration with #Order and
Set a header based requestMatcher before the addFilter
#EnableWebSecurity
public class SecurityConfiguration {
#Order(100) // lower number = higher priority
#Configuration
#RequiredArgsConstructor
public static class AppSecurityJWT extends WebSecurityConfigurerAdapter {
final JWTUtil jwtUtil;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/saml2/service-provider-metadata/**", "/idm-app/**").permitAll()
.antMatchers("/**").authenticated().and()
// This configuration will only be active if the Authorization header is present in the request
.requestMatcher(new RequestHeaderRequestMatcher("Authorization")).addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtUtil))
;
}
}
#Order(101)
#Configuration
#RequiredArgsConstructor
public static class AppSecuritySAML2 extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/saml2/service-provider-metadata/**", "/idm-app/**").permitAll()
.antMatchers("/**").authenticated().and()
// This whole configuration will only be active, if the previous (100) didn't match
.saml2Login()
//...
;
}
}

Spring Boot 2 security - pre-authenticated token - allow healthcheck

My Spring Boot Actuator healthCheck is blocked because of a (pre_authenticated) token is missing.
There are many answers available, BUT this is question has interference with pre-authenticated security. As far as I searched, this is NOT a duplicate.
How can I allow the health check in a pre-authenticated security environment?
My question is also, do I need more settings (in e.g. the application.properties)?
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new XyzPreAuthenticatedGrantedAuthoritiesUserDetailsService());
auth.authenticationProvider(provider);
}
// Try-1, see below
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(xyzTokenRequestHeaderAuthenticationFilter(), RequestHeaderAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated();
}
#Bean
public XyzTokenRequestHeaderAuthenticationFilter xyzTokenRequestHeaderAuthenticationFilter() throws Exception {
XyzTokenRequestHeaderAuthenticationFilter filter = new XyzTokenRequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
My second try was:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(xyzTokenRequestHeaderAuthenticationFilter(), RequestHeaderAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll();
}
It looks like the xyz filter is not implemented in the 'perfect' way.
This way will help you get things workin':
1 - use the management port:
management.server.port=8081
management.security.enabled=false
management.server.address=127.0.0.1
management.server.ssl.enabled=false
management.endpoints.health.sensitive=false
management.endpoint.health.show-details=always
2 - configure both ways web and api. Use this beyond the standard parts:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/**").authenticated();
http.addFilterBefore(xyzTokenRequestHeaderAuthenticationFilter(), AbstractPreAuthenticatedProcessingFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/actuator/**");
}
3 - Inside the Docker container, use the 8081 port for the healthCheck.
Try to add in .ignoring() and add #EnableGlobalMethodSecurity(prePostEnabled = true), #Configuration at class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/actuator/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(xyzTokenRequestHeaderAuthenticationFilter(), RequestHeaderAuthenticationFilter.class);
}
}
The problem seems to be with your XyzTokenRequestHeaderAuthenticationFilter implementation. If you wrote that by extending RequestHeaderAuthenticationFilter, then you must set the property exceptionIfHeaderMissing to false.
If you didn't extend that Spring Security pre auth core class then you need to show the implementation.

Basic authentification ignoring password validation

I configure WebSecurityConfig, create user in memory
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("username")
.password(passwordEncoder().encode("password"))
.authorities("READ_ORDERS")
.roles("USER");
}
configure WebSecurityConfig
#Configuration
#Order(1)
public static class BasicAuthenticationAdapter extends WebSecurityConfigurerAdapter {
private final AuthenticationEntryPoint authEntryPoint;
#Autowired
public BasicAuthenticationAdapter(AuthenticationEntryPoint authEntryPoint) {
this.authEntryPoint = authEntryPoint;
}
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/orders**")
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.csrf().disable()
.httpBasic().authenticationEntryPoint(authEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
When i try to Authorise with invalid credentials first time - 401 exception, it's ok.
But after successful authorization, when i use invalid username and password,
i also authorised.
What can be the problem ?
That is how basic authentication works. As soon as you have logged in successfully the valid credentials will always be posted.
Spring security works with SessionCreationPolicy, and default policy is IF_REQUIRED. It means spring creates session if it does not have and is required.
In order to solve your issue you have to change this policy.
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
Restart your server and try again.

Categories

Resources