Intercepting Requests Using Spring Security Rather Than putting pre-handler Interceptor - spring-mvc

I am building a Spring MVC web-application. I want to intercept the request with some business logic of validating token stored in database. I want some alternative to HandlerInterceptor's preHandle method. I am trying to implement the same with Spring Security.Please suggest some way.
EDIT
This is how I intercept all requests in my web application :
public class RequestInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(RequestInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//I validate user's token from my database here
//return true if valid token else false
}
}
My servlet-context.xml looks like
<mvc:interceptors>
<bean class="com.clarice.rest.security.RequestInterceptor">
<property name="XXX">
<list>
<value>YYY</value>
</list>
</property>
</bean>
</mvc:interceptors>
So my question is , Is there any way to implement the same interceptor kind of thing using Spring Security writing my own code as I do in RequestInterceptor class ?

Related

Integrating Atmosphere with Spring MVC

I want to provide push notifications in a Spring MVC project, using JDK 1.6 targeting all browsers. I followed this post and finally decided to go with Atmosphere.
For Server-sent events, My server controller is (source):
#Controller
public class AtmosphereController {
#RequestMapping(value="/getTime", method=RequestMethod.GET)
#ResponseBody
public void websockets(final AtmosphereResource atmosphereResource) {
final HttpServletRequest request = atmosphereResource.getRequest();
final HttpServletResponse response = atmosphereResource.getResponse();
atmosphereResource.suspend();
final Broadcaster bc = atmosphereResource.getBroadcaster();
bc.scheduleFixedBroadcast(new Callable<String>() {
public String call() throws Exception {
return (new Date()).toString();
}
}, 10, TimeUnit.SECONDS);
}
}
While running, I got org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.atmosphere.cpr.AtmosphereResource]: Specified class is an interface.
In solution to this, I got this relevant post. I added this to my dispatcher-servlet.xml:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean id= "atmosphereResource"
class="org.atmosphere.cpr.AtmosphereResourceImpl" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
Doing this, also results in a new error:
[cvc-complex-type.2.1: Element 'mvc:annotation-driven' must have no character or element information item [children], because the type's content type is empty.]
I've also tried this. Please help me inject AtmosphereResource in Spring Controller. Do I also need to update web.xml or some other configuration file to make it work or what part I'm missing. Please help!
Please also comment on other alternatives to provide Server-Side Events functionality. Thanks in advance!
Your argument-resolvers must be a class like this:
public class AtmosphereArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest httpServletRequest= webRequest.getNativeRequest(HttpServletRequest.class);
return Meteor.build(httpServletRequest).getAtmosphereResource();
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return AtmosphereResource.class.isAssignableFrom(parameter.getParameterType());
}
}
and try this:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.yourpackage.AtmosphereArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
try Atmosphere 2.1.0-RC1 and following this document . All you need to do is to add the atmosphere-spring.jar to your dependency.
-- Jeanfrancois

How to implement custom authentication in Spring Security 3?

I know this has been answered so many times, but I am confused. I already have an Authentication mechanism in my application and I just want to use the authorization part of Spring MVC. I'm using Spring MVC 3 and Spring Security 3.
When I search on internet I found two solutions, the first one is to just implement AuthenticationProvider interface. Example1. The second one is to implement UserDetails and UserDetailsService, Example2 so I'm lost here.
----Update----
The second part of the Question is here. And the solution to the workaround.
In most cases when only using usernames and passwords for authentications and roles for authorisation, implementing your own UserDetailsService is enough.
The flow of the username password authentication is then generally as follows:
A spring security filter (basic authentication/form/..) picks up the username and password, turns it into an UsernamePasswordAuthentication object and passes it on to the AuthenticationManager
The authentication manager looks for a candidate provider which can handle UsernamePasswordtokens, which in this case is the DaoAuthenticationProvider and passes the token along for authentication
The authentication provider invokes the method loadUserByUsername interface and throws either a UsernameNotFound exception if the user is not present or returns a UserDetails object, which contains a username, password and authorities.
The Authentication provider then compares the passwords of the provided UsernamePasswordToken and UserDetails object. (it can also handle password hashes via PasswordEncoders) If it doesn't match then the authentication fails. If it matches it registers the user details object and passes it on to the AccessDecisionManager, which performs the Authorization part.
So if the verification in the DaoAuthenticationProvider suits your needs. Then you'll only have to implement your own UserDetailsService and tweak the verification of the DaoAuthenticationProvider.
An example for the UserDetailsService using spring 3.1 is as follows:
Spring XML:
<security:authentication-manager>
<security:authentication-provider user-service-ref="myUserDetailsService" />
</security:authentication-manager>
<bean name="myUserDetailsService" class="x.y.MyUserDetailsService" />
UserDetailsService Implementation:
public MyUserDetailsService implements UserDetailsService {
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//Retrieve the user from wherever you store it, e.g. a database
MyUserClass user = ...;
if (user == null) {
throw new UsernameNotFoundException("Invalid username/password.");
}
Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("Role1","role2","role3");
return new User(user.getUsername(), user.getPassword(), authorities);
}
}

