Two Contexts in Spring MVC applications – Application Context, WebApplicationContext

By W.ZH Sept 2016

If you directly start learning Spring from the Spring MVC. then you may be ignored a context issue. MVC works in a context called webapplicationcontext  or servlet context. besides this one spring itself already has a root context or application context. So there are some very important points you must be clear on these two:

  • Application Context default use this files as config file – applicationContext.xml . One web application only has one Application Context. Spring loads applicationContext.xml file and creates the ApplicationContext for the whole application. if you have more than one config files for the context, you can config them in the web.xml as the <context-param>:
<web-app ..... >
....
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml, classpath:spring-security.xml</param-value>
    </context-param>
...
</web-app>

  • Spring security is in the root context – Application Context, not in the WebApplicationContext
  • Each DispatcherServlet associated with single WebApplicationContext. There can be multiple WebApplicationContext in a single web application.each DispatcherServlet could have one xml config used. in Web.xml it looks like this:
     <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc-dispatcher.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
  • WebApplicationContext is a child of “ApplicationContext”, You can access bean in the root context from the WebApplicationContext, but you can not access the bean in the WebApplicationContext from root context.
  • WebApplicationContext contains the  controllers, RestControllers and view resolvers. Root context contains other services, components, DAO access  initialized by the ContextLoaderListener.
  • If you define a property-placeholder in one context, it can not be accessed from another context. This means if you have one properties files need be accessed by @Value in two context, you need to insert this piece config in both context’s xml file”
<context:property-placeholder location="classpath:myConfigProp.properties"
        ignore-resource-not-found="true" />

 

Refer to:

https://spring.io/understanding/application-context

http://hmkcode.com/spring-mvc-share-root-webapplicationcontext-beans-with-child-webapplicationcontext/

 

Advertisements

Spring security note – Create a simple authentication-manager by get user from DB table – 2

By WZH. Sept 2016

When you use the Spring Security, you system could save user define in many kinds of places, such as dummy test account , DB table, or LDAP. So you just need to have different user service supply to spring security’s  authentication-manager, as far as you meet the needs defined by the authentication-manager.

authentication-manager in fact needs a UserDetails object to contain the user name, password and Authorities. Here I show you one example that just use XML to define a DB access user service :

<security:authentication-manager id="directDBAuthManager">
        <security:authentication-provider>
            <security:password-encoder hash="md5" />
            <security:jdbc-user-service
                data-source-ref="myDataSource" id="userDetailsDBService"
                users-by-username-query="SELECT username, password, enabled FROM abc_DB.usertable WHERE username=?"
                authorities-by-username-query="SELECT username, role FROM abc_DB.usertable where username =?  " />
        </security:authentication-provider>
    </security:authentication-manager>

 

myDataSource is a datasource define in the spring, refer to datasource define docs. Then  you just need to use it at here to access DB.

users-by-username-query must be a SQL to return the user name , password and enable(true/false) in this order to the system(no need to be these names). So you can see that my DB of abc_DB has a table called usertable to contain the user data and return these data to system.

authorities-by-username-query in fact returns the user and and roles to the system to create the authorities.

Spring internal in fact uses there two query to get data and create the method of
public UserDetails loadUserByUsername(String username)  for the UserDetailsService.

<security:password-encoder hash=”md5″ /> this is a note that password format in fact is a MD5. So when the authentication-manager get the UserDetails, it will compare the login input’s password with password from UserDetails in the MD5 format.

id=”userDetailsDBService” – with this ID here, you can look up this bean by ID in the java code to call this UserDetailsService. so that when you need to UserDetails in java code, you can just call this user service directly.

Two or more authentication-manager in Spring security

By W.ZH Sept 2016

In Spring Security, you may need to two separate authentication-manager in system.  here is one example :

    <!-- Authentication manager 1 -->
    <security:authentication-manager id="userAuthenManager">
        <security:authentication-provider
            user-service-ref="userDetailsService">
            <security:password-encoder hash="md5" />
        </security:authentication-provider>
    </security:authentication-manager>
  
    <bean id="userDetailsService" class="com.abc.userdetails.UserDetailsServiceImpl" />

    <!-- Authentication manager 2 -->
    <security:authentication-manager id="dummyAuthenManager"> 
        <security:authentication-provider> 
            <security:user-service> 
                <security:user name="admin_user" password="password" authorities="ROLE_ADMIN" /> 
                <security:user name="normal_user" password="password" authorities="ROLE_USER" /> 
            </security:user-service> 
        </security:authentication-provider> 
    </security:authentication-manager>

 

So there is some special note you need to notice at here:

  1. Two or more authentication-manager must have respectively unique ID for them. So  that when you define the http security checker, you can use ID to define which authentication-manager is used. Like this:
    <security:http auto-config="true" use-expressions="true"
            authentication-manager-ref="userAuthenManager">
              。。。。。。。。。。。。。。。。。。。
        </security:http>
  2.  Order and others no special request.
  3. Normally when you create a authentication-manager, you must have have the user service  for it. they will have different user details service for it.
  4. To create a user service, you can use the dummy user hard code there(for testing only), like the dummyAuthenManager. or you can use a JDBC access the user details from DB tables. or you can implement a user service by your self by implement interface of org.springframework.security.core.userdetails.UserDetailsService, such as the UserDetailsServiceImpl in my example.  The jave code will like this in my example:

 

package com.abc.userdetails;

import javax.annotation.Resource;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.abc.dbservice.impl.UserInfoServiceImpl;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource(name = "userInfoService")
    private UserInfoServiceImpl userInfoService;
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        UserDetails user = userInfoService.searchByLoginId(username);
        if (user == null) {
            throw new UsernameNotFoundException(String.format(
                    "No user found with username '%s'.", username));
        } else {
            return user;
        }
    }
}

loadUserByUsername method is the only one you need to realize for this interface. and authentication-manager will call this method to get user, and then compare it with the user’s input name and password to do the authentication.

Implement the UserDetailsService inteface should be the best way for the user service as you can do a lot things in the UserDetailsServiceImpl code.