I configured my security.xml file to support preAuthorized annotations
<bean id="permissionEvaluator" class="package.MyPermissionEvaluatorClass" />
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
<global-method-security pre-post-annotations="enabled" proxy-target-class="true">
<expression-handler ref="expressionHandler"/>
</global-method-security>
I added proxy-target-class=true as my REST class
My REST method with annotation looks like this
#PreAuthorize("#permissionEvaluator.hasPermission(#organizationId, 'MY_PERMISSION')")
#RequestMapping()
public ModelAndView findProductConfigurationsByOrganizationId(...) {}
My permissions evaluation class looks like this
public class MyPermissionEvaluatorClass extends PermissionEvaluatorImpl {
#Override
public boolean hasPermission(Authentication userObj, Object target, Object permission) {
LOGGER.info("Attempt to resolve permissions for {}.", target);
return false;
}
#Override
public boolean hasPermission(Authentication userObj, Serializable target, String targetType,
Object permission) {
return false;
}
But when I do rest requests - it seems does not enter method hasPermission at all.
What is the problem?
Related
I have a controller whose response is camelCase json value. Now we are re-writing the code with new version and the response required is in snake_case.
I have added a message converter and modified object mapper to setsetPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
public class ResponseJSONConverter extends MappingJackson2HttpMessageConverter {
#Autowired
public ResponseJSONConverter(ObjectMapper objectMapper) {
setObjectMapper(objectMapper);
}
}
I have registered this convertor with spring and its working as expected. Now I want my old endpoints to return in camelCase for backward compatibility for my consumers and new endpoints with snake_case.
I have tried to have one more message convertor with simple object mapper without setting camelCase to Snake case property and registered with spring. Only one message convertor gets applied based on the order declared in the spring configuration.
Is there any way we can achieve this ? Loading message convertor based on the condition ?
EDIT
Added my spring config file
<beans xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean id="moneySerializer" class="api.serialize.MoneySerializer"/>
<bean id="moneyDeserializer" class="api.serialize.MoneyDeserializer"/>
<bean id="serializationModule" class="api.serialize.SerializationModule">
<constructor-arg index="0" ref="moneySerializer"/>
<constructor-arg index="1" ref="moneyDeserializer"/>
</bean>
<bean id="customObjectMapper" class="api.serialize.CustomObjectMapper" primary="true">
<constructor-arg ref="serializationModule"/>
</bean>
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="api.serialize.ResponseJSONConverterCamelCaseToSnakeCase" >
<constructor-arg ref="customObjectMapper"/>
</bean>
<bean class="api.serialize.ResponseJSONConverter">
<constructor-arg ref="objectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/>
</beans>
EDIT 2.0
my servlet.xml
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="com.tgt.promotions.api.serialize.ServiceJSONConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
CustomMessageConverter
public class ServiceJSONConverter extends MappingJackson2HttpMessageConverter {
#Autowired
public ServiceJSONConverter(SnakeCaseObjectMapper snakeCaseObjectMapper) {
setObjectMapper(snakeCaseObjectMapper);
}
}
Custom Object Mapper
#Component
public class SnakeCaseObjectMapper extends ObjectMapper {
#Autowired
public SnakeCaseObjectMapper(PropertyNamingStrategy propertyNamingStrategy) {
setSerializationInclusion(JsonInclude.Include.NON_NULL);
setPropertyNamingStrategy(propertyNamingStrategy);
}
}
Custom Property Naming Strategy
#Component
public class CustomPropertyNamingStrategy extends PropertyNamingStrategy {
#Autowired
private HttpServletRequest request;
private final PropertyNamingStrategy legacyStrategy = PropertyNamingStrategy.LOWER_CASE;
private final PropertyNamingStrategy defaultStrategy = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES;
#Override
public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) {
return getStrategy().nameForConstructorParameter(config, ctorParam, defaultName);
}
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
return getStrategy().nameForField(config, field, defaultName);
}
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return getStrategy().nameForGetterMethod(config, method, defaultName);
}
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return getStrategy().nameForSetterMethod(config, method, defaultName);
}
private PropertyNamingStrategy getStrategy() {
if (isLegacyEndpoint(request)) {
return legacyStrategy;
} else {
return defaultStrategy;
}
}
private boolean isLegacyEndpoint(HttpServletRequest request) {
return request != null && request.getRequestURL() != null && !request.getRequestURL().toString().contains("/v3");
}
}
Instead of having 2 different object-mappers, I suggest creating a custom implementation of PropertyNamingStrategy, using the 2 other strategies accordingly:
public class AwesomePropertyNamingStrategy extends PropertyNamingStrategy {
private PropertyNamingStrategy legacyStrategy = PropertyNamingStrategy.LOWER_CASE;
private PropertyNamingStrategy defaultStrategy = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES;
#Override
public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName) {
return getStrategy().nameForConstructorParameter(config, ctorParam, defaultName);
}
// TODO: implement other nameForXXX methods
private PropertyNamingStrategy getStrategy() {
if (isLegacyEndpoint()) {
return legacyStrategy;
} else {
return defaultStrategy;
}
}
private boolean isLegacyEndpoint() {
// TODO: get hold of the RequestContext or some other thead-local context
// that allows you to know it's an old or a new endpoint
return false;
}
}
You should come up with a way to toggle between legacy and new mode:
Using the endpoint URL by accessing the request context in some way
In case your old endpoint use different response objects, use the class of the object that is being converted to determine legacy/normal or your own custom #LegacyResponse annotation on all old classes instead.
Well, nothing worked after many attempts. Finally ended up defining 2 different servlets. one without any version and one with v1 version.
web.xml
<servlet>
<servlet-name>snake-case</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>snake-case</servlet-name>
<url-pattern>/v1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>camel-case</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>camel-case</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Accordingly defined two servlets snake-case-servlet.xml and camel-case-servlet.xml.
snake-case-servlet.xml
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="com.tgt.promotions.api.serialize.DataJSONConverter">
<constructor-arg ref="snakeCaseObjectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
camel-case-servlet.xml
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="com.tgt.promotions.api.serialize.DataJSONConverter">
<constructor-arg ref="objectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Now, for any requests with /v1* , snakeCaseObjectMapper is used and for other requests default object mapper is used.
I'm trying to achieve authorization for my RestAPI, by letting Spring's Interceptor's (HandlerInterceptorAdapter) PreHandle method check wether the user is in the required role, before the scope hits the requested action in the controller. This requires, however, that I provide each action (URL Path) with the ID of the role it requires. This is my current setup:
public class AuthorizationInterceptor extends HandlerInterceptorAdapter{
#Autowired
IUserService us;
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, Integer roleId)
throws Exception {
String userId = request.getHeader("UserId");
if(!us.isUserInRole(Long.parseLong(userId), roleId))
return false;
return true;
}
}
And (a part of) the servlet-context.xml:
<interceptors>
<interceptor>
<mapping path="/" />
<mapping path="/users/**" />
<beans:bean class="com.lumi.api.interceptors.AuthorizationInterceptor"></beans:bean>
</interceptor>
</interceptors>
My question is, wether I can pass in the parameter roleId with the bean in the servlet-context config. I can't seem to find anything in the docs. I think I once saw something like:
<mapping path="/" />
<parameter name="something" value="some value">
But i'm not sure.
You can simply set a property using a standard spring sintax, an example
<beans:bean class="com.lumi.api.interceptors.AuthorizationInterceptor">
<beans:property name="roleId" value="REGISTERED_USER"/>
</beans:bean>
your interceptor should of course include the property, so simply
public class AuthorizationInterceptor extends HandlerInterceptorAdapter{
private String roleId;
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
// The rest of your code
}
In a swing GUI application, where MVC pattern is applied, how can we use Spring to wire the model view and controller? i.e. what beans (model, view or controller) should be injected using spring and what should be created from the application? I have applied the MVC pattern described here when developing the application. Thanks in advance.
If you have some leeway in the technologies you're using, I'd say you switch to (Griffon)[http://griffon.codehaus.org/]. It uses spring in the background and you also get the power of groovy and Swing UI builders. Best part is, you can still reuse the java code you've written so far. Also, you don't need to worry about DI and stuff. Griffon handles it for you.
On one of my projects, I successfully used Spring Rich Client.
If you are starting from scratch, I suggest that you take a look at it, it worth it. And it also provides some services out of the box (like authentication box and so).
I suggest that you can use "spring mvc".
Jsp(View) controller how to show the data;
Controller controll the return the view required data;
Server controller the system logic;
Model is the database model.
It would come to noone's surprise that I'd recommend you to have a look at Griffon. The MVC pattern is deeply engrained in Griffon's DNA, have a look at this sample app as shown in the Griffon Guide
http://griffon.codehaus.org/guide/0.9.5-rc2/guide/2.%20Getting%20Started.html#2.3%20A%20Groovy%20Console%20Example
Griffon provides basic DI capabilities for each MVC member, you only need to define properties following a naming convention. Services, where you would usually put most of the application's logic, are also automatically injected into controllers, as the guide explains in
http://griffon.codehaus.org/guide/0.9.5-rc2/guide/8.%20Controllers%20and%20Services.html#8.2%20Services
However you can make use of Spring DI too via the Spring plugin
http://artifacts.griffon-framework.org/plugin/spring
Spring beans may be defined using the standard XML approach, annotations or the Groovy Spring DSL.
I defined all the beans in spring and used a factory method to create the views when required. Controller is injected to the view and the model and view are added to the controller via spring.
Following are the code samples from a simple example that I came up with, in order to find a solution: (sorry for the long post!)
the application context file:
<bean id="firstModel" class="com.model.FirstModel"></bean>
<bean id="secondModel" class="com.model.SecondModel"></bean>
<bean id="firstController" class="com.controller.FirstController" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="firstController" />
</property>
<property name="targetMethod">
<value>addModel</value>
</property>
<property name="arguments">
<list>
<value>FIRST</value>
<ref local="firstModel" />
</list>
</property>
</bean>
<bean id="secondController" class="com.controller.SecondController" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="secondController" />
</property>
<property name="targetMethod">
<value>addModel</value>
</property>
<property name="arguments">
<list>
<value>SECOND</value>
<ref local="secondModel" />
</list>
</property>
</bean>
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="secondController" />
</property>
<property name="targetMethod">
<value>addModel</value>
</property>
<property name="arguments">
<list>
<value>FIRST</value>
<ref local="firstModel" />
</list>
</property>
</bean>
<bean id="firstForm" class="com.view.FirstForm">
<property name="controller">
<ref bean="firstController" />
</property>
</bean>
<bean id="secondForm" class="com.view.SecondForm">
<property name="controller">
<ref bean="secondController" />
</property>
</bean>
following is the abstract controller class:
public class AbstractController implements PropertyChangeListener {
Map<Type, BaseView> registeredViews;
Map<Type, AbstractModel> registeredModels;
public AbstractController() {
registeredViews = new HashMap<Type, BaseView>();
registeredModels = new HashMap<Type, AbstractModel>();
}
public void addModel(Type type, AbstractModel model) {
registeredModels.put(type, model);
model.addPropertyChangeListener(this);
}
public void removeModel(AbstractModel model) {
registeredModels.remove(model);
model.removePropertyChangeListener(this);
}
public void addView(BaseView view, Type type) {
registeredViews.put(type, view);
}
public void removeView(javax.swing.JFrame view) {
registeredViews.remove(view);
}
public void propertyChange(PropertyChangeEvent evt) {
for (BaseView view : registeredViews.values()) {
view.modelPropertyChange(evt);
}
}
protected void setModelProperty(String propertyName, Object newValue) {
for (AbstractModel model : registeredModels.values()) {
Statement statment = new Statement(model, "set" + propertyName, new Object[] { newValue });
try {
statment.execute();
} catch (NoSuchMethodException e) {
continue;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
following is the abstract model class:
public class AbstractModel {
protected PropertyChangeSupport propertyChangeSupport;
public AbstractModel() {
propertyChangeSupport = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
}
Following is the code sample of the view interface:
public interface BaseView {
void modelPropertyChange(PropertyChangeEvent evt);
public abstract void showForm();
}
following is the code sample of the factory class:
public class FormFactory {
private ApplicationContext context;
private static FormFactory viewFactory;
private FormFactory() {
if (context == null) {
context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
}
}
public static synchronized FormFactory getInstance() {
if (viewFactory == null) {
viewFactory = new FormFactory();
}
return viewFactory;
}
public BaseView createForm(Type type) {
BaseView form = null;
switch (type) {
case FIRST:
form = (BaseView) context.getBean("firstForm");
break;
case SECOND:
form = (BaseView) context.getBean("secondForm");
break;
default:
break;
}
return form;
}
}
i just wonder, can i convert uri string to another object type ?
#RequestMapping(value="/key/{keyDomain}", method=RequestMethod.GET)
public String propertyEditor(#PathVariable(value="keyDomain") KeyDomain key, Model model){
model.addAttribute("key", key);
return "propertyEditor";
}
and here my configuration
<beans:bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<beans:property name="customEditors">
<beans:map>
<!-- <beans:entry key="com.template.baseline.domain.KeyDomain" value="com.template.baseline.propertyEditor.KeyPropertyEditor"/> -->
<beans:entry key="com.template.baseline.domain.KeyDomain">
<beans:ref bean="keyDomainPropertyEditor" />
</beans:entry>
</beans:map>
</beans:property>
</beans:bean>
<!-- key domain property editor bean -->
<beans:bean id="keyDomainPropertyEditor" class="com.template.baseline.propertyEditor.KeyPropertyEditor">
<beans:property name="keyDomain">
<beans:bean class="com.template.baseline.domain.KeyDomain" />
</beans:property>
</beans:bean>
and propertyEditor Class :
public class KeyPropertyEditor extends PropertyEditorSupport{
private KeyDomain keyDomain;
/**
* example : 10435
* - 10 will be keyId
* - 435 will be baseOfficeId
*/
#Override
public void setAsText(String text) throws IllegalArgumentException{
KeyDomain keyDomain = new KeyDomain();
keyDomain.setKeyId(Integer.parseInt(text.substring(0,1)));
keyDomain.setBaseOfficeId(Integer.parseInt(text.substring(2,4)));
setValue(keyDomain);
}
#Override
public String getAsText() {
KeyDomain value = (KeyDomain) getValue();
return (value != null ? value.toString() : "");
}
public void setKeyDomain(KeyDomain keyDomain) {
this.keyDomain = keyDomain;
}
}
i thought that i can use Property Editor to convert my URI string become appropriate object type. i already made an implementation and configure CustomEditorConfigurer, but i always get ConversionNotSupportedException.
if i add initBinder at my controller, everything will just fine :
#InitBinder
public void setBinder(WebDataBinder dataBinder) {
dataBinder.registerCustomEditor(KeyDomain.class, new KeyPropertyEditor());
}
and i get Warning something like this
WARN : org.springframework.beans.factory.config.CustomEditorConfigurer - Passing PropertyEditor instances into CustomEditorConfigurer is deprecated: use PropertyEditorRegistrars or PropertyEditor class names instead. Offending key [com.template.baseline.domain.KeyDomain; offending editor instance: com.template.baseline.propertyEditor.KeyPropertyEditor#1a271f5
thanks for the answer.
ps : webBindingInitalizer injected on AnnotationMethodHandlerAdapter
<beans:bean id="AnnotationMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<beans:property name="webBindingInitializer">
<beans:bean class="com.template.baseline.initialize.CustomWebBindingInitializer" />
</beans:property>
</beans:bean>
and Implementation
public class CustomWebBindingInitializer implements WebBindingInitializer {
public CustomWebBindingInitializer(){
System.out.println("******** constructor *********");
}
public void initBinder(WebDataBinder binder, WebRequest request) {
System.out.println("******** initBinder *********");
binder.registerCustomEditor(KeyDomain.class, new KeyDomainPropertyEditor());
}
}
CustomEditorConfigurer has nothing to do with web request data binding.
If you want to register your PropertyEditor globablly, you need to implement WebBindingInitializer and supply AnnotationMethodHandlerAdapter with it:
<bean
class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<proeprty name = "webBindingInitializer">
<bean class = "MyWebBindingInitializer" />
</property>
</bean>
Another option is to implement your conversion logic as a Formatter and configure it via FormattingConversionServiceFactoryBean and <mvc:annotation-driven>, see mvc-showcase sample.
I'd like to convert this SimpleFormController to use the annotation support introduced in Spring MVC 2.5
Java
public class PriceIncreaseFormController extends SimpleFormController {
ProductManager productManager = new ProductManager();
#Override
public ModelAndView onSubmit(Object command)
throws ServletException {
int increase = ((PriceIncrease) command).getPercentage();
productManager.increasePrice(increase);
return new ModelAndView(new RedirectView(getSuccessView()));
}
#Override
protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
return priceIncrease;
}
}
Spring Config
<!-- Include basic annotation support -->
<context:annotation-config/>
<!-- Comma-separated list of packages to search for annotated controllers. Append '.*' to search all sub-packages -->
<context:component-scan base-package="springapp.web"/>
<!-- Enables use of annotations on controller methods to map URLs to methods and request params to method arguments -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean name="/priceincrease.htm" class="springapp.web.PriceIncreaseFormController">
<property name="sessionForm" value="true"/>
<property name="commandName" value="priceIncrease"/>
<property name="commandClass" value="springapp.service.PriceIncrease"/>
<property name="validator">
<bean class="springapp.service.PriceIncreaseValidator"/>
</property>
<property name="formView" value="priceincrease"/>
<property name="successView" value="hello.htm"/>
<property name="productManager" ref="productManager"/>
</bean>
Basically, I'd like to replace all the XML configuration for the /priceincrease.htm bean with annotations within the Java class. Is this possible, and if so, what are the corresponding annotations that I should use?
Thanks,
Don
It'll look something like the following, although whether it works or not exactly as is will depend a bit on your configuration (view resolver, etc). I should also note that there are about eight billion valid ways to write this thing. See the Spring documentation, 13.11.4 "Supported handler method arguments and return types" for an overview of the insanity. Also note that you can autowire the validator
#Controller
#RequestMapping("/priceincrease.htm")
public class PriceIncreaseFormController {
ProductManager productManager;
#Autowired
public PriceIncreaseFormController(ProductManager productManager) {
this.productManager = productManager;
}
// note: this method does not have to be called onSubmit
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("priceIncrease") PriceIncrease priceIncrease, BindingResult result, SessionStatus status {
new PriceIncreaseValidator().validate(priceIncrease, result);
if (result.hasErrors()) {
return "priceincrease";
}
else {
int increase = priceIncrease.getPercentage();
productManager.increasePrice(increase);
status.setComplete();
return "redirect:hello.htm";
}
}
// note: this method does not have to be called setupForm
#RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
model.addAttribute("priceIncrease", priceIncrease);
return "priceincrease";
}
}
Someone completed this project with a recent MVC and it's on github, so you can see how all the classes are changed compared to Spring's tutorial.
Link: PriceIncreaseFormController