I created a CustomWebSecurityExpressionHandler to check users on a db table by searching for a function id. I want to change my roles on every function with only some db update and a restart of the context, without recompile and editing a mountain of XML.
I want to use SpringSecurityExpression inside a webflow! Like I can do in any other parts of Spring...
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<secured attributes="isFUUU('key')" />
<view-state id="main" view="dashboard/main.html" >
</view-state>
</flow>
How can I make isFUU("key") working? it's a custom CustomAccessDecisionManager needed?
I found a workaround
I had to debug 20 classes of spring security and webflow to discover that in SecurityFlowExecutionListener even if you set spring security to work with expression, the listener will be role-based only.
I found that for parsing expression a would need a specific type config attribute, WebExpressionConfigAttribute to be precise.
But it's not a public class!!! https://jira.spring.io/browse/SEC-1727 .
So as suggested in this OLD Jira, I needed to creat my CustomSecurityFlowExecutionListener in the same package (org.springframework.security.web.access.expression)
Here the example
CustomSecurityFlowExecutionListener:
package org.springframework.security.web.access.expression; //First part of the trick!
import foo.bar.example.services.security.CustomAccessDecisionManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.expression.ExpressionParser;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.webflow.security.SecurityFlowExecutionListener;
import org.springframework.webflow.security.SecurityRule;
/**
* Force Spring WebFlow Security listener to use expression!
*
* #author roberto.gabrieli
*/
public class CustomSecurityFlowExecutionListener<T > extends SecurityFlowExecutionListener
{
/**
* Convert SecurityRule into a form understood by Spring Security Force the usage of WebExpressionConfigAttribute!
*
* #param rule
* the rule to convert
* #return list of ConfigAttributes for Spring Security
*/
#Override
#SuppressWarnings("deprecation")
protected Collection<ConfigAttribute> getConfigAttributes(SecurityRule rule)
{
// Get Access Decision Manager to find if has my expression handler
AccessDecisionManager adm = getAccessDecisionManager();
ExpressionParser ep = null;
// Check if is my CustomAccessDecisionManager so I can use my expressions
if ( adm instanceof CustomAccessDecisionManager )
{
ep = ((CustomAccessDecisionManager) adm).getWebSecurityExpressionHandler().getExpressionParser();
}
List<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
for ( String attribute : rule.getAttributes() )
{
if ( ep != null )
// this will end the trick with fireworks!
configAttributes.add(new WebExpressionConfigAttribute(ep.parseExpression(attribute)));
else
configAttributes.add(new SecurityConfig(attribute));
}
return configAttributes;
}
}
WebFlow-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
...
<bean id="securityFlowExecutionListener"
class="org.springframework.security.web.access.expression.MamSecurityFlowExecutionListener">
<property name="accessDecisionManager" ref="customAccessDecisionManager"/>
</bean>
...
</beans>
I've found another solution how to use Spring Expression Language in WebFlows. It's from the book "Pro Spring Security". In short, they define a custom AccessDecisionManger with a custom AccessDecisionVoter (implements AccessDesisionVoter<org.springframework.webflow.engine.State) and a custom SecurityExpressionRoot. So no need for an own listener as in your solution. These custom classes support expressions at the flow state level. The full example you can find on github with this link.
Related
I am very new to Spring Framework. I am using NetBeans for IDE. I followed couple of tutorials to learn it by myself. However, I am stuck in the middle and cannot proceed further. Let me breakdown my project here:
My project folder structure looks like this:
There are two classes; the major one MainApp.java contains following code:
package com.myprojects.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context;
context = new ClassPathXmlApplicationContext("classpath*:beans.xml");
FirstPage obj;
obj = (FirstPage) context.getBean("firstPage");
obj.getMessage();
}
}
Second class file FirstPage.java looks like this:
package com.myprojects.spring;
public class FirstPage {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
The beans.xml file looks like below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.0.RELEASE.xsd
">
<bean id = "firstPage" class = "com.myprojects.spring.FirstPage">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
Now, the error I am getting is like below:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'firstPage' is defined
I know I have been doing some silly mistake here.
Thank you in Advance !
Almost similar problem have been discussed before. I think your program is unable to locate beans.xml.
Try doing this:
context = new ClassPathXmlApplicationContext("META-INF/beans.xml");
EDIT:
This new error XmlBeanDefinitionStoreException means that your schema is not valid. Try changing your schema as described in one of these answers:
https://stackoverflow.com/a/21525719/2815219
https://stackoverflow.com/a/25782515/2815219
Spring configuration XML schema: with or without version?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id = "firstPage" class = "com.myprojects.spring.FirstPage">
<property name = "message" value = "Hello World!"/>
</bean>
</beans>
According to the directory structure you posted it is very likely that src/main/resources is on your classpath. If you like to reference your spring context file beans.xml you have to specify it relative to the folders on your classpath. Hence you should try:
context = new ClassPathXmlApplicationContext("classpath:/META-INF/beans.xml");
Besides: the notation classpath*:beans.xml means you want to read in all context files having a name of beans.xml.
Put beans.xml to outside Meta-inf ,
or use new ClassPathXmlApplicationContext("META-INF/beans.xml");
And http://www.springframework.org/schema/beans/spring-beans-4.0.0.RELEASE.xsd
should change to
http://www.springframework.org/schema/beans/spring-beans-4.0.0.xsd , as spring's xsd filenames don't contain "RELEASE".
The xsd files are in org.springframework.beans.factory.xml package in spring-beans.jar, see if the xsd file is in that package.
Doing following two things solved my issue.
1) There was an incorrect beans.xml path. I changed that to context = new ClassPathXmlApplicationContext("META-INF/beans.xml");.
2) Also, there was an invalid xsi:schemaLocation attribute value. I changed that attribute's value to http://www.springframework.org/schema/beans/spring-beans-3.0.xsd.
Thank you all for your help.
I'm running into an issue trying to use Spring caching with ehcache in my application. For reasons that I can't elaborate on, my application uses a graph of BeanFactories instead of ApplicationContexts. This approach has worked well as long as we manually register our BeanPostProcessors, as is called out in the Spring documentation.
We are now adding caching to the app. When we used the simplest annotation configuration, it works.
// This works
package com.x.y.z;
public class RoleManager {
private String user;
public RoleManager( String user ) {
this.user = user;
}
public String getName() {
return user;
}
#Cacheable("user")
public boolean isAllowed(String permissionId, Map<String,?> params)
{
... lengthy and expensive operation to determine if user is permitted to do something
}
}
We configure this to using spring xml for this bean factory:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
<bean id="roleManager" class="com.x.y.z.RoleManager" scope="prototype"/>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean>
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="file:${conf.dir}/ehcache.xml"/>
<property name="shared" value="true"/>
</bean>
</beans>
... unrelated business beans elided ...
We are using Spring 4.1.9 and ehcache 2.10.2
The above code works quite well. Our ehcache instance for "user" begins to fill as we get cache misses, and returns cached values for hits.
Once this was running correctly, we found that it isn't possible to evict all the entries for a particular user because the cache key is a concatenation of the permissionid and the Map::toString result. We decided to create a cache per user so we would have more control over eviction. To use Spring, we need to use a CacheResolver to accomplish this.
package com.x.y.z;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.AbstractCacheResolver;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import java.util.Collection;
import java.util.Collections;
public class MyCacheResolver extends AbstractCacheResolver {
public MyCacheResolver() {
}
public MyCacheResolver(CacheManager cacheManager) {
super(cacheManager);
}
#Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> cacheOperationInvocationContext) {
if(cacheOperationInvocationContext.getTarget() instanceof RoleManager) {
return Collections.singleton(((RoleManager) cacheOperationInvocationContext.getTarget()).getName());
}
return Collections.singleton("user");
}
}
We wire this up by adding a new bean definition
<bean id="myCacheResolver" class="com.x.y.z.MyCacheResolver">
<constructor-arg index="0" ref="cacheManager"/>
</bean>
And change the annotation in RoleManager to
#Cacheable(cacheResolver="myCacheResolver")
Once we do this, however, we get the following exception when the isAllowed method is invoked:
java.lang.NullPointerException
at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:57)
at org.springframework.cache.interceptor.CacheAspectSupport.getBean(CacheAspectSupport.java:282)
at org.springframework.cache.interceptor.CacheAspectSupport.getCacheOperationMetadata(CacheAspectSupport.java:254)
at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:226)
at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:500)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy61.isAllowed(Unknown Source)
at com.x.y.z.RoleManager.isAllowed(CompositeRoleManager.java:61)
When I look at the CacheAspectSupport class from the stack trace, I see that it has a member, applicationContext, which is null.
protected <T> T getBean(String beanName, Class<T> expectedType) {
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName);
}
This seems like a bug in Spring to me since we do not use ApplicationContexts, and yet caching works until we need to use a CacheResolver. I've looked over the documentation and I see no mention that one must use ApplicationContexts in order to use the Spring caching abstraction.
I guess my question is, has anyone experienced this problem, and if so, what did you do to resolve it? We absolutely cannot use ApplicationContexts in our application, and I'd rather not throw out a perfectly usable abstraction and code directly to the ehcache (or JSR-107) APIs.
Thanks in advance!
Spring 4.3 has fixed the problem by adding a setBeanFactory() method and using the BeanFactory thus set to call the CacheResolvers. Unfortunately I am unable to update our Spring library code to 4.3 at this time, but it will work when we are able to upgrade in the future.
I want to weave the following custom aspect using Spring 4.X.
(I use Scala, but is exactly the same as in Java).
I'm based on this existing code, acting as sample from the Vaughn Vernon's IDDD book:
#Aspect
#Component
class EventProcessor #Autowired()(private val eventRepository: EventRepository) {
#Before("execution(* com.mymainpackage.*.application.commands.*.*(..)")
def listen() {
DomainEventPublisher.instance().subscribe(new DomainEventSubscriber[Event] {
def handleEvent(domainEvent: Event) {
eventRepository.save(domainEvent)
}
def subscribedToEventType = {
classOf[Event]
}
})
}
}
My application-context.xml:
.........
<context:load-time-weaver/>
<context:annotation-config/>
<context:component-scan
base-package="........" />
The typical classes/services I want to weave with aspect contains this kind of method:
def handle(event:Event)
and its corresponding interfaces and implementation may be found in this explicit package for instance:
com.mymainpackage.myboundedcontext1.application.commands.anestedpackage
I well checked that the base-package contains the class's package of the custom aspect.
Is there a necessity to declare a #PointCut additionally to #Before?
The sample doesn't do it...
Of course, I executed my code with the following jar allowing "weaving" at Runtime so:
-javaagent:/cache/org.springframework/spring-instrument/jars/spring-instrument-4.0.0.RELEASE.jar
Did I miss something "obvious"?
Any idea how to fix this?
As #M.Deinum mentionned, I forgot a last ) regarding the matcher of my pointcut.
Furthermore I forgot to specify the aspectj-autoproxy, in order to take into account my aspect class.
With that, would no need to declare an aop.xml if I expect all aspects classes to have their created respective proxies.
So I specified it by adding spring-aop xsd to my application-context.xml like this ("------->" to make a legend, although not valid :) in this xml):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" ------->That is added
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" ---------> That is added >
.....
<aop:aspectj-autoproxy/>
.....
Now the whole works.
I am new at Spring and am wondering if one can load an application just by annotating the class whose variables must be injected (instead of using ApplicationContext ctx = new ApplicationContext("myAppContext")).
Let me give the following example:
I have this class TestSpring.java in which a string should be autowired
package mytest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
//Is it possible to put an annotation here that loads the application context "TestSpringContext.xm"??
public class TestSpring {
#Autowired
#Qualifier("myStringBean")
private String myString;
/**
* Should show the value of the injected string
*/
public void showString() {
System.out.println(myString);
}
}
The spring bean configuration file (TestSpringContext.xml) looks like this
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
>
<context:annotation-config />
<bean id="myStringBean" class="java.lang.String">
<constructor-arg value="I am an injected String."/>
</bean>
</beans>
Now I would like to display the value of the autowired string myString (declared in TestSpring.java) using following code in RunTestSpring.java:
package mytest;
public class RunTestSpring {
public static void main(String[] args) {
TestSpring testInstance = new TestSpring();
testInstance.showString();
}
}
Now my question, is it possible to run "RunTestSpring.java" successfully while loading the application context by just annotating RunTestSpring.java. If yes, with which annotation?
#Configurable is probably what you are looking for, it will ensure that objects which are not instantiated by Spring can have their dependencies autowired by Spring. However the catch is that it requires AspectJ compile time/load time weaving for it to work(not Spring AOP).
Here is one reference:
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-atconfigurable
I would suggest writing a JUnit class that would use spring injection for environment initialization. Something like this -
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="/spring/spring-wireup.xml", inheritLocations = true)
public class MyTestCase extends TestCase {
// your test methods ...
}
Can you enlighten me on this problem I encountered while experimenting with Spring.
I have 2 context here. let's name them springA.xml and springB.xml
springA.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="springB.xml" />
<bean name="name2" class="java.lang.String"/>
</beans>
springB.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="name2,name3" class="java.lang.String"/>
</beans>
springC.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="name3,name2" class="java.lang.String"/>
</beans>
And this is my Java File.
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("springA.xml"));
Object obj1 = factory.getBean("name2");
Object obj2 = factory.getBean("name3");
System.out.println(obj1.getClass().getName() + " " + obj2.getClass().getName());
}
And the result, I get a "java.lang.String java.lang.String". If I change the position of
the name "name2,name3" to "name3,name2" (springC.xml), I get a "java.lang.Object java.lang.Object".
I am just confused as to why the result is like that. I was expecting that the function will return java.lang.String for name2 and java.lang.Object for name3 (since name2 is already used in the springA.xml, I am assuming this name will not be used and instead, will use name3 for springB.xml)
Thanks!
PS:
Spring 2.5
Eclipse 3.5
From Spring's documentation:
Every bean has one or more ids (also
called identifiers, or names; these
terms refer to the same thing). These
ids must be unique within the
BeanFactory or ApplicationContext the
bean is hosted in.
According to this, your combined application context is invalid as it contains two different beans which have the same ID - your bean named "name2" from ContextA.xml and your bean named "name2", aliased "name3" in ContextC.xml. I would expect Spring to issue at least a warning about this.
To answer your question: You shouldn't expect any sane results from this kind of setup. Bean names have to be unique and if they aren't the results are undefined. And by "undefined" I mean "unlikely to be helpful" :)
Hope this helps.
I believe you are seeing different results because Spring is loading the beans in the context in different orders in each scenario. Spring makes no guarantee as to which order it will load it's beans other than the fact that any beans used as "ref"'s in other bean definitions will be loaded before the beans that depend on them.
The correct solution to your problem is DO NOT use duplicate bean identifiers and then you won't have to guess as to which bean you will get when you look one up.
I've ran your code on Spring 2.5.6 and 3.0.0.M1 and both version produce the same result.
java.lang.String java.lang.String
My advice is if you want two strings and you are getting strange results with 2.5, then bump to 2.5.6 or 3.0.0.M1.