I have a Spring mvc (3.1.1) app, and I want to define conditions beyond what's available in RequestMapping. I have a couple of things I want to use it for.
First, it would be nice if I could show a different home page for different user types:
#Controller
public class HomepageController {
#RequestMapping(value = "/")
#CustomCondition(roles = Guest.class)
public String guestHome() { /*...*/ }
#RequestMapping(value = "/")
#CustomCondition(roles = Admin.class)
public String adminHome() { /*...*/ }
}
Second, I want the app to function both as a web site and as a REST service (e.g. for mobile apps), so I'd want to let the website access both html and json actions, and let the service (different subdomain) only access json actions (some kind of #CustomCondition(web = true) which only matches website urls)
Can this work for any of the two uses I'm planning?
I found very little documentation about custom conditions, but I did find one example that implements custom conditions which might be what I want, but it uses a #Configuration class instead of the XML configuration which I'm using and I don't want to move my entire spring xml definitions to a #Configuration class.
Can I define a customMethodCondition for RequestMappingHandlerMapping in the XML?
I tried subclassing RequestMappingHandlerMapping and override getCustomMethodCondition, to return my custom RequestCondition, but it didn't work - getMatchingCondition() in my condition didn't fire.
Any help would be greatly appreciated!
UPDATE
I read a little more, and it looks like RequestMappingHandlerMapping is a new class (since ver 3.1).
What happens in my app is that the #Configuration that tries to override and thereby redefine the requestMappingHandlerMapping bean actually works, but the url mappings (#RequestMapping methods in #Controllers) seem to get processed twice, once by the subclass ExtendedRequestMappingHandlerMapping and once by the original RequestMappingHandlerMapping --first with a custom condition, and then again without it.
Bottom line is my custom conditions are simply ignored.
This is supposed to be an advanced pattern, but IMO it should be quite common...
Comments anyone?
Spring MVC already provides a mechanism for distinguishing between json and html, the RequestMapping annotation takes a consumes attribute which looks at the content type of the request...
// REST version, Content-type is "application/json"
#RequestMapping(value = "/", consumes = "application/json")
public void myRestService() {
...
// HTML version, Content-type is not "application/json"
#RequestMapping(value = "/", consumes = "!application/json")
public void myHtmlService() {
...
Another way to use the same url but have distinct methods is with the param or headers attribute...
// the url is /?role=guest
#RequestMapping(value = "/", param = "role=guest")
public void guestService() {
// the url is / with header role=admin
#RequestMapping(value = "/", headers = "role=admin")
public void adminService() {
I would think you would want distinct urls for security. Typically, with something like Spring Security, you would put all of the admin functionality under /admin and let the framework manage it all...
<http auto-config="true">
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
...
Would this be sufficient for your use case(s)?
If you have extended RequestMappingHandlerMapping(say ExtendedRequestMappingHandlerMapping) you have to register this new mapping a little differently in application context xml.
You cannot use <mvc:annotation-driven/> to configure the Spring MVC as that defines it's own handlerMapping internally, you can instead do something along these lines(or follow the approach in the link with #Configuration that you have provided):
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="handlerMapping" class="..ExtendedRequestMappingHandlerMapping">
</bean>
This should ensure that your mapping takes effect and will ensure that the appropriate handler method is found by the handlerAdapter component.
Related
I'm trying to figure out a way to store certain properties in an encrypted form while they are at rest, and have them transparently decrypted before the property is injected into any beans, whether they are using #Value or they are defined in xml by setting properties. We're not using spring-boot yet. The property file would look like this:
db_password={aes}some_encrypted_value
I can see in the logs where the PropertySourcesPropertyResolver gets the value for my property. It should be pretty simple to create my own implementation of the PropertySourcesPropertyResolver.getProperty method that looks for the "{aes}" prefix, decrypting it if necessary, but I can't figure out how to use my subclass in my application.
Does anyone have any idea how I can get spring to use my implementation instead of Springs?
I initially tried to use the PropertySourcesPlaceholderConfigurer that worked for me in Spring 3, but I couldn't figure out how to make it work in spring 4. I also couldn't get the newer PropertySourcesPlaceholderConfigurer to work either.
Can anyone point me in the right direction?
We did it as follows with Spring 4.0.3 RELEASE
public class MyPropertyConfigurer extends PropertyPlaceHolderConfigurer{
protected void convertProperties(Properties props){
Enumeration<?> propertyNames = props.propertyNames();
while(propertyNames.hasMoreElements()){
String propName = (String)propertyNames.nextElement();
String propValue = (String)props.getProperty(propName);
if(propName.indexOf("db_password") != -1){
setPropertyValue(props,propName,propValue);
}
}
}
private void setPropertyValue(Properties props,String propName,String propValue){
String decryptedValue = PasswordUtility.decrypt(propValue);
props.setProperty(propName,decryptedValue);
}
}
In xml, it was configured as below
<bean id="dbPropertyPlaceholder" class="mypackage.MyPropertyConfigurer">
<property name="locations">
<list>
<value>file:myProp.properties</value>
<list>
</property>
</bean>
I am developing an app in spring 4.1 . I know that Controllers / any other bean in spring are not thread safe . ie: Singleton. That mean same instance of Controller will be used to process multiple concurrent requests. Till here I am clear . I want to confirm that do I need to explicitly set #Scope("prototype") or request in the Controller class ? I read on StackOverflow previous post that even if scope is not set as request/prototype , Spring container will be able to process each request individually based on #RequestParams passed or #ModelAttribute associated with method arguements .
So i want to confirm is my below code is safe to handle multiple request concurrently ?
#Controller
public class LogonController {
/** Logger for this class and subclasses */
protected final Log logger = LogFactory.getLog(getClass());
#Autowired
SimpleProductManager productManager;
#Autowired
LoginValidator validator;
#RequestMapping( "logon")
public String renderForm(#ModelAttribute("employee") Logon employeeVO)
{
return "logon";
}
#RequestMapping(value="Welcome", method = RequestMethod.POST)
public ModelAndView submitForm(#ModelAttribute("employee") Logon employeeVO,
BindingResult result)
{
//Check validation errors
validator.validate(employeeVO, result);
if (result.hasErrors()) {
return new ModelAndView("logon");
}
if(!productManager.chkUserValidation(employeeVO.getUsername(), employeeVO.getPassword())){
return new ModelAndView("logon");
}
ModelAndView model = new ModelAndView("Welcome");
return model ;
}
}
Also i have another doubt.
since i am using SimpleProductManager productManager; Do i need to specify scope="prototype in its bean declaration in app-servlet.xml ?
Below is my configuration.xml
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
</bean>
<bean id="productManager" class="com.BlueClouds.service.SimpleProductManager" >
<property name="productDao" ref="productDao"/>
</bean>
<bean id="productDao" class="com.BlueClouds.dao.HbmProductDao">
<property name="sessionFactory"><ref bean="mySessionFactory"/></property>
</bean>
<bean id="loginValidator" class="com.BlueClouds.service.LoginValidator" >
</bean>
Being singleton single instance of validator is being shared among all request , for that do i need to add scope=request in bean configuration xml or do i need to surround validate() in synchronized block ? Please advise.
Thanks much .
You can tell your code is thread safe or not by answering following questions
Are there threads might modify a static field, which is not thread safe(ex: arrayList), in the same time?
Are there threads might modify a field of an instance, which is not thread safe, in the same time?
If any answer of the above is yes, then your code is not thread safe.
Since your code doesn't change any field, so it should be thread safe.
The general idea about thread safe is that if there are threads might change/access the same memory section in the same time, then it's not thread safe, which means "synchronized" is needed.
You'd better learn more about stack memory, heap memory and global memory in JAVA. So that you can understand if your code changes the same memory section in the same time or not.
How do you parse spring (web) security expressions like hasRole('admin') programmatically (without using tags, annotations or ...)? (reference doc)
I've found Spring: What parser to use to parse security expressions - but I don't know how to find or build the EvaluationContext e.g. inside a spring controller.
Without providing an EvaluationContext gives
org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 0): Method call: Attempted to call method hasRole(java.lang.String) on null context object
you need to add several things in order to get this thing working. You have to plug into the Spring's security API. Here's how I did it and it is working fine with Spring 3.2.
First as it was stated before you must have similar configuration in your spring-context.xml:
<security:http access-decision-manager-ref="customAccessDecisionManagerBean">
<security:http/>
<bean id="customWebSecurityExpressionHandler"
class="com.boyan.security.CustomWebSecurityExpressionHandler"/>
<bean id="customAccessDecisionManagerBean"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler" ref="customWebSecurityExpressionHandler" />
</bean>
</list>
</property>
</bean>
This defines a new expressionHandler to override the default one for the WebExpressionVoter. Then we add this new decision voter to the decision manager. CustomWebSecurityExpressionHandler's purpose it to control the creation of SecurityExpressionRoot. So far so good. The question is why do you need a CustomWebSecurityExpressionRoot and the answer is simple as that - you define your custom security methods there. Having this in mind we can write the following classes:
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
CustomWebSecurityExpressionRoot expressionRoot =
new CustomWebSecurityExpressionRoot(authentication, delegationEvaluator);
return expressionRoot;
}
}
}
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication auth, FilterInvocation fi) {
super(auth, fi);
}
// in here you must define all of the methods you are going to invoke in #PreAuthorize
// for example if you have an expression with #PreAuthorize('isBoyan(John)')
// then you must have the following method defined here:
public boolean isBoyan(String value) {
//your logic goes in here
return "Boyan".equalsIgnoreCase(value);
}
}
If you want to get a reference to the ExpressionParser you can use the following method AbstractSecurityExpressionHandler.getExpressionParser(). It is accessible through CustomWebSecurityExpressionHandler. Also you can take a look at its API if you want to do something more specific.
I hope this answers you question.
I have some experience with JSF but I want to learn some Spring MVC now. I wish to display the options to the user to change the language my website is displayed in. To accomplish this I want to define the languages in XML and set them in a bean, then in a JSP iterate over that list to show the languages options to the user.
This is what my XML looks like:
<bean id="languagesSupportedBean" class="be.maxcorp.Util.LanguageBean">
<property name="languagesSupported">
<array>
<value>en</value>
<value>nl</value>
</array>
</property>
</bean>
This is my LanguagesSupportedBean class:
#Component
public class LanguageBean {
public String[] languagesSupported;
public String[] getLanguagesSupported() {
return languagesSupported;
}
public void setLanguagesSupported(String[] languagesSupported) {
this.languagesSupported = languagesSupported;
}
}
In my JSP I'd like to do something like this:
<c:forEach items="${languageBean.LanguagesSupported}" var="language">
${language}
</c:forEach>
Because Spring MVC is request-based and not component-based I suppose this approach won't work unless I add the LanguageBean as attribute to every Model param in every controller method?
I'd greatly appreciate any tips on accomplishing this.
If you're using an InternalResourceViewResolver you should be able to set a property called exposeContextBeansAsAttributes that will expose your beans as attributes that JSPs can access directly:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="exposeContextBeansAsAttributes" value="true"/>
</bean>
So if your LanguageBean is specified as id="languagesSupportedBean" in your XML, you can reference it directly in your JSP using its id:
<c:forEach items="${languagesSupportedBean.languagesSupported}" var="language">
${language}
</c:forEach>
So no controller or model modifications needed.
If you are declaring your LanguageBean in XML, then you won't need to annotate it #Component
Alternatively, if you're not using InternalResourceViewResolver you could inject your LanguageBean into your controller and then expose it using a method annotated #ModelAttribute:
#ModelAttribute("languagesSupportedBean")
public LanguageBean getLanguageBean() {
return languageBean;
}
That would then be accessible in your JSP using the name languagesSupportedBean and would alleviate the need to set the bean on every model in every controller method.
Store the bean in the session. You can access it the same way from the JSP. Otherwise you can extend Model and #Autowire your been there(not sure that it will work).
I have a Rest service using Resteasy (running on top of Appengine), with the following psuedo code:
#Path("/service")
public class MyService {
#GET
#Path("/start")
public Response startService() {
// Need to read properties file here.
// like: servletContext.getResourceAsStream("/WEB-INF/config.properties")
}
}
However its obvious that the servlet context cannot be accessed here.
And code like:
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream("/WEB-INF/config.properties");
Can't be executed within the Appengine environment.
EDIT:
I have tried doing it with Spring like:
appContext.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="/WEB-INF/auth.properties"/>
</bean>
Then, put this on the actual class fields:
#Path("/service")
public MyService{
#Autowired
#Value("${myservice.userid}")
private String username;
#Autowired
#Value("${myservice.passwd}")
private String password;
// Code omitted
}
However, part of the code of the MyService complains because the username and password was not "injected", I mean its empty although its on the auth.properties file
In RESTEasy you can easily inject Servlet context via #Context annotation: http://docs.jboss.org/resteasy/docs/2.3.1.GA/userguide/html_single/index.html#_Context
Examples can be found here: Rest easy and init params - how to access?
This should work if you put the file in /WEB-INF/classes/ (which, importantly, is on the classpath), specifying config.properties as a file at the top-level.
this.getClass().getClassLoader().getResourceAsStream("/config.properties");
See this similar question: How to load properties file in Google App Engine?
Edit: Now you've edited, I'll respond & answer the Spring-related question. So, put the auth.properties into /WEB-INF/classes/ , and then specify classpath as follows.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:auth.properties"/>
</bean>