Most of projects need security to check logged in user and its roles to access to different parts of an application. In Spring MVC(Simple CRUD) and Spring MVC-View post we could create a simple CRUD usecase. Here I am going to make a simple security with customary Roles.
Spring has a powerfull security framework that can commiunicate usual security systems like CAS, LDAP and so on. Here I will create a simple security system including authentication and authorization.
the usecase senario is:
- An anonymous user first visit the home page. it can log in to the system
- In log in page user should enter its username and password.
- After login user can see a list of system user and it can change the list depending on its roles:
- administrator: it can add, edit, delete the records. and also make a user expired or disabled and so on.
- poweruser: it can add and view users. but it can not make user expired or disabled and so on.
- user: it can just view users.
- It can log out.
- The log out page will be displayed.
- It can go back to home page(refer 1).
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-servlet.xml"/>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!--security config-->
<import resource="classpath:security-config.xml"/>
...
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<http use-expressions="true" auto-config="true">
<intercept-url pattern="/user/**" access="isAuthenticated()"/>
<intercept-url pattern="/**" access="permitAll"/>
<form-login authentication-success-handler-ref="loginSuccess"/>
<logout logout-success-url="/home/logout.html"/>
<remember-me/>
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management>
</http>
<beans:bean id="loginSuccess" class="com.security.LoginSuccessHandler"/>
<beans:bean id="userDetailsService" class="com.security.UserSecurityService"/>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="md5"/>
</authentication-provider>
</authentication-manager>
</beans:beans>
- global-method-security: we can use spring security annotation(@PreAuthorize and @PostAuthorize) by this tag.
- intercept-url: we can check security in any url patterns by this tag.
- form-login: we can make a custom login form or use default spring security login form. in this sample we use the default spring security form. But you can get authentication success handler class to set general attribute when a user successfully logs in. For example setting current user theme. Here we create "com.security.LoginSuccessHandler" class to get the loggin success handler.
- logout: you can set logout url and logout from via this tag.
- authentication-provider: we can determine which authentication-providers should authenticate the user. we can use different providers like: CAS, LDAP and so on. Here we use implicit provider: "com.security.UserSecurityService". This class should implements "UserDetailsService".
- password-encoder: we can determine password encoder policy via this tag. we can use hash encoder like: MD4, MD5, SHA, ... and also Base64.
- Role: implements GrantedAuthority (needed for spring security)
- User: implements UserDetails (needed for spring security)
UserSecurityService class should implements loadUserByUsername method. so in this method i use userService calls to fetch a user from database by its user name:
public class UserSecurityService implements UserDetailsService {
@Autowired
UserService userService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
return userService.findByUsername(username);
}
}
this was the authentication step.
the authorization step will be check in different levels:
1. action url
in security-config.xml file we can use "intercept-url" to make and access for each url. for example:
<intercept-url pattern="/user/**" access="hasRole('administrator')"/>
2. code
we can use @PreAuthorize or @PostAuthorize annotation on top of a method or we can check a role manually by use of SecurityContextHolder class.
here is annotation sample:
@PreAuthorize("hasRole('administrator') or hasRole('power_user')")
public void saveOrUpdate(User user, List<Long> roleIds) {
...
}
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
for(Role role : user.getRoleList()){
if(role.getAuthority().equals("administrator") ||
role.getAuthority().equals("power_user")
)
return true;
}
return false;
there are some tag libraries to in "http://www.springframework.org/security/tags". we can use them to check authentication and authorization and also display current user and current user access list:
- security:authorize: A tag which outputs the body of the tag if the configured access expression
evaluates to true for the currently authenticated principal. - authentication: Allows access to the current Authentication object.
- accesscontrollist: Allows inclusion of a tag body if the current Authentication
has one of the specified permissions to the presented
domain object instance.
<security:authorize access="hasRole('administrator')">
<input type="submit" value="edit"
onclick="setUserId(${user.id}); setFormAction('<spring:url value="/user/edit.html"/>');"/>
</security:authorize>
<security:authorize access="isAuthenticated()">
<a href="/j_spring_security_logout">logout</a> welcome <security:authentication property="name"/>
</security:authorize>
now its the time to download the sample source code and take a tour in it yourself. in this sample I imported spring security jar files. But other needed Spring jar files are available in sample of Spring MVC(Simple CRUD) post. after running the sample, the first page you can see is: http://localhost:8080/home/view.html
To create the needed databse schema, there is a mysql script in [sample root]/db/security.sql file. you can restore it in you mysql server. The datasource properties are available in [sample root]/src/database.properties file. after you restore the script, a single user with role of administrator will be created. its specification is:
username:administrator
password:123456
you can run the sample and log in by this user then create more users with different roles.
Enjoy yourselves.
all rights reserved by Mostafa Rastgar and Programmer Assistant weblog
1 comment:
thanks for giving the nice example, but i have facing the configuration problem spring-servlet is not configured please help me how to solve the problem
Post a Comment