Search code examples
springspring-bootspring-mvcspring-security

An Authentication object was not found in the SecurityContext - Spring 5


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?


Solution

  • 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 {
    
    }