#PostAuthorize not working - spring

I'm trying to build a simple handler method that will prevent users to browse item's that belong to different user. The method is below:
#PostAuthorize("principal.username == #model['username']")
#RequestMapping(value = "/show/{id}", method = RequestMethod.GET)
public String single(#PathVariable Long id, Model model) {
Item item = itemService.findById(id);
model.addAttribute("item", item);
model.addAttribute("username", item.getUser().getUsername());
return "item";
}
so the main idea is to compare principal.username with the username stored in the model. I'm using Spring 5.0.5, security 5.0.4. Java Config without boot. My config holds (amnogst other things)
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
and
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
final DefaultMethodSecurityExpressionHandler expressionHandler
= new DefaultMethodSecurityExpressionHandler();
return expressionHandler;
}
}
Despite of this, I'm able to login and than via direct url access items of different user. Any hint is welcome. Thanks

Add this method security config to your project. This configuration act as globally and of-course your proxyTargetClass = true so that, spring can generate proxy for your controller class also.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
/*
We can enable annotation-based security using the #EnableGlobalMethodSecurity annotation
on any #Configuration instance.
*/
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
Hope this will solve your problem.

OK, so in case others experience a same problem. The problem is that I was using a #Pre/#PostAuthorize annotation on a Controller. Controllers are typically not behind an interface, and by the default, the aspect that configure the behaviour didn't kick in.
The solution for me was to #EnableAspectJAutoProxy on a WebConfig class that scan packages in search for controllers:
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"rs.saga.web"})
public class WebConfig implements WebMvcConfigurer {
...
}

Related

Spring Secured Rest Controller is not found in WebMVCTest

