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/

 

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.

 

 

 

Solve the Json/Java polymorphism request by Jackson

By W.ZH

When convert between json and jave objects, you could often face the polymorphism issue.
Such as a json of Content could be:

{
  "type":"valuea",
  "value": {
    "valueaName": "corn",
    "bar":  "sweet"
  }
}

and also could be

{
  "type":"valueb",
  "value": {
    "valuebName": "toy",
    "color":  "yellow",
    "price":  "20"     
  }
}

Value objects in fact is depends on the type data to change. This requires that depending on the type data and create different value’s object at Java side, How to do?

Here is the solution at the Jackson code by using the     @JsonTypeInfo

let us define a abstract class Value and another two class ValueA and ValueB to Extends it.

public abstract class Value {
}

and ValueA to inherit Value

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ValueA extends Value {

    @JsonProperty("valueaName")
    private String valueaName = "";

    @JsonProperty("bar")
    private String bar;
....................................
....................................
}

and ValueB Class to inherit Value.

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ValueB extends Value {

    @JsonProperty("valuebName")
    private String valuebName = "";

    @JsonProperty("color")
    private String color;

    @JsonProperty("price")
    private String price;
....................................
....................................
}

 

Then after that we can create a Class for Content to let “type” files  work as a EXTERNAL_PROPERTY to control the Value object in side the Content:

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Content {

    @JsonProperty("type")
    private String type;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonProperty("value")
    private Value value = null;
    
    public Content (){
    }
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
    @JsonSubTypes({ @Type(value = ValueA.class, name = "valuea"),@Type(value = ValueB.class, name = "valueb")})
    public void setValue(Value value) {
        this.value = value;
    }

    public Value getValue() {
        return value;
    }

}

In this way. @JsonTypeInfo in fact define how to rely on “type” to dynamically  serialize and de-serialize the json/java object.

To make this properly work, your Jackson version must higher than the 2.5 version. As I have faced the duplicated fields bug in the 2.5  version Jackson , here is sample dependency:

 

        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.8.0</version>
        </dependency>

 

 

Spring MVC + Security Note (4) – Migrate from Spring Security 3.X to 4.X

By W.ZH

Refer to this link

http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-xml.html

Here are some notes that common change in page or configuration files:

  1. default URL for login and logout form action are changed:
    /j_spring_security_logout     to /logout
    /j_spring_security_check     to /login
    So we need to change our JSP content for these two.
  2. Default csrf is enabled… so if you want to be disabled, just add this
    <csrf disabled=”true”/> in the <http />tag
    If you want enable, you do not need to do sth.
  3. <access-denied-handler error-page=”/page/403″ />  put inside the <http />
    not like old on as the attribute <http *** />
  4. If you have more than one roles, you can not use the
    access=”ROLE_USER,ROLE_ADMIN”  any more, you have to change to<http auto-config=”true” use-expressions=”true” >
    …..
    <intercept-url  ……. access=”hasAnyRole(‘ROLE_USER’,’ROLE_ADMIN’)” …..you maybe have others need changes, need to refer to reference page

Spring MVC + Security Note (3) – About the Role Name

By WZH.

In the last two examples we see that user has a role define called ROLE_USER

<user     name=”mkyong”
password=”123456″
authorities=”ROLE_USER” />

<intercept-url pattern=”/admin**” access=”ROLE_USER” />

 

You should note that ROLE_USER here is a string only.  Just need to matched the authorities later in the  Authentication part.  Authentication provider give this role and it matched with the login part request, then this role will will assigned to this principle after authentication.

You can define any role by your self. Only ROLE_ANONYMOUS is a predefined role name in the spring security to an anonymous user.

Inside Spring, the default AccessDecisionManager (which interprets the access attributes that you specify in the intercept-url element) uses a RoleVoter implementation. By default this looks for the prefix “ROLE_” on the attribute, so your best option is to make sure that your roles have this prefix.

If you want use another prefix, . eg AAA_USER, you have to define a custom AppVoter:

<bean class=”org.springframework.security.vote.RoleVoter”>
<property name=”rolePrefix” value=”AAA”/>
</bean>
you need to read more on how to do this thing.

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

By WZH. Aug 2016

We want authenticate user against with a DB rather from a hard code user service with username and password. So system can work like a production system.

Refer to this article and its code:

http://www.mkyong.com/spring-security/spring-security-form-login-using-database/

Here is key points:

        <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource"
                users-by-username-query=
                    "select username,password, enabled from users where username=?"
                authorities-by-username-query=
                    "select username, role from user_roles where username =?  " />
        </authentication-provider>

users-by-username-query and users-by-username-query are two queries that to get user + password   and user + role from DB.  You do not need to use exactly field names for these in DB define , but SQL return data should there 3 items in order for users and 2 items in order for authorities.

After you make this part correct and your DB ok, you should be able to implement authenticate from DB easily. But this password could be clear pass save in DB.

Question – what if your password field is MD5 of real password in DB to ensure security how to do it in Spring security?

Let me show you one example that what you should do, add this line at <authentication-provider> first:

<authentication-provider>
            <password-encoder hash="md5"/>
            <jdbc-user-service data-source-ref="mySQLDataSource"
                users-by-username-query=
                    "select loginId, password, true from users where loginId=?"
                authorities-by-username-query=
                    "select loginId, authority from user_roles where loginId =?  " />
        </authentication-provider>

<password-encoder hash=”md5″/> will tell spring security that password read from DB is MD5 hash.  So spring will compare MD5 of the LoginForm input password with the MD5 read out from DB to do the authentication. But when you create/ register a new user into your DB, you need to calculate the MD5 by your code. You have to ensure your MD5 hash result is same with Spring.

Remember this piece of code:

PasswordEncoder encoder = new Md5PasswordEncoder();
String hashedPass = encoder.encodePassword("origClearPassword", null);

then you can save hashedPass to you DB as the “password” for hashed authentication.

 

Refer too:

http://docs.spring.io/spring-security/site/docs/3.0.x/reference/appendix-namespace.html