I am new to Spring Boot and Spring Security and have inherited a webapp project that uses them. We will be migrating the webapp to a new deployment environment. One of the things we will be changing is the authentication mechanism, so that it will operate in the new environment. Meanwhile, I'd like use some existing PostMan tests to exercise the REST endpoints, bypassing security. Basically, I want to disable security temporarily.
I have a class that provides global method level security:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
// ...
}
I have multiple controller classes, e.g., a class that provides information about users:
@RestController
public class UserController {
@ApiOperation(value = "Gets the list of users for an admin.")
@PreAuthorize("#oauth2.hasScope('dashboard') and #oauth2.hasScope('read') and hasRole('ROLE_SYSADMIN')")
@GetMapping(value = "/user/list", produces = MediaType.APPLICATION_JSON_VALUE)
@AuditAccess(message = "Accessing /user/list")
public ResponseEntity<List<UserResponse>> getUserList() {
// ...
}
// ...
}
If I try to run my PostMan tests, they fail, because there is no authentication mechanism set up in my local test environment. My first attempt to bypass security was to comment out the @EnableGlobalMethodSecurity
line, but that led to run-time errors, presumably due to the @PreAuthorize
annotations on the methods. If I comment out those also, I can get the tests to run. However, there are many such annotations spread across many files. I'd rather not comment out all that; I'd rather temporarily substitute a "do nothing" version of method security. Here's my attempt:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
/**
* This class is designed to let everything pass the method filter, i.e.,
* effectively removing method level security.
*/
class DoNothingMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
return filterTarget;
}
@Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
// do nothing
}
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
MethodSecurityExpressionHandler doNothingHandler =
new DoNothingMethodSecurityExpressionHandler();
return doNothingHandler;
// TODO restore below
// return new OAuth2MethodSecurityExpressionHandler();
}
}
If I try to run my PostMan test with
GET http://localhost:8080/myapp/user/list
I get an error:
2021-12-21 06:28:14,252 INFO [stdout] (default task-1) Request: http://localhost:8080/myapp/user/list }raised
2021-12-21 06:28:14,253 INFO [stdout] (default task-1) org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
2021-12-21 06:28:14,254 INFO [stdout] (default task-1) at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:379) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
2021-12-21 06:28:14,254 INFO [stdout] (default task-1) at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:223) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
2021-12-21 06:28:14,254 INFO [stdout] (default task-1) at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
2021-12-21 06:28:14,255 INFO [stdout] (default task-1) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,255 INFO [stdout] (default task-1) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,256 INFO [stdout] (default task-1) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,257 INFO [stdout] (default task-1) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,257 INFO [stdout] (default task-1) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,257 INFO [stdout] (default task-1) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,258 INFO [stdout] (default task-1) at mycompany.rest.controller.UserController$$EnhancerBySpringCGLIB$$f2b6b566.getUserList(<generated>) ~[classes:?]
2021-12-21 06:28:14,259 INFO [stdout] (default task-1) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_271]
2021-12-21 06:28:14,259 INFO [stdout] (default task-1) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_271]
2021-12-21 06:28:14,260 INFO [stdout] (default task-1) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_271]
2021-12-21 06:28:14,260 INFO [stdout] (default task-1) at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_271]
2021-12-21 06:28:14,260 INFO [stdout] (default task-1) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,261 INFO [stdout] (default task-1) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,261 INFO [stdout] (default task-1) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,263 INFO [stdout] (default task-1) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,264 INFO [stdout] (default task-1) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,265 INFO [stdout] (default task-1) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,265 INFO [stdout] (default task-1) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
2021-12-21 06:28:14,266 INFO [stdout] (default task-1) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
For some reason, we are still trying to authenticate.
This question is similar to some others (Spring Boot 2.0 Disable Default Security, Spring Oauth2 : Authentication Object was not found in the SecurityContext, and An Authentication object was not found in the SecurityContext - Spring 3.2.2), but involves different versions and a different use case. I haven't been able to figure out how to apply those answers to my situation. My Spring environment includes:
<spring.version>5.2.5.RELEASE</spring.version>
<spring.oath2.version>2.4.1.RELEASE</spring.oath2.version>
<spring.boot.version>2.2.6.RELEASE</spring.boot.version>
<spring.jwt.version>1.1.0.RELEASE</spring.jwt.version>
<spring.security.version>5.3.1.RELEASE</spring.security.version>
What would be an easy way to bypass security?
CodePudding user response:
"Switching" code (un-/commenting) for tests is a NO-GO! (In "strict teams" you may not even commit commented code! -> sonarqube)
Where in complex (distributed) environments, having (spring) @Profile("test")
(e.g. switching off security), sounds absolutely legitime!
Still it is [very good - essential] to (unit integration) test also your security ( configuration)!
Spring Security 5.5.x Reference Documentation, Chapter 19 Testing
This section describes the testing support provided by Spring Security.
19.1. Testing Method Security
Before we can use Spring Security Test support, we must perform some setup. An example can be seen below:
@RunWith(SpringJUnit4ClassRunner.class) // 1. @ContextConfiguration // 2. public class WithMockUserTests { ...
(We can have both in one: @SpringBootTest
;)
This is a basic example of how to setup Spring Security Test. The highlights are:
@RunWith
instructs the spring-test module that it should create an ApplicationContext. This is no different than using the existing Spring Test support. For additional information, refer to the Spring Reference@ContextConfiguration
instructs the spring-test the configuration to use to create the ApplicationContext. Since no configuration is specified, the default configuration locations will be tried. This is no different than using the existing Spring Test support. For additional information, refer to the Spring ReferenceSpring Security hooks into Spring Test support using the
WithSecurityContextTestExecutionListener
which will ensure our tests are ran with the correct user. It does this by populating theSecurityContextHolder
prior to running our tests. If you are using reactive method security, you will also needReactorContextTestExecutionListener
which populatesReactiveSecurityContextHolder
. After the test is done, it will clear out theSecurityContextHolder
. If you only need Spring Security related support, you can replace@ContextConfiguration
with@SecurityTestExecutionListeners
.Remember we added the
@PreAuthorize
annotation to ourHelloMessageService
and so it requires an authenticated user to invoke it. If we ran the following test, we would expect the following test will pass:@Test(expected = AuthenticationCredentialsNotFoundException.class) public void getMessageUnauthenticated() { messageService.getMessage(); }
(Tests expected (authentication) exception!), so one of:
19.1.2. @WithMockUser
...
19.1.3. @WithAnonymousUser
...
19.1.4. @WithUserDetails
...
19.1.5. @WithSecurityContext
...
19.1.6. Test Meta Annotations
... and the rest of the chapter/reference, will be useful.
CodePudding user response:
Instead of removing @EnableGlobalMethodSecurity
, you can keep it but just disable its prePostEnabled
.
But it requires to have at least of one the following is enabled (see this for the details) :
- prePostEnabled (For enable
@PreAuthorize
/@PreFilter
/@PostAuthorize
/@PostFilter
) - securedEnabled (For enable
@Secured
) - jsr250Enabled (For enable JSR-250 annotations such as
@DenyAll
,@PermitAll
,@RolesAllowed
) - define a custom
MethodSecurityMetadataSource
Since you only enable prePostEnabled
in your existing configuration , I believe you should not use any @Secured
and JSR-250 annotation on any methods now.
So the following trick should just disable @PreAuthorize
only while keeping other things remain unchanged :
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = false, securedEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
or
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = false, jsr250Enabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
CodePudding user response:
You can try setting prePostEnabled = false and then removing any authentication filters in WebSecurityConfigurerAdapter implementation with something like
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests()
.anyRequest().permitAll();
}