Spring framework has made securing your application so much easy that you only need to use some basic configurations CORRECTLY, and that’s it !! This security can be applied to multiple levels in your web application. Spring’s basic support is for these levels:
- URL level security
- Method level security
- Entity or Object level security
In this Spring security tutorial, learn to apply method security using annotations such as @PreAuthorize
and @Secured
.
Enable @Secured and @PreAuthorize
In the core of method level security is the configuration element “<global-method-security/>“. This needs to be defined inside your spring’s configuration file. This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element). You should only declare one <global-method-security/>
element. e.g.
<global-method-security pre-post-annotations="enabled" />
Above configuration will enable the @PreAuthorize
and @PostAuthorize
annotations in your code.
//OR
Another variation of above configuration is:
<global-method-security secured-annotations="enabled" />
This will enable the @Secured
annotation in your code.
These annotations take one string parameter which is either is role-name
or expression, and which one to use depends on your configuration for <http>
element’s use-expression
value.
If use-expression
is set to true, then you should use expressions inside the annotation otherwise role name should be used directly.
Expression-based annotations are a good choice if you need to define simple rules that go beyond checking the role names against the user’s list of authorities. You can enable more than one type of annotation in the same application, but you should avoid mixing annotations types in the same interface or class to avoid confusion.
Test Security Annotations
To test above annotations in running application, I am using the code base of previous tutorial related to login form based security.
This application is already secured for URL level security. Now, we will add support for method level security also.
Modify application-security.xml configuration
To enable support for method level security, I will update the application-security.xml
file with <global -method-security> tag as below:
<?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.3.xsd"> <global-method-security pre-post-annotations="enabled" /> <http auto-config="false" use-expressions="true"> <intercept-url pattern="/login" access="permitAll" /> <intercept-url pattern="/logout" access="permitAll" /> <intercept-url pattern="/accessdenied" access="permitAll" /> <intercept-url pattern="/**" access="hasRole('ROLE_USER')" /> <form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied" /> <logout logout-success-url="/logout" /> </http> <authentication-manager alias="authenticationManager"> <authentication-provider> <user-service> <user name="lokesh" password="password" authorities="ROLE_USER" /> <user name="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN" /> </user-service> </authentication-provider> </authentication-manager> <beans:bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl" /> <beans:bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl" /> </beans:beans>
Rest all the code is same as previous tutorial.
Annotate methods to be secured
In this tutorial, I want that user with role admin can only add an employee to employees collection. Rest other operations are allowed as before. To do so, I will annotate add method in EmployeeDaoImpl.java as below:
package com.howtodoinjava.dao; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Repository; import com.howtodoinjava.entity.EmployeeEntity; @Repository public class EmployeeDaoImpl implements EmployeeDAO { @Autowired private SessionFactory sessionFactory; @PreAuthorize("hasRole('ROLE_ADMIN')") @Override public void addEmployee(EmployeeEntity employee) { //System.out.println(((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getAuthorities()); this.sessionFactory.getCurrentSession().save(employee); } @SuppressWarnings("unchecked") @Override public List<EmployeeEntity> getAllEmployees() { return this.sessionFactory.getCurrentSession().createQuery("from Employee").list(); } @Override public void deleteEmployee(Integer employeeId) { EmployeeEntity employee = (EmployeeEntity) sessionFactory.getCurrentSession().load( EmployeeEntity.class, employeeId); if (null != employee) { this.sessionFactory.getCurrentSession().delete(employee); } } }
@PreAuthorize
annotation will test that if logged in used has the ‘ROLE_ADMIN
‘ authority or not. If user has not this authority, an access denied exception will be thrown.
Demo
Our application is configured and is ready to be deployed. So, lets do it !!
1) Hit the URL “http://localhost:8080/Spring3HibernateIntegration/login
” in browser window
A login window will appear because all the URLs are protected.
2) Login with username “lokesh
” and password “password
” and try to add an employee
A access denied exception will be thrown as lokesh does not have admin permission.
3) Login with username “admin
” and password “password
” and try to add an employee
Admin will be able to add employee because he has ‘ROLE_ADMIN
‘ assigned to him.
Let me know if you have any issue in running the application.
Happy Learning !!
Thank you!
Can u suggest how to use database MySQL for login access instead using XML based services?
Thanks in advance
Try code given in this link for custom user details service.
Hi Lokesh, your blog is really helpful.
Can you redirect me where can i find tuotrial on spring batchs??
So far, I have not covered spring batch.
Hello Lokesh,
I have a requirement to implement Spring ACL security with these steps
1. Annotate methods
2. Load user security context with ACL information
3. Use custom LookupStrategy to read ACL information from user security
context and perform ACL validation
Moreover I want to use user and useraccess(with fields UserId,AllowRead,AllowWrite,AllowExecute,Denied,Project,Client,Environment) table of my database instead of using spring ACLS table.
I am stuck in this and not getting any help anywhere .Is this possible/relevant to do ?
I will write an example on it asap.
For people with with the downloaded source code who want to set up the database, here is the SQL:
CREATE DATABASE `dbo` /*!40100 DEFAULT CHARACTER SET utf8 */;
use dbo;
CREATE TABLE `employee` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`FIRSTNAME` varchar(30) DEFAULT NULL,
`LASTNAME` varchar(30) DEFAULT NULL,
`TELEPHONE` varchar(15) DEFAULT NULL,
`EMAIL` varchar(30) DEFAULT NULL,
`CREATED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Also jdbc.properties may need the “com.mysql.jdbc.Driver” half uncommented. And you need “jdbc.databaseurl=jdbc:mysql://127.0.0.1:3307/” (Delete “EmployeeDatabase”).
Thanks for sharing.
Lokesh, I moved the annotation “PreAuthorize” into handler, why it doesn’t work? Thanks
Are you creating handler with Spring? If spring context creates handler for you, then it should be called. If handler is not created and not managed by spring context, then method will not be invoked.
sorry, I mean “controller”, not “handler”.
I put my security configuration into child context, so it doesn’t work.
I got the solution from https://stackoverflow.com/questions/3652090/difference-between-applicationcontext-xml-and-spring-servlet-xml-in-spring-frame?lq=1.
Thanks for your reply.
Can we do the same thing without using @PreAuthorize or @Secured annotation. I mean using xml based confiuration
You can use pointcut expressions inside your <global-method-security> tag in your XML configuration. This will also let you match multiple methods with a regex, if you need to.
<global-method-security>
<protect-pointcut expression=”execution(* com.yourpackage.IHelloController.welcome(..))” access=”ROLE_ADMIN”/>
</global-method-security>
I see a lot of pieces missing in here. empty placeholders
Thanks for pointing it out. I corrected it. Formatting messed up during one of post update.