When using #Secured on a REST-Controller, implementing an interface, the Controller is not found in a #WebMvcTest. Either removing the #Secured annotation or removing the implements on the class will make it run in the test.
#Controller
#RequestMapping(path="/failing")
public class FailingTestController implements MyPasswordApi {
#RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE, path = "/test")
#Secured("ROLE_USER")
public ResponseEntity<GetEntity> getMethod()
and
#Controller
#RequestMapping(path = "/running")
public class RunningTestController {
#RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE, path = "/test")
#Secured("ROLE_USER")
public ResponseEntity<GetEntity> getMethod() {
are both used in different jUnit-5 Tests. The "RunningTest" will succeed (i.e. the GET-Request will have status 200), whereas the "FailingTest" will end up with a status 404. Using the injected RequestMapppingHanderMapping one can see, that the controller with the inheritance is not bound.
In fact, in the application, both controllers are found.
My question is, how to test an controller implementing security and an interface.
A testcase is found on github: https://github.com/sanddorn/Spring-Boot-Security-Rest-Showcase
The real answer could only be found in the github-repo (see question). The root problem was to annotate the application class with #SpringBootApplication and #EnableGlobalMethodSecurity like
#SpringBootApplication
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class TestApplication {
public static void main(String []argv) {
SpringApplication.run(TestApplication.class);
}
}
Using a different configuration class like
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class TestConfiguration {
}
and removing all extra annotation to the application-class solves the problem.
You are using wrong annotation, replace Controller to RestController
#RestController
#RequestMapping(path = "/running")
public class RunningTestController {
}

Pointcut expression not matching the Spring Data method despite specfying exact name in expression

In my Spring Boot project, I have AddressRepository that brings all addresses from Database. I have an Aspect class and a pointcut expression that executes after the findAll() method called. When I execute my test case, the Advice is not being triggered and other methods like findAll(Sort sort), findAll(Pageable pageable) work just fine. I am not sure if this is a bug with Spring Boot or my expression. I tried with Spring Boot 2.0.5 and 2.1.0, nothing seemed to solve my problem
AddressLogging.java
#Aspect
#Configuration
public class AddressLogging {
private Logger log=LoggerFactory.getLogger(AddressLogging.class);
//#Pointcut("execution(* com.springtesting.repo.AddressRepository.*(..))")
#Pointcut("execution(* com.springtesting.repo.AddressRepository.findAll())")
public void getAddresses() {}
#After("getAddresses()")
public void afterAdvice() {
log.error("Log Message: Inside afterAdvice() advice");
}
}
AopTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
public class AopTest {
#Autowired
private AddressRepository addressRepository;
#Test
public void getAddresses() {
//addressRepository.findAll(PageRequest.of(0,20, Sort.by("id")));
addressRepository.findAll();
}
#Test
public void findAddressById() {
addressRepository.findById(1L);
}
}
AddressRepository
public interface AddressRepository extends JpaRepository<Address,Long> {}
A Spring AOP aspect should also be a #Component and be picked up by component scan. I have no idea why you added #Configuration to the aspect instead because there is no configuration here.
Maybe your test of a separate configuration class should bear the #Configuration annotation and you should also activate something like #EnableAspectJAutoProxy(proxyTargetClass = true) and #ComponentScan(basePackages = { "de.scrum_master" }).
Here is a snippet from one of my Spring AOP playground projects (I hardly use it, I don't use Spring AOP or even Spring itself, usually I use the more powerful AspectJ:
package de.scrum_master.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ComponentScan(basePackages = { "de.scrum_master" })
public class Application2 {
public static void main(String[] args) throws Exception {
ApplicationContext appContext = new AnnotationConfigApplicationContext(Application2.class);
B b = (B) appContext.getBean("b");
System.out.println(b.getData("bbb"));
A a = (A) appContext.getBean("b");
System.out.println(a.getData("aaa"));
}
}

Disable Spring Boot Security #Secured at runtime [duplicate]

Is there a way I can disable the global method security using the boolean securityEnabled from my config.properties? Any other approach?
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled=true)
#PropertySource("classpath:config.properties")
public class SecurityConfig
extends WebSecurityConfigurerAdapter {
#Value("${securityconfig.enabled}")
private boolean securityEnabled;
...
}
The easiest way to do this is:
Extract method security to its own class
Remove the securedEnabled attribute entirely
Override the customMethodSecurityMetadataSource method and return the result based on the configured value.
For example:
#EnableWebSecurity
#Configuration
#PropertySource("classpath:config.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
#EnableGlobalMethodSecurity
#Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${securityconfig.enabled}")
private boolean securityEnabled;
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return securityEnabled ? new SecuredAnnotationSecurityMetadataSource() : null;
}
}
I've managed this by defining a Spring "securityDisabled" profile and conditionally applying security config based off that. I'm using Spring Boot 2.0.2. I believe this should work if not using Spring Boot and in previous versions of Spring Boot, but I have not tested. It's possible some tweaks may be required to property and class names because I know in Spring 2.0 some of that changed.
// In application.properties
spring.profiles.include=securityDisabled
Then my security config looks like this:
#Configuration
public class SecurityConfig {
// When the securityDisabled profile is applied the following configuration gets used
#Profile("securityDisabled")
#EnableWebSecurity
public class SecurityDisabledConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure http as needed from Spring Security defaults when
// NO security is desired
}
}
// When the securityDisabled profile is NOT applied the following configuration gets used
#Profile("!securityDisabled")
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityEnabledConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure http as needed from Spring Security defaults when
// security is desired
}
}
}
In Springboot2, a simple solution consists in replacing the security method interceptor by a dummy one when the security is off :
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${disableSecurity}")
private boolean disableSecurity;
public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
return disableSecurity ? new SimpleTraceInterceptor()
: super.methodSecurityInterceptor(methodSecurityMetadataSource);
}
}
Thanks to Rob Winch for the solution. For folks who would like to do something similar but with prePostEnabled i have tried and tested the below similar approach and works just fine.
#EnableGlobalMethodSecurity(securedEnabled = true)
#Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${security.prePostEnabled}")
private boolean prePostEnabled;
#Autowired
private DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return prePostEnabled ? new PrePostAnnotationSecurityMetadataSource(new ExpressionBasedAnnotationAttributeFactory(defaultMethodSecurityExpressionHandler)) : null ;
}}
EDIT: In addition to above i realized it is required to add following beans to the class. The below will help using the expression based pre invocation checks along with avoiding "ROLE_" prefix that is defaulted in all the handlers
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
//This is required in order to allow expression based Voter to allow access
accessDecisionManager.getDecisionVoters()
.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
//Remove the ROLE_ prefix from RoleVoter for #Secured and hasRole checks on methods
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(""));
return accessDecisionManager;
}
/**
* Allow skip ROLE_ when check permission using #PreAuthorize, like:
* #PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
* Added all the Beans
*/
#Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultMethodSecurityExpressionHandler;
}

Add a Servlet Filter in a Spring Boot application

