Adding parameters to Spring MVC Interceptor - java

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
}

Related

#PreAuthorize is not invoked

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?

Spring : Spring-cache not working

I am working on a Spring-MVC application. After the profiler went through the backend, I noticed that getCurrentlyAuthenticatedUser() is an hotspot. For the reason I thought of using cache. Unfortunately it is not working. Once the user logs in, I am getting null user, even though the user is logged in. What is going wrong. Ofcourse when I remove the #Cacheable annotation and config from XML, everything works fine. Any help would be nice.
PersonServiceImpl :
#Service
#Transactional
public class PersonServiceImpl implements PersonService {
private final PersonDAO personDAO;
#Autowired
public PersonServiceImpl(PersonDAO personDAO) {
this.personDAO = personDAO;
}
#Override
#Cacheable(value = "person", condition = "#person == null")
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
} else {
return personDAO.findPersonByUsername(authentication.getName());
}
}
}
config for using cache :
<cache:annotation-driven />
<beans:bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<beans:property name="caches">
<beans:set>
<beans:bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="person"/>
</beans:set>
</beans:property>
</beans:bean>
when I call this method, even if logged in or not, I am getting null back. Any help would be nice. Thanks.
In the below code line:-
#Cacheable(value = "person", condition = "#person == null")
Try to use unless in place of condition
Actually you have put up an condition that "Caching will only work if the person is null", which should be like "Caching will work unless person is null".
Happy coding.

Using Custom AuthenticationProcessingFilter with <form-login> (auto-config="true")

Spring security (2.0.x) http namespace, form-login definition automatically uses AuthenticationProcessingFilter.
<form-login login-page='/logon.jsp'
default-target-url='/home.jsp'
always-use-default-target='true' />
I also know that If I set auto-config="false" I can customise authentication by providing custom bean definition.
I have CustomAuthenticationProcessingFilter that extends AuthenticationProcessingFilter overrides obtainUsername and uses custom logic to get username than the one passed.
protected String obtainUsername(HttpServletRequest request) {
// custom logic to return username from parameter/cookies/header etc ...
}
Is it possible to use CustomAuthenticationProcessingFilter while still using auto-config="true" <form-login> without needing to define customAuthFilter and all dependent beans ?
<beans:bean id="customAuthFilter" class="x.y.z.CustomAuthenticationProcessingFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
<beans:property name="defaultTargetUrl" value="/home.jsp"></beans:property>
...
...
</beans:bean>
Intro
Spring Security 2.0 is in maintenance mode, so there are not going to be any official updates to it. There are, however, a few approaches you can use to get around this problem.
BeanPostProcessor
A trick you can use from the Spring Security FAQ is to use a BeanPostProcessor. Instead of modifying a property, you can return your custom Filter. An example might be something like this:
public class CustomFilterBeanPostProcessor implements BeanPostProcessor {
private Filter customFilter;
public Object postProcessAfterInitialization(Object bean, String name) {
if (bean instanceof AuthenticationProcessingFilter) {
return customFilter;
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String name) {
return bean;
}
public void setFilter(Filter filter) {
this.customFilter = filter;
}
}
Then your configuration would include the following:
<beans:bean class="CustomFilterBeanPostProcessor">
<beans:property name="filter" ref="customAuthFilter"/>
</beans:bean>
Use before attribute
An alternative is to insert the custom Filter before the AuthenticationProcessingFilter. This will have an additional Filter, but it should be minimally invasive since it is small and should not ever be reached (i.e. since the custom Filter only continues the FilterChain when the AuthenticationProcessingFilter ignores the request). An example configuration using this approach can be seen below:
<beans:bean id="customAuthFilter" class="x.y.z.CustomAuthenticationProcessingFilter">
<custom-filter before="AUTHENTICATION_PROCESSING_FILTER" />
<beans:property name="defaultTargetUrl" value="/home.jsp"></beans:property>
...
...
</beans:bean>
Alas, As it appears (If I am not wrong) nothing much could be done as AuthenticationProcessingFilter class name is hardcoded in <HttpSecurityBeanDefinitionParser> :(
if (formLoginElt != null || autoConfig) {
FormLoginBeanDefinitionParser parser =
new FormLoginBeanDefinitionParser("/j_spring_security_check",
"org.springframework.security.ui.webapp.AuthenticationProcessingFilter");
.
.
It would have been nicer if filter class was a config attribute and be controlled externally (just like default-target-url)
may be using attribute authentication-filter-class
<form-login login-page='/logon.jsp'
default-target-url='/home.jsp'
always-use-default-target='true'
authentication-filter-class='x.y.z.CustomAuthenticationProcessingFilter'
/>
Hope Spring folks are listening ;)
The fact is that spring's namespace handler internally defines bean with the name _formLoginFilter for AuthenticationProcessingFilter (See for BeanIds complete list). There are coulpe of ways to workaround with this issue (i.e to authenticate using something other than j_username from DaoAuthenticationProvider , like say take username from header etc... )
Use Spring AOP bean() syntax to intercept doFilter()
Define a pointcut that looks for bean with name _formLoginFilter and intercepts doFiltermethod. ( AuthenticationProcessingFilter.doFilter() method) and conditionally delegate to something else
public class AuthenticationProcessingFilterAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationProcessingFilterAspect.class);
public Object intercept(ProceedingJoinPoint pjp) throws Throwable {
LOGGER.info("intercept------------------{}",pjp.toLongString());
//Delegate to customised method instead of default pjp.proceed()
return pjp.proceed();
}
}
Config
<beans:bean id="authFilterAspect" class="x.y.z.AuthenticationProcessingFilterAspect" />
<aop:config>
<aop:aspect ref="authFilterAspect">
<aop:around pointcut="bean(_formLoginFilter) && execution(* doFilter(..))" method="intercept"/>
</aop:aspect>
</aop:config>
Use CustomWebAuthenticationDetails to do authentication
Define a bean postprocessor for AuthenticationProcessingFilter bean that injects CustomWebAuthenticationDetails which populates custom fields
public class AuthenticationProcessingFilterBeanPostProcessor implements
BeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationProcessingFilterBeanPostProcessor.class);
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if ("_formLoginFilter".equals(beanName) && bean instanceof AuthenticationProcessingFilter) {
AuthenticationProcessingFilter filter = (AuthenticationProcessingFilter) bean;
WebAuthenticationDetailsSource source = (WebAuthenticationDetailsSource) filter.getAuthenticationDetailsSource();
source.setClazz(CustomWebAuthenticationDetails.class);
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#SuppressWarnings("serial")
public static class CustomWebAuthenticationDetails extends
WebAuthenticationDetails {
private String customAttribute;//customfield
public CustomWebAuthenticationDetails(HttpServletRequest request) {
super(request);
//Build custom attributes that could be used elsewhere (say in DaoAuthenticationProvider )
//with (CustomWebAuthenticationDetails)authentication.getDetails()
customAttribute = request.getHeader("username");
}
public boolean getCustomAttribute() {
return customAttribute;
}
}
}
Config
<beans:bean id="authFilterProcessor" class="x.y.z.AuthenticationProcessingFilterBeanPostProcessor" />
Use thread bound request to do actual authentication (within DaoAuthenticationProvider)
Use getHttpServletRequest() to access threadbound request object and use request.getHeader("username") to do custom authentication.
public static HttpServletRequest getHttpServletRequest(){
return((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
}
Also need to Define this in web.xml if request is not through DispatcherServlet
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/j_spring_security_check</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/j_spring_security_logout</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
If its faces application use FacesContext.getCurrentInstance()
public static HttpServletRequest getHttpServletRequest(){
FacesContext context = FacesContext.getCurrentInstance();
return (HttpServletRequest) context.getExternalContext().getRequest();
}

how to convert uri string to complex object type in spring

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.

annotations in Spring MVC

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

Categories