I'm facing some issues when I run and hit my rest service, I saw that my service instance got a #Proxy value. Below you can see the classes that I have:
Initializer class:
public class AllureWebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
createDispatcherServlet(servletContext);
initializeListener(servletContext);
}
private void initializeListener(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = createContext();
servletContext.addListener(new ContextLoaderListener(rootContext));
}
private void createDispatcherServlet(final ServletContext context) {
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(AllureWebConfig.class);
ServletRegistration.Dynamic dispatcher = context.addServlet(AllureWebConstants.DISPATCHER, new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(AllureWebConstants.SLASH);
}
private AnnotationConfigWebApplicationContext createContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AllureWebConfig.class);
return context;
}
}
Config class:
#Configuration
#Import(AllureServiceConfig.class)
#ComponentScan(basePackages = {"com.allure.events.web.controller"})
#EnableWebMvc
public class AllureWebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ApplicationContextProvider applicationContextProvider;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(AllureWebConstants.RESOURCES_DOUBLE_WILDCARD)
.addResourceLocations(AllureWebConstants.RESOURCES);
}
#Bean
public ApplicationContextProvider applicationContextProvider() {
return applicationContextProvider;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource;
messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:META-INF/web/resources/release");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_NULL);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
Controller class:
#Controller
public class SupplierEndpoint extends AllureEndpoint {
#Autowired
SupplierService supplierService;
#RequestMapping(value = AllureWebConstants.SUPPLIERS, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Supplier> getSuppliers() {
List<Supplier> suppliers = getSuppliersList(supplierService.getSuppliers());
return suppliers;
}
}
Service interface
public interface SupplierService {
public List<SupplierDto> getSuppliers();
}
Service Impl
#Service
public class SupplierServiceImpl implements SupplierService {
#Autowired
private SupplierMapper supplierMapper;
#Autowired
private SupplierDtoHelper supplierHelper;
#Override
#Transactional
public List<SupplierDto> getSuppliers() {
List<Supplier> suppliers = supplierMapper.getSuppliers();
List<SupplierDto> dtos = new ArrayList<SupplierDto>();
for (Supplier supplier : suppliers) {
dtos.add(supplierHelper.convertEntityToDto(supplier));
}
return dtos;
}
}
if I debug, I see that the instance value is :
supplierService - $Proxy28
|
-> h = JdkDynamicAopProxy
Can someone guide me, please?
Regards,
Edgardo Quiroz
It's not an issue when you work with Spring.
Spring Framework uses Proxy to make the different features possible.
Here you have a Transactional Annotation and proxy is been used to implement it.
Related
My Spring Boot application implements the TenantStore example for storing data in ThreadLocalTargetSource detailed in this link
#Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
The working example allows for the TenantStore object to be set and injected by the Spring Framework. My version of the TenantFilter class described in that article sets the properties of the TenantStore object whenever a Servlet request is made
#Autowired
private TenantStore tenantStore;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
String token = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
if (token != null) {
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (oAuth2AccessToken.getAdditionalInformation() != null) {
String tenantName = (String) oAuth2AccessToken.getAdditionalInformation().get("tenant");
storeTenantInThread(tenantName);
}
}
}
chain.doFilter(request, response);
} catch (ResourceNotFoundException e) {
log.error(e.getMessage());
} finally {
clearTenant();
}
}
private void storeTenantInThread(String tenantName) {
tenantStore.setName(tenantName);
}
private void clearTenant() {
tenantStore.clear();
}
I then have a number of services where TenantStore is autowired and in each of these services the TenantStore contains the information that was populated in the doFilter() method. Except for one class. For some reason the properties of the TenantStore in this class are still null. The name of the class affected is MyCacheService and the architecture is as follows:
#RestController
#RequestMapping("/here")
public class MyController {
#Autowired
private MyService myService
#GetMapping
public ResponseEntity myGetMethod(#RequestParam("text") String text) {
myService.myMethod(text);
return new ResponseEntity(Http.OK);
}
}
#Service
public class MyService {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyOtherService myOtherService;
public void myMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
myOtherService.myOtherMethod(text);
}
}
#Service
public class MyOtherService {
#Autowired
private TenantStore tenantStore;
#Autowired
private Map<String, MyComponent> myComponents;
public void myOtherMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
MyComponent useThisComponent = myComponents.get("componentName");
useThisComponent.myComponentMethod(text);
}
}
#Component("componentName")
public class MyComponent {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyCacheService myCacheService;
public void myComponentMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
entityAliasCacheService.myCacheMethod(String text);
}
}
#Service
public class MyCacheService {
#Autowired
private TenantStore tenantStore;
public void myCacheMethod(String text) {
System.out.println(tenantStore.getName()); //DOES NOT WORK - tenantStore object is not null but the name property is
}
}
From what I can guess, for some reason the TenantStore in MyCacheService is being populated in a different thread, though I've no idea why.
I noticed similar behaviour. I fixed the issue by adding a bean dependancy
#Service
#DependsOn("proxiedThreadLocalTargetSource") // asks Spring to first load proxy bean
public class MyCacheService {
where proxiedThreadLocalTargetSource bean is defined like in the OP's example -
#Primary
#Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
So, by adding the dependancy, Spring knows that it should load MyCacheService bean after the proxiedThreadLocalTargetSource. Without this dependancy, I noticed that TenantStore got injected instead of the proxy bean.
Getting instance of TenantStore from org.springframework.context.ApplicationContext
First implement ApplicationContextAware like as below
#Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext context() {
return context;
}
}
And your MyCacheService Will be like this:
public class MyCacheService {
public void myCacheMethod(String text) {
TenantStore tenantStore = ApplicationContextUtil.context().getBean(TenantStore.class);
System.out.println(tenantStore.getName());
}
}
I have logic to intercept the RestTemplate and I am adding/registering that RestTemplate in a configuration file (SecurityConfiguration.java)
but I want to add that interceptor from another configuration file by getting RestTemplate object which is already registered:
public class TranslogRestTemplateCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean isRestTemplate = false;
try {
if (context.getBeanFactory() != null) {
isRestTemplate = (context.getBeanFactory().getBean(RestTemplate.class) != null);
}
} catch (BeansException e) {
return false;
}
return isRestTemplate;
}
}
Configuration class:
#Configuration
public class RestTemplateConfig {
private final MyInterceptor myInterceptor;
#Value("${com.pqr.you.rest.enabled:true}")
private boolean transEnabled;
#Autowired
public RestTemplateConfig(MyInterceptor myInterceptor) {
this.myInterceptor = myInterceptor;
}
#Autowired
private ApplicationContext appContext;
// The logic added below is not working for me
#Bean
#Conditional(TranslogRestTemplateCondition.class)
public RestTemplate addInterceptor(ApplicationContext appContext) {
RestTemplate restTemplate = appContext.getBean(RestTemplate.class);
if (transEnabled) {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(myInterceptor);
restTemplate.setInterceptors(interceptors);
}
return restTemplate;
}
}
Actual logic for RestTemplate, which is going to return with required interceptors, and some other values (at the time of returning this restTemplate, my interceptor also need to be add here, without over riding existing values)
OR
by taking the below restTempalte object and add MyInterceptor to restTemplate.
#Configuration
public class SecurityConfiguration {
#Bean
public AbcInterceptor abcRequestInterceptor(XyzService xyzService) {
return new AbcInterceptor("abc-app", null, xyzService);
}
// I dont want to create bean here
/*#Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}*/
#Bean
public RestTemplate restTemplate(AbcRequestInterceptor abcRequestInterceptor) {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(abcRequestInterceptor);
//interceptors.add(myInterceptor); // I dont want to add this interceptor here
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
I think what you need in this case is a BeanPostProcessor not a Condition class. It will allow you to modify the bean if it exists. you can create one like below instead of the RestTemplateConfiguration class and the Condition:
#Configuration
public class RestTemplatePostProcessor implements BeanPostProcessor {
#Value("${com.pqr.you.rest.enabled:true}")
private boolean transEnabled;
#Bean
MyInterceptor myInterceptor() {
return new MyInterceptor();
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RestTemplate) { // or you can check by beanName if you like
final RestTemplate restTemplate = (RestTemplate) bean;
if (transEnabled) {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(myInterceptor());
restTemplate.setInterceptors(interceptors);
}
return restTemplate;
}
return bean;
}
}
Note that in your code you are trying to create another RestTemplate instance (named "addInterceptor"), which seems to be undesired.
I am having trouble caching internal methods within my DAO layer while in Proxy mode.
I am aware that while in Proxy mode, only external method calls coming in through the proxy are intercepted. However,I want to avoid having to switch to AspectJ mode and was wondering if any other work arounds existed.
I am displaying my code below and am wondering what changes, if any, I can add to make this process work.
--Note I am using swagger to document my code
--Also note my code has been watered down....for obvious reasons
//Controller
#RestController
#Api(produces = "application/json", protocols = "https", tags = "Securities", description = "Securities information")
public class SecuritiesInfoController extends Controller {
private SecuritiesInfoManager _securitiesInfoManager = new SecuritiesInfoManager();
#RequestMapping(value = "/security", method = RequestMethod.GET)
public List<SecuritiesInfo> getAll(){
return _securitiesInfoManager.getAll();
}
}
//Service
public class SecuritiesInfoManager extends Manager {
private SecuritiesInfoDAO _securitiesDAO = new SecuritiesInfoDAO();
public List<SecuritiesInfo> getAll() {
return _securitiesDAO.getAll();
}
}
//DAO
public class SecuritiesInfoDAO extends DAO {
private static String securitiesTable = "Securities";
#SecuritiesInfoDAOInterface
public List<SecuritiesInfo> getAll() {
//Magic
}
}
//Interface
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#Cacheable(cacheNames = "SecuritiesInfo",cacheManager="cacheManager",
keyGenerator="keyGenerator" )
public #interface SecuritiesInfoDAOInterface {
}
//CacheConfig
#Configuration
//#EnableCaching(mode = AdviceMode.PROXY)
#EnableCaching(proxyTargetClass = true)
//#EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
#Bean
public SecuritiesInfoDAO myService() {
// configure and return a class having #Cacheable methods
return new SecuritiesInfoDAO();
}
#Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
// Defaults
redisConnectionFactory.setHostName("Nope");
redisConnectionFactory.setPort(LoL);
System.out.println("IN CONNTECTION");
redisConnectionFactory.setPassword("Please help me :)");
return redisConnectionFactory;
}
#Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
System.out.println("cf: "+cf.toString());
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
}
/*
#Primary
#Bean
public RedisTemplate<String,ExpiringSession> redisTemplate2(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, ExpiringSession> template = new RedisTemplate<String, ExpiringSession>();
template.setHashValueSerializer(new LdapFailAwareRedisObjectSerializer());
template.setConnectionFactory(connectionFactory);
return template;
}
*/
#Bean
public CacheManager cacheManager(RedisTemplate<String, String> redisTemplate) {
System.out.println("IN CACHE MANAGER");
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
// cacheManager.setDefaultExpiration(300);
return cacheManager;
}
#Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
#Override
public Object generate(Object o, Method method, Object... objects) {
// This will generate a unique key of the class name, the method name,
// and all method parameters appended.
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
System.out.println(sb.toString());
return sb.toString();
}
};
}
So I figured out the answer. It turns out I wasn't implementing/instantiating the interface correctly.
First I have to #Autowire my manager class in my controller. Then #autowire my interface class in my manager.
For a more detailed solution, I am placing my revised code below.
//Controller
#RestController
#Api(produces = "application/json", protocols = "https", tags = "Securities", description = "Securities information")
public class SecuritiesInfoController extends Controller {
#Autowired
private SecuritiesInfoManager _securitiesInfoManager = new SecuritiesInfoManager();
#RequestMapping(value = "/security", method = RequestMethod.GET)
public List<SecuritiesInfo> getAll(){
return _securitiesInfoManager.getAll();
}
}
//Service
public class SecuritiesInfoManager extends Manager {
#Autowired
public void setSecuritiesInfoDAOInterface(SecuritiesInfoDAOInterface _securitiesInfoDAOInterface) {
this._securitiesInfoDAOInterface = _securitiesInfoDAOInterface;
}
public List<SecuritiesInfo> getAll() {
return _securitiesInfoDAOInterface.getAll();
}
}
//DAO
public class SecuritiesInfoDAO extends DAO implements SecuritiesInfoDAOInterface {
private static String securitiesTable = "Securities";
#Override
public List<SecuritiesInfo> getAll() {
//Magic
}
}
//Interface
public interface SecuritiesInfoDAOInterface {
#Cacheable(cacheNames = "SecuritiesInfo",cacheManager="cacheManager", keyGenerator="keyGenerator" )
List<SecuritiesInfo> getAll();
}
}
//CacheConfig
#Configuration
#EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
#Bean
public SecuritiesInfoManager myService() {
// configure and return a class having #Cacheable methods
return new SecuritiesInfoManager();
}
//rest same as before
}
//WebConfig
#Configuration
#ComponentScan(basePackages = {"package name"})
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
AntPathMatcher matcher = new AntPathMatcher();
matcher.setCaseSensitive(false);
configurer.setPathMatcher(matcher);
}
}
I am trying to implement Method Security using #PreAuthorize.
Spring Version: 4.2.3.Release
Spring Security: 4.0.3.Release
I have implemented a CustomPermissionEvaluator. I have noticed that it seems to be working fine except for 1 service where the hasPmerission is not called.
I know this because I get the a logging message from hasPermission / or in the erroneous case do not get the log:
public boolean hasPermission(Authentication authentication, Object o, Object o1) {
logger.info("Call to hasPermission with "+o+" and "+o1);
...
}
My Spring configuration is as follows:
#Configuration
#ComponentScan
public class RootConfiguration {
}
MVC Config
#EnableWebMvc
#Configuration
#ComponentScan({"OntoRais.*"})
#PropertySource("classpath:application.properties")
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name="multipartResolver")
public CommonsMultipartResolver commonsMultipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setDefaultEncoding("utf-8");
commonsMultipartResolver.setMaxUploadSize(50000000);
return commonsMultipartResolver;
}
}
Method Security Config:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
private CustomPermissionEvaluator permissionEvaluator;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler
= new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
public CustomPermissionEvaluator getPermissionEvaluator() {
return permissionEvaluator;
}
public void setPermissionEvaluator(CustomPermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}
}
Initializer:
#Configuration
#EnableSpringConfigured
public class MessageWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(org.springframework.web.context.request.RequestContextListener.class);
super.onStartup(servletContext);
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { MvcConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new HiddenHttpMethodFilter(),
new OpenEntityManagerInViewFilter(),
new DelegatingFilterProxy("springSecurityFilterChain")
};
}
}
Security Config:
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
OntoRAISUserDetailsService ontoRAISUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
formLogin().
and().
logout().
and().
authorizeRequests().
antMatchers("/login").permitAll().
anyRequest().authenticated().
and().csrf().disable();
}
#Autowired
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(ontoRAISUserDetailsService);
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(ontoRAISUserDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public OntoRAISUserDetailsService getOntoRAISUserDetailsService() {
return ontoRAISUserDetailsService;
}
public void setOntoRAISUserDetailsService(OntoRAISUserDetailsService ontoRAISUserDetailsService) {
this.ontoRAISUserDetailsService = ontoRAISUserDetailsService;
}
The Service in question:
#Service
public class StakeholderService {
#Autowired
private OntopManager om;
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
public OntopManager getOm() {
return om;
}
public void setOm(OntopManager om) {
this.om = om;
}
#PreAuthorize("hasPermission(#stakeholderType, 'Create_StakeholderType')")
public void createStakeholderType(StakeholderType stakeholderType) {
try {
logger.info("Create stakeholder type in service layer");
List<OBDADataSource> sources = om.getObdaModel().getSources();
OBDAMappingAxiom mapping = om.getObdaModel().getMapping(new URI("genertatedURI"), MappingList.StakheholderType());
HashMap<String, String> values = new HashMap<>();
values.put("stakeholderName", stakeholderType.getLabel());
String query = ClassSQLHelper.generateSQLCreateSatement(mapping.getSourceQuery(), values);
SQLHelper.executeSQL(query, sources.get(0));
} catch (URISyntaxException e) {
logger.error(e.getMessage());
}
}
And the controller from which i call the service layer:
#Api(description = "Operations related to Stakeholders")
#RestController
public class StakeholderController {
#Autowired
private OntopManager om;
#Autowired
StakeholderService stakeholderService;
#Autowired
ProjectService projectService;
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
...
/**
* Add a new Stakeholder Type
*
* #param stakeholdertype The new Stakeholder to be added.
* #return
*/
#ApiOperation(value = "Add new stakeholder type",
notes = "",
response = ResponseResource.class,
responseContainer = "Object")
#JsonView(Views.Details.class)
#RequestMapping(value = "/api/stakeholder/types", method = RequestMethod.POST)
public ResponseEntity<List<StakeholderType>> addStakeholderType(#RequestBody StakeholderType stakeholdertype) {
logger.info("Add Stakeholder type in controller");
getStakeholderService().createStakeholderType(stakeholdertype);
return getStakeholderTypes();
}
When calling api/stakeholder/types" with method = POST
This is my debug output:
Add Stakeholder type in controller
Create stakeholder type in service layer
INSERT INTO prefix_http_www_ontorais_de_stakeholdertype(id,stakeholderName) VALUES(DEFAULT,'TESTEWRTERETE');
As you can see the log from hasPermission is not present -> not called.
I can see that the method is called from my other method sercurity annotations in other service objects.
A similar Service which correctly invokes hasPermission as expected just for comparison:
#Service
public class OrganisationService {
private static final Logger logger = LoggerFactory.getLogger("OntoRais");
#Autowired
private OntopManager om;
#Autowired
private ProjectService projectService;
...
#PreAuthorize("hasAuthority('Add_Organisation')")
public void addOrganisation(Organisation organisation) {
List<OBDADataSource> sources = om.getObdaModel().getSources();
OBDAMappingAxiom mapping = null;
try {
mapping = om.getObdaModel().getMapping(new URI("genertatedURI"), MappingList.OrganisationMapping());
} catch (URISyntaxException e) {
e.printStackTrace();
}
HashMap<String, String> valueMap = new HashMap<>();
valueMap.put("organisationName", organisation.getName());
valueMap.put("organisationDescription", organisation.getDescription());
String query = ClassSQLHelper.generateSQLCreateSatement(mapping.getSourceQuery(), valueMap);
SQLHelper.executeSQL(query, sources.get(0));
}
Any hints on what I am doing wrong/missing/am blind for are very welcome thanks.
Benedict
Ok I found the problem, and a solution.
The Problem was that my CustomPermissionEvaluator depended on a method within the StakeholderService. Even though that method was not secured this resulted in the Spring not being able to proxy the object, therefore preventing any security checks.
Even though it is a bad idea to use a service layer object in the PermissionEvaluator, perhaps someone could elaborate on the exact implications, as i am definately not an expert in spring sercurity
I am new at Spring and trying to xml based config to annotation basic. I read this tutorial amd coded. It works perfect with xml based config.
MVC Spring CRUD Tutorial
And now I converted all xml based config to annotation but I have a problem. I ried almost everything what I read but I didn't solve this problem.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.ulotrix.spring.controller.PersonController.setPersonService(com.ulotrix.spring.service.PersonService); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.ulotrix.spring.service.PersonService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
AppConfig.java
#EnableWebMvc
#Configuration
#ComponentScan({ "com.ulotrix.spring.controller" })
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
builder.scanPackages("com.ulotrix.spring.model");
builder.addProperties(getHibernationProperties());
return builder.buildSessionFactory();
}
private Properties getHibernationProperties() {
Properties prop = new Properties();
prop.put("hibernate.format_sql", "true");
prop.put("hibernate.show_sql", "true");
prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return prop;
}
#Bean(name = "dataSource")
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/deneme_db2");
ds.setUsername("root");
ds.setPassword("xxx");
return ds;
}
#Bean
public HibernateTransactionManager txManger() {
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
SpringMVCInitializer.java
public class SpringMvcInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(AppConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
PersonController.java
#Controller
public class PersonController {
private PersonService personService;
#Autowired(required = true)
#Qualifier(value = "personService")
public void setPersonService(PersonService ps) {
this.personService = ps;
}
#RequestMapping(value = "/persons", method = RequestMethod.GET)
public String listPersons(Model model) {
model.addAttribute("person", new Person());
model.addAttribute("listPersons", this.personService.listPersons());
return "person";
}
//For add and update person both
#RequestMapping(value = "/person/add", method = RequestMethod.POST)
public String addPerson(#ModelAttribute("person") Person p) {
if(p.getId() == 0) {
this.personService.addPerson(p);
}else {
this.personService.updatePerson(p);
}
return "redirect:/persons";
}
#RequestMapping(value = "/remove/{id}")
public String removePerson(#PathVariable("id") int id) {
this.personService.removePerson(id);
return "redirect:/persons";
}
#RequestMapping(value = "/edit/{id}")
public String editPerson(#PathVariable("id") int id, Model model) {
model.addAttribute("person", this.personService.getPersonById(id));
model.addAttribute("listPersons", this.personService.listPersons());
return "person";
}
}
The PersonService is defined in package com.ulotrix.spring.services, so change #ComponentScan({ "com.ulotrix.spring.controller" }) to #ComponentScan({ "com.ulotrix.spring" }) thanks to which spring can discover all the beans defined inside package spring.