I'd like to have ETag suport. For this purpose there is a ShallowEtagHeaderFilter which does all the work. How can I add it without declaring it in my web.xml (which actually does not exist, because I somehow got by without it so far)?
P.S. I use Spring Boot 1.1.4
P.P.S. Here's a full solution
package cuenation.api;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import javax.servlet.DispatcherType;
import java.util.EnumSet;
#Configuration
public class WebConfig {
#Bean
public FilterRegistrationBean shallowEtagHeaderFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new ShallowEtagHeaderFilter());
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
registration.addUrlPatterns("/cue-categories");
return registration;
}
}
When using Spring Boot
As mentioned in the reference documentation, the only step needed is to declare that filter as a Bean in a configuration class, that's it!
#Configuration
public class WebConfig {
#Bean
public Filter shallowEtagHeaderFilter() {
return new ShallowEtagHeaderFilter();
}
}
When using Spring MVC
You're probably already extending a WebApplicationInitializer. If not, then you should convert your webapp configuration from a web.xml file to a WebApplicationInitializer class.
If your context configuration lives in XML file(s), you can create a class that extends AbstractDispatcherServletInitializer - if using configuration classes, AbstractAnnotationConfigDispatcherServletInitializer is the proper choice.
In any case, you can then add Filter registration:
#Override
protected Filter[] getServletFilters() {
return new Filter[] {
new ShallowEtagHeaderFilter();
};
}
Full examples of code-based Servlet container initialization are available in the Spring reference documentation.
A bit late answer.
My solution was to create custom annotation:
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
// ...
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Component
public #interface Filter {
#AliasFor(annotation = Component.class, attribute = "value")
String value() default "";
}
And then simply apply it to the filter implementations:
#Filter
public class CustomFilter extends AbstractRequestLoggingFilter {
#Override
protected void beforeRequest(HttpServletRequest request, String message) {
logger.debug("before req params:", request.getParameterMap());
}
#Override
protected void afterRequest(HttpServletRequest request, String message) {
logger.debug("after req params:", request.getParameterMap());
}
}
See more: #AliasFor, Spring custom annotations question

Spring: Properly setup #ComponentScan

I have following set up for my Spring Application Context.
#Configuration
public class RmiContext {
#Bean
public RmiProxyFactoryBean service() {
RmiProxyFactoryBean rmiProxy = new RmiProxyFactoryBean();
rmiProxy.setServiceUrl("rmi://127.0.1.1:1099/Service");
rmiProxy.setServiceInterface(Service.class);
return rmiProxy;
}
}
#Configuration
public class LocalContext {
#Bean
public Controller Controller() {
return new ControllerImpl();
}
}
#Configuration
#Import({RmiContext.class, LocalContext.class})
public class MainContext {
}
The above setup works fine, but I want to enable #ComponentScan annotating Controllers with #Component as there are many Controllers in my application which is tedious when declared one by one using #Bean.
#Configuration
#ComponentScan(basePackageClasses = {Controller.class})
public class LocalContext {
/* ... */
}
The problem is that when I do #ComponentScan(basePackageClasses = {Controller.class}), the previously fine working RmiProxyFactoryBean are not recognized or can't be created.
So, How do I configure my MainContext so that both beans via RMI and local beans are created?
#Configuration is also a candidate for component scan, so you can scan all the beans in RmiContext and all controllers in your controller package by:
#Configuration
#ComponentScan(basePackages = {"org.example.controllers", "package.of.RmiContext"})
public class MainContext {
}
--edit--
#Configuration is a candidate for component scan, here is the test case that works in my pc:
package scan.controllers;
#Controller
public class ExampleController {
}
package scan;
public interface RMIService {
}
package scan;
#Configuration
public class RmiContext {
#Bean
public RmiProxyFactoryBean service() {
RmiProxyFactoryBean rmiProxy = new RmiProxyFactoryBean();
rmiProxy.setServiceUrl("rmi://127.0.1.1:1099/Service");
rmiProxy.setServiceInterface(RMIService.class);
rmiProxy.setLookupStubOnStartup(false);
return rmiProxy;
}
}
package scan;
#Configuration
//MainContext will auto scan RmiContext in package scan and all controllers in package scan.controllers
#ComponentScan(basePackages = {"scan", "scan.controllers"})
public class MainContext {
}
package scan;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={MainContext.class})
public class TestContext {
#Autowired private RMIService rmi;
#Autowired private ExampleController controller;
#Test
public void test() {
//both controller and rmi service are autowired as expected
assertNotNull(controller);
assertNotNull(rmi);
}
}
May be you could try using the base packages of your classes (RMI, Controller):
#ComponentScan(basePackages = {"your controller package", "your rmi package"})
If the RMI classes package is different than controller then they will fail to instantiate by spring.
If I understand you correctly, you use "#ComponentScan(basePackageClasses" but it is not detecting and registering your spring beans?
I had the same issue a few minutes ago. I did not give up and tried all funny guesses. One guess did it.
I had to add an XML component-scan entry in XML. I just put a dummy package there, like below:
component-scan base-package="dummy.filler.to.enable.component.scan"
It seems that the component-scan in XML enables the #ComponentScan.
[Late Edit: I noticed, my spring xml schema is spring 2.5. Anyway, I dont know if this matters. Best Regards.]

Resources