I have a spring-boot web application that declares some security through this class:
#Configuration
#EnableWebSecurity
#Order(Ordered.LOWEST_PRECEDENCE - 50) // needs to be after SpringBootAuthenticationConfigurerAdapter to register default in memory user
public class StorefrontSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER - 1)
#Configuration
public static class MyStorefrontSecurityConfig extends WebSecurityConfigurerAdapter {
.....
}
and it all works fine. I also add these annotations to some of my service methods:
#PreAuthorize("hasPermission(#entity, 'APPROVE') or hasPermission(#entity, 'ADMINISTRATION') or hasRole('ROLE_ADMINGROUP')")
void approve(final EntityModificationEntityDefinition entity);
#PreAuthorize("hasPermission(#entity, 'APPROVE') or hasPermission(#entity, 'ADMINISTRATION') or hasRole('ROLE_ADMINGROUP')")
void reject(final EntityModificationEntityDefinition entity);
and for now they don't do much - which is perfectly fine. But now I create jar file with the following configuration:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class PersonalizationConfig extends GlobalMethodSecurityConfiguration {
private final Logger LOG = LogManager.getLogger(getClass());
/* Global Method Security */
#Override
public AccessDecisionManager accessDecisionManager() {
final List<AccessDecisionVoter<? extends Object>> accessDecisionVoters = new ArrayList<>();
accessDecisionVoters.add(new RoleVoter());
accessDecisionVoters.add(new AuthenticatedVoter());
accessDecisionVoters.add(new PreInvocationAuthorizationAdviceVoter(preInvocationAuthorizationAdvice()));
final UnanimousBased accessDecisionManager = new UnanimousBased(accessDecisionVoters);
accessDecisionManager.setAllowIfAllAbstainDecisions(true);
return accessDecisionManager;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return this.defaultMethodSecurityExpressionHandler();
}
This jar has a spring.factories file in META-INF so that being a spring-boot application the #Configuration is loaded. Now I expect when I include this jar in the classpath to have the #PreAuthorize annotations to start working. However what I see is that AbstractSecurityExpressionHandler is invoked and it calls the abstract method createSecurityExpressionRoot(authentication, invocation); which always goes to DefaultWebSecurityExpressionHandler and never to the DefaultMethodSecurityExpressionHandler. I can see the DefaultMethodSecurityExpressionHandler is constructed when my application starts, so I'm really not sure what happens here.
EDIT: Here's my spring.factories file:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.nemesis.platform.module.personalization.core.config.PersonalizationConfig
Related
I've run to some issues trying to migrate dropwizard from 1.2 to 2.0.24 (and 2.0.25) related to DI, wondering if someone has had same issues or any ideas.
We have one application,
public class Account extends Application<AccountConfiguration> {
public static void main(String[] args) throws Exception {
new Account().run(args);
}
#Override
public void initialize(Bootstrap<Configuration> bootstrap) {
bootstrap.addBundle(new DropwizardBundle<>());
}
#Override
public void run(AccountConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().register(new SessionResource(authenticationService));
}
}
The DropWizardBundle class binds an instance to a class:
public void run(AccountConfiguration configuration, Environment environment) {
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
bind(configuration.getResponder()).to(Responder.class);
}
});
And the SessionResource looks like
#Path("/sessions")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON, "application/v1+json"})
#RequiredArgsConstructor
#Timed
public class SessionResource {
#Inject
private Responder responder;
private final AuthenticationService authenticationService;
#POST / #GET methods
}
The current code, in Dropwizard 1.2 is running and Responder is injected. Switching to 2.0.24/25 Responder is null. I am wondering if I missed something in the migration..
I'm not sure if this is working the way you intended (or the way you are think it is) in version1.X for you. I do not know what configuration.getResponder() is returning so I can't really test your case. I found for resource classes using DI, a lot of problems I see people face can be solved by registering the class and allow the H2K instantiate the resources for you.
environment.jersey().register(SessionResource.class);
You will have to update how AuthenticationService is passed into the resource by registering it to so it can also be injected. The below is an example of registering it as a AuthenticationService as a singleton.
public void run(AccountConfiguration configuration, Environment environment) {
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
//Not sure how AuthenticationService is instantiate but this is an example
bind(new AuthenticationService()).to(AuthenticationServic.class).in(Singleton.class);
}
});
I'm trying to make CDI work on JBeret SE.
This is my code:
SampleBatchlet class
#Named
public class SampleBatchlet extends AbstractBatchlet
{
#Inject
#BatchProperty(name = "foo")
String foo;
#Inject
StepContext stepContext;
#Inject
Logger logger;
#Override
public String process() throws Exception {
final String say = stepContext.getProperties().getProperty("say");
System.out.println("hello foolish");
return null;
}
}
SampleBatchletTest class
#EnableWeld
class SampleBatchletTest {
#Inject
Logger logger;
#WeldSetup
public WeldInitiator weld = WeldInitiator
.from(
LoggerProducer.class
)
.activate(
RequestScoped.class,
ApplicationScoped.class
)
.build();
#Test
void app() throws InterruptedException {
final JobOperator jobOperator = BatchRuntime.getJobOperator();
long id = jobOperator.start("simplebatchlet", null);
final JobExecutionImpl jobExecution = (JobExecutionImpl) jobOperator.getJobExecution(id);
jobExecution.awaitTermination(5, TimeUnit.SECONDS);
Assertions.assertEquals(BatchStatus.COMPLETED, jobExecution.getBatchStatus());
}
}
Server class
#ApplicationScoped
public class Server {
#Inject
private Logger logger;
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) throws InterruptedException {
logger.info("init");
}
LoggerProducer class
public class LoggerProducer {
#Produces
public Logger produceLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
The issue is Logger instance is not injected on SampleBatchlet, whereas is correctly injected either on test and server class above.
Any hints?
LITTLE UPDATE
By reading this reference
https://jberet.gitbooks.io/jberet-user-guide/content/batch_properties/
I discovered java.util.logging.Logger can be injected.
Therefore I added
<batchlet ref="it.infocert.shop.main.SampleBatchlet" >
<properties>
<property name="logger" value="java.util.logging.Logger" />
</properties>
</batchlet>
where value can be actually anything..
On SampleBatchlet I added
#Inject
#BatchProperty
Logger logger;
and now it is injected. I'm a bit perplexed by the way, because I wish to use another logger implementation..
When injecting via #BatchProperty, JBeret tries to check the type of injection field and match it up with the injection value, and instantiate an instance for injection. That's why the logger created by JBeret, instead of your own logger, is injected. For details, see JBeret BatchBeanProducer.java
To inject your own logger via a producer method, you may need to add a qualifier to disambuiguise it. For example,
public class LoggerProducer {
#Produces
#Named("myLogger")
public Logger produceLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
#Inject
#Named("myLogger")
Logger logger;
I changed batchet ref on my xml from:
<batchlet ref="it.infocert.shop.main.SampleBatchlet">
to:
<batchlet ref="sampleBatchlet">
now it works
Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}
Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}
This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}
I am using Spring Boot 1.2.0 with embedded h2database.
The database is initialized using schema.sql file in the classpath.
It worked fine until I added #EnableGlobalMethodSecurity annotation to the WebSecurityConfiguration class.
java.lang.IllegalStateException: ApplicationEventMulticaster not initialized exception is thrown from DataSourceInitializer.runSchemaScripts
What may be the problem ?
Here is the code:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#EnableGlobalMethodSecurity(securedEnabled = true)
#Configuration
class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
I think the solution is to not use the init method to setup JDBC authentication. Try with a separate class that extends GlobalAuthenticationConfigurerAdapter and override the config method.
Per the Spring Security 3.2.0 documentation I've created a Spring Security configuration and reference it in getRootConfigClasses:
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfiguration.class, SpringSecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringWebConfiguration.class};
}
I can prove that this mostly works as Spring Security forces users to login per my configurataion. The problem is with method security. I've annotated SpringSecurityConfig with #EnableGlobalMethodSecurity like so:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
then annotated a method in my contoller with:
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
with the idea of only allowing those with ROLE_ADMIN access to this controller method. However, those logged in with ROLE_USER and ROLE_ADMIN can call this method, not what is expected.
By modifying my web application initializer to doubly include the Spring Security configuration it starts to work, but I'd like to use method authentication on methods in my root context as well the web context, which I can't seem to make happen:
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfiguration.class, SpringSecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringSecurityConfig.class, SpringWebConfiguration.class};
}
Does each context need its own security config? Or should one in the parent context suffice?
Thanks.
I finally managed to have a root context, with a child web context and have #Pre and #Post authorization annotations work for controllers.
The trick was to expose the AuthenticationProvider created in the RootContext, which is not exposed by default.
So, my setup is :
#Order(1)
public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer {}
#Order(2)
public class ApiDispatcherInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { ApiWebMvcConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/*" };
}
}
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// IMPORTANT: to expose it to the WebContext
#Bean(name = "myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
#Configuration
#EnableWebMvc
#EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = true) // <-- IMPORTANT to make it work for controllers
#ComponentScan(basePackageClasses = { foo.bar.Package.class }, useDefaultFilters = false, includeFilters = { #Filter(Controller.class) })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
}
Hope this might help someone.