Controller Inheritance and Ambiguous Mappings with URL Versioning in Spring MVC

I am trying to setup versioned services with Spring MVC, using inheritance to extend older controllers to avoid rewriting unchanged controller methods. I've based my solution on a previous question about versioning services, however I've run into a problem with ambiguous mappings.
#Controller
#RequestMapping({"/rest/v1/bookmark"})
public class BookmarkJsonController {
#ResponseBody
#RequestMapping(value = "/write", produces = "application/json", method = RequestMethod.POST)
public Map<String, String> writeBookmark(#RequestParam String parameter) {
// Perform some operations and return String
}
}
#Controller
#RequestMapping({"/rest/v2/bookmark"})
public class BookmarkJsonControllerV2 extends BookmarkJsonController {
#ResponseBody
#RequestMapping(value = "/write", produces = "application/json", method = RequestMethod.POST)
public BookmarkJsonModel writeBookmark(#RequestBody #Valid BookmarkJsonModel bookmark) {
// Perform some operations and return BookmarkJsonModel
}
}
With this setup I get IllegalStateException: Ambiguous mapping found. My thought regarding this is that because I have two methods with different return/argument types I have two methods in BookmarkJsonControllerV2 with the same mapping. As a workaround I attempted to override writeBookmark in BookmarkJsonControllerV2 without any request mapping:
#Override
public Map<String, String> writeBookmark(#RequestParam String parameter) {
return null; // Shouldn't actually be used
}
However, when I compiled and ran this code I still got the exception for an ambiguous mapping. However, when I hit the URL /rest/v2/bookmark/write I got back an empty/null response. Upon changing return null to:
return new HashMap<String, String>() {{
put("This is called from /rest/v2/bookmark/write", "?!");
}};
I would receive JSON with that map, indicating that despite not having any request mapping annotation, it is apparently "inheriting" the annotation from the super class. At this point, my only "solution" to future-proofing the extension of the controllers is to make every controller return Object and only have the HttpServletRequest and HttpServletResponse objects as arguments. This seems like a total hack and I would rather never do this.
So is there a better approach to achieve URL versioning using Spring MVC that allows me to only override updated methods in subsequent versions or is my only real option to completely rewrite each controller?
For whatever reason, using the #RequestMapping annotation was causing the ambiguous mapping exceptions. As a workaround I decided to try using springmvc-router for my REST services which would allow me to leverage inheritance on my controller classes so I would not have to reimplement endpoints that did not change between versions as desired. My solution also allowed me to continue using annotation mappings for my non-REST controllers.
Note: I am using Spring 3.1, which has different classes for the handler mappings than previous versions.
The springmvc-router project brings the router system from the Play framework over to Spring MVC. Inside of my application-context.xml, the relevant setup looks like:
<mvc:annotation-driven/>
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
<bean class="org.resthub.web.springmvc.router.RouterHandlerMapping">
<property name="routeFiles">
<list>
<value>routes/routes.conf</value>
</list>
</property>
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="order" value="1" />
</bean>
This allows me to continue using my annotated controllers alongside the router. Spring uses a chain-of-responsibility system, so we can assign multiple mapping handlers. From here, I have a router configuration like so:
# Original Services
POST /rest/bookmark/write bookmarkJsonController.write
POST /rest/bookmark/delete bookmarkJsonController.delete
# Version 2 Services
POST /rest/v2/bookmark/write bookmarkJsonControllerV2.write
POST /rest/v2/bookmark/delete bookmarkJsonControllerV2.delete
Alongside controllers looking like:
#Controller
public class BookmarkJsonController {
#ResponseBody
public Map<String, Boolean> write(#RequestParam String param) { /* Actions go here */ }
#ResponseBody
public Map<String, Boolean> delete(#RequestParam String param) { /* Actions go here */ }
}
#Controller
public class BookmarkJsonControllerV2 extends BoomarkJsonController {
#ResponseBody
public Model write(#RequestBody Model model) { /* Actions go here */ }
}
With a configuration like this, the URL /rest/v2/bookmark/write will hit the method BookmarkJsonControllerV2.write(Model model) and the URL /rest/v2/bookmark/delete will hit the inherited method BookmarkJsonController.delete(String param).
The only disadvantage from this comes from having to redefine entire routes for new versions, as opposed to changing the #RequestMapping(value = "/rest/bookmark") to #RequestMapping(value = "/rest/v2/bookmark") on the class.

Spring beans injection into jax-ws services

So I already learnt that integration of spring and jax-ws is not an easy thing.
I want to inject a spring bean into jax-ws service, but for some reason I get an exception during the deployment:
Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.IllegalArgumentException: javax.servlet.ServletException: com.sun.enterprise.container.common.spi.util.InjectionException: Error creating managed object for class: class org.springframework.web.context.ContextLoaderListener|#]
this is my jax-ws configuration:
<wss:binding url="/ws/users">
<wss:service>
<ws:service bean="#usersWs"/>
</wss:service>
</wss:binding>
<bean id="usersWs" class="love.service.endpoint.implementations.UserServiceImpl" />
And this is my service:
#WebService
public class UserServiceImpl implements UserService{
#EJB
private DBManager dbmanager;
#Override
#WebMethod
public boolean addUser(String name, String password, String email) {
return false;
}
#Override
#WebMethod
public boolean isUsernameAvailable(String username) {
return dbmanager.isLoginAvailable(username);
}
#Override
#WebMethod
public boolean isEmailAvailable(String email) {
return dbmanager.isEmailAvailable(email);
}
}
and finally my bean configuration:
<bean id="dbmanager" class="love.commons.database.DBManager" scope="request">
<aop:scoped-proxy/>
</bean>
I also tried injecting the bean into some controllers and then it works perfectly well.
If I replace #EJB with #Autowired, the application starts, but the service still doesn't work. When I tried sending a message to it, my only response was the following:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
<faultcode>S:Server</faultcode>
<faultstring>Error creating bean with name 'scopedTarget.dbmanager': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.</faultstring>
</S:Fault>
</S:Body>
</S:Envelope>

Spring security:How to map j_spring_security_check in controller?

I'm trying to integrate spring security into existing code base which is in spring mvc.
In my existing code base i v'e a client delegate in web project which interacts with service delegate in service project which in turn interacts with service interface of back-end layer.It is the service interface impl of back-end layer which i'm making implement the UserDetailsService interface.
P.S - I'm creating/converting/passing on various objects i.e. Model,DO,DTO,Request,Response etc across all the layers.
So now,when i click on submit i want the control to go through the delegate from web to back-end layer till the class which implements UserDetailsService interface. How can i achieve this?
[Update]:After going through this link i changed my security xml like shown below:
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/welcome.html" access="hasRole('ROLE_USER')" />
<security:form-login login-page="/portalLogin.html" login-processing-url="/signIn" always-use-default-target="true"
username-parameter="username" password-parameter="password"
default-target-url="/welcome.html" authentication-failure-url="/loginfailed.html" />
<security:logout logout-success-url="/logout.html" />
</security:http>
My LoginController is like this:
#RequestMapping("/signIn")
public ModelAndView onClickLogin(#ModelAttribute("dashboard")
LoginModel loginModel,Model model){
try{
delegate.clickLogin(loginModel);
if(null != loginModel.getError()){
// return new ModelAndView("error");
}
}catch(Exception exception){
}
return new ModelAndView("redirect:welcome.html");
}
[Update]:Sorry for very late update.Controller Mapping issue is resolved, now i'm facing issues because of UsernamePasswordAuthenticationFilter.After comparing logs generated by above code with properly working code logs,i figured out that spring security is not firing 'UsernamePasswordAuthenticationFilter' for some reasons in my case.'UsernamePasswordAuthenticationFilter' is the one which authenticates the user.
//Logs when authentication is done
[while using spring's default /j_spring_security_check].
DEBUG FilterChainProxy - /j_spring_security_check at position 3 of 10 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
DEBUG UsernamePasswordAuthenticationFilter - Request is to process authentication
DEBUG ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
session created
DEBUG SessionImpl - Opened session at timestamp: 13661947944
SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])
After openSession
DEBUG SessionImpl - Opened session at timestamp: 13661947944
Criteria before eq::this::CriteriaImpl(com.infy.itrade.core.entity.LoginDO:this[][])::CriteriaImpl(com.infy.itrade.core.entity.LoginDO:this[][])...
//Logs when authentication is not done [while using custom login processing url : /signIn.html].
DEBUG FilterChainProxy - /signIn.html at position 3 of 10 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' //UsernamePasswordAuthenticationFilter is not doing authentication process instead it skips to next filter.
DEBUG FilterChainProxy - /signIn.html at position 4 of 10 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
DEBUG FilterChainProxy - /signIn.html at position 5 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
I created a custom filter 'MyFilter' which extends 'AbstractAuthenticationProcessingFilter'. But still 'MyFilter' doesn't do any authentication as the log shows the following:
DEBUG FilterChainProxy - /signIn.html at position 2 of 9 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG FilterChainProxy - /signIn.html at position 3 of 9 in additional filter chain; firing Filter: 'MyFilter'
DEBUG FilterChainProxy - /signIn.html at position 4 of 9 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
MyFilter:
public class MyFilter extends AbstractAuthenticationProcessingFilter {
private static final String DEFAULT_FILTER_PROCESSES_URL = "/signIn";
private static final String POST = "POST";
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
public MyFilter() {
super(DEFAULT_FILTER_PROCESSES_URL);
}
public MyFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
// TODO Auto-generated constructor stub
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse arg1) throws AuthenticationException,
IOException, ServletException {
// TODO Auto-generated method stub
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(
SPRING_SECURITY_LAST_USERNAME_KEY,
TextEscapeUtils.escapeEntities(username));
}
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
if (request.getMethod().equals(POST)) {
// If the incoming request is a POST, then we send it up
// to the AbstractAuthenticationProcessingFilter.
super.doFilter(request, response, chain);
} else {
// If it's a GET, we ignore this request and send it
// to the next filter in the chain. In this case, that
// pretty much means the request will hit the /login
// controller which will process the request to show the
// login page.
chain.doFilter(request, response);
}
}
...
}
Context xml:
<bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/portalLogin.html"/>
</bean>
<bean id="MyFilter" class="com.infosys.itrade.core.dao.security.MyFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successHandler"></property>
<property name="authenticationFailureHandler" ref="failureHandler"></property>
<property name="filterProcessesUrl" value="/signIn"></property>
</bean>
<bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/welcome.html" />
</bean>
<bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/loginfailed.html" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
user-service-ref="LoginUserDetailsService">
</security:authentication-provider>
</security:authentication-manager>
<bean id="LoginUserDetailsService"
class="com.infy.itrade.core.service.login.LoginServiceImpl">
<property name="logindao" ref="loginDAOImpl" />
</bean>
<bean id="loginDAOImpl" class="com.infy.itrade.core.dao.login.LoginDAODBImpl">
<property name="sessionFactory"> <ref bean ="sessionFactory"/> </property>
</bean>
</beans>
What am i missing here?
[Update]: Since i couldn't get this working i changed my approach as suggested by others here i.e. I'm avoiding the architectural flow for login module alone. But right now i'm facing a different issue with the new approach for which i'll ask a different question. Thanks.

Resources