What is Spring Security?
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applicationsWhat is @Secured annotation?
Spring Security is one of the most mature and widely used Spring projects. Founded in 2003 and actively maintained by SpringSource since, today it is used to secure numerous demanding environments including government agencies, military applications and central banks. It is released under an Apache 2.0 license so you can confidently use it in your projects.
Source: Spring Security
From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. It provides support for JSR-250 annotation security as well as the framework's original @Secured annotation.@Secured annotation allows you to put restrictions in your methods. For example, you can authorize a get() method to be accessible by all registered users. But for the edit() method, you can mark it be accessible by admins only.
Source: Spring Security 3.1 Reference 2.4 Method Security
We'll build first the Spring MVC part of our application, then add Spring Security. Our application has an admin and common pages. It also has a view to show all persons. Only admins can edit a person's record.
Let's declare our primary controller first.
MainController
This controller declares two mappings:
/main/common - any registered user can access this page /main/admin - only admins can access this pageEach mapping will resolve to a specific JSP page. The common JSP page is accessible by everyone, while the admin page is accessible only by admins. Right now, everyone has access to these pages because we haven't enabled Spring Security yet.
Notice we've annotated each method with @Secured annotation. For the getAdminPage(), we put
@Secured("ROLE_ADMIN")and for the getCommonPage(), we put
@Secured("ROLE_USER"). Once we activated Spring Security these annotations will take effect.
Here are the JSP pages:
commonpage.jsp
adminpage.jsp
We've finished setting-up the primary controller and the associated JSP views. Now, we add the required XML configurations to enable Spring MVC and Spring Security at the same time.
We start by adding the web.xml:
web.xml
Notice the url-patterns for the DelegatingFilterProxy and DispatcherServlet. Spring Security is placed at the root-path
/*Whereas, Spring MVC is placed at a sub-path
/krams/*
We also referenced two important XML configuration files:
spring-security.xml applicationContext.xml
spring-security.xml contains configuration related to Spring Security.
spring-security.xml
The elements are self-documenting. If you're using an IDE, like Eclipse or STS, try pointing your mouse to any of these elements and you will see a short description of the element.
Notice that the bulk of the security configuration is inside the http element. Here's what we observe:
1. We enabled @Secured annotation by adding the global-method-security tag:
2. We declared the denied page URL in the
access-denied-page="/krams/auth/denied"3. We declared the login URL
login-page="/krams/auth/login"4. We declared the login failure URL
authentication-failure-url="/krams/auth/login?error=true"5. We declared the URL where the user will be redirected if he logs out
logout-success-url="/krams/auth/login"6. We declared the logout URL
logout-url="/krams/auth/logout"7. We declared a default authentication-manager that references an in-memory user-service
8. We declared an in-memory user-service:
This basically declares two users with corresponding passwords and authorities. This user-service is a good way to prototype and test a Spring Security application quickly.
9. We also declared an Md5 password encoder:
When a user enters his password, it's plain string. The password we have in our database (in this case, an in-memory lists) is Md5 encoded. In order for Spring to match the passwords, it's need to encode the plain string to Md5. Once it has been encoded, then it can compare passwords.
Compare this configuration with the one declared in Spring Security 3 - MVC: Using a Simple User-Service Tutorial. Our current configuration is missing the following declaration:
We don't need to declare those intercept-urls anymore because we've already annotated the methods that corresponds to those URLs. You can actually mix them if you need to.
Now we need to create a special controller that handles the login and logout requests.
LoginLogoutController
This controller declares two mappings:
/auth/login - shows the login page /auth/denied - shows the denied access pageEach mapping will resolve to a specific JSP page.
Here are the JSP pages:
loginpage.jsp
deniedpage.jsp
That's it. We got a working Spring MVC 3 application that's secured by Spring Security. We've managed to build a simple and quick Spring Security configuration.
To access the common page, enter the following URL:
http://localhost:8080/spring-security-annotation-secured/krams/main/commonTo access the admin page, enter the following URL:
http://localhost:8080/spring-security-annotation-secured/krams/main/adminTo login, enter the following URL:
http://localhost:8080/spring-security-annotation-secured/krams/auth/loginTo logout, enter the following URL:
http://localhost:8080/spring-security-annotation-secured/krams/auth/logoutBut there's more! Notice we annotated the methods from our controller. What will happen if we annotate the methods of our services instead?
To explore this inquiry, we will provide a hypothetical business scenario. Our client needs to view a list of persons. All registered users can view this list, but only admins can edit it.
We start by declaring a controller.
PersonController
In this controller we have three mappings:
/main - retrieve all persons /main/edit/{id} - (GET) retrieve and edit a person by his id /main/edit/{id} - (POST) save a person based on his idNotice we have two /main/edit/{id}. How does our controller know which one to call? The controller's @RequestMapping doesn't just rely on the mapping value but it also uses the method type. In our case, it's either POST or GET. The GET method is used when we retrieve a page; whereas, the POST method is used when we're submitting a form. For more info, please check the following blog from SpringSource Annotated Web MVC Controllers in Spring 2.5.
Also, we're using a special identifier in the mappings. We have declared a {id} in the path, and referenced that as @PathVariable in the method parameter. This is a URI template, one of the RESTful features of Spring 3 MVC.
What is a URI template?
A URI template is a URI-like string, containing one or more variable names. When these variables are substituted for values, the template becomes a URI. For more information, see the proposed RFC.For a thorough description of this subject, please visit the blog from SpringSourceREST in Spring 3: @MVC
Source: REST in Spring 3: @MVC
It's worth noting that we did not annotate this controller with @Secured annotation.
Let's examine the associated JSP view for each mappings.
personspage.jsp
This is referenced by the mapping /persons.
To access the persons page, type the following URL in the browser:
http://localhost:8080/spring-security-annotation-secured/krams/persons
editpage.jsp
This is referenced by the mapping /persons/edit/{id} (GET)
To access the edit page, we need to manually type the following URL in the browser:
http://localhost:8080/spring-security-annotation-secured/krams/persons/edit/2Just make sure to change the number to match the id that you want to edit.
Now let's define our service. Remember we referenced a PersonService in the PersonController.
PersonService
Pay attention to the methods. We've annotated the methods with @Secured.
getAll() and get() has been assigned with ROLE_USER. This means all registered users can access these methods.
edit() has been assigned with ROLE_ADMIN. This means only admins can access this method.
If you need to test these methods, try logging-in as john (password is admin) and as jane (password is user). Both users can view the list of persons. Both users can access the edit page. But only john can do a successful edit, while jane will get the following message
HTTP Status 405 - Request method 'POST not supported'
The best way to learn further is to try the actual application.
Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring3-security-mvc-integration-tutorial/
You can download the project as a Maven build. Look for the spring-security-annotation-secured.zip in the Download sections.
You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
Share the joy:
|
Subscribe by reader Subscribe by email Share
This comment has been removed by the author.
ReplyDeleteThere are two main approaches to query for logged-in users using Spring Security MVC:
Delete1. Using getAllPrincipals (Limited Approach):
Method: The SessionRegistry interface provides a getAllPrincipals() method that retrieves a collection of all principals (usually usernames) associated with active sessions.
Limitation: This approach only returns usernames, not detailed user information. Additionally, it might not reflect expired sessions if setRemoveExpiredSessions(false) is configured.
Custom User Tracking (More Control):
Concept: Implement a custom mechanism to store user information alongside session data. This could involve extending the SessionInformation class or using a separate data store (e.g., database) to track active user sessions.
Flexibility: This approach offers more control over the data you store and retrieve about logged-in users.
Implementation:
Cloud Security Projects For Final Year
cyber security projects for Final Year
Information Security Projects For Final Year Students
Create a custom class to hold user information along with session details (e.g., username, last access time).
Modify your SessionRegistry implementation or leverage a separate data store to manage these custom session objects.
Develop logic in your controllers or services to retrieve and manage user session data based on your custom implementation.
Hello, I have seen the list of your tutorials. I thing that liked specifically in your blog are the examples with most updated releases of spring which is the required thing for someone who is trying now.
ReplyDeleteI tried to replicate the code as yours in my app with a difference of "Only one method with @Secured("ROLE_USER") and there are no Roles specified for other methods. But when i am accessing that secured method...Spring Security is not stopping me for login....in my logs i guess i see some related things as below...
DEBUG [org.springframework.security.web.FilterChainProxy] - Candidate is: '/test-app/...../example.xls'; pattern is /**; matched=true
Irrespective of the requested pattern, it is taking only the above one.
DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] - Public object - authentication not attempted
can you pls suggest what i am missing to make it work ?
thanks,
kandaala
I got my problem solved by adding the
ReplyDeletesecurity:global-method-security secured-annotations="enabled"
in my app-servlet.xml
Thank you very much for your article.
Thank you for an excellent article. I applied the techniques here to a simple Spring / Hibernate application of my own design. My application works perfectly in the case of @Secured on controller methods, but not on service methods.
ReplyDeleteSpecifically -
if I put an @Secured on a delete method of a controller class, I get the correct 405 response if the user does not have the named role.
if I put an @Secured on a delete method of a service class, then no errors are given and no transaction takes place (Hibernate does not issue the any SQL) for users of ANY role.
Have you any idea why this would be? I'm stuck!
Bruno.
Hi,
ReplyDeleteI got working this example. I am facing one problem. I logged in with user 'jane' still I can access "http://localhost:8080/spring-security-integrationkrams/main/admin" page. @secure doesn't work for me in controller. Please help.
Can I use this on android platform ?
ReplyDeleteWhat's the diffrents with @Secured and @PreAuthorize ?
ReplyDeleteThanks,
ReplyDeleteVery good work.
Thanks for your great tutorials!
ReplyDeleteIt seems like every time I have a problem with Spring I find the solution here.
And your examples always work. Amazing!
Hi,
ReplyDeleteThanks for helping us, you examples are excellent.
I login as :
username : jane
password : user
the when I typed in the browser :
http://localhost:8080/spring-security-annotation-secured/krams/main/admin
I can see the admin page :Admin Page once :
Only admins have access to this page.
Curabitur quis libero elit, dapibus iaculis nisl. Nullam quis velit eget odio adipiscing tristique non sed ligula. In auctor diam eget nisl condimentum laoreet..
But when I refresh the page I got :
Access Denied!
Only admins can see this page!
I am wondering where is the security here if you login as a regular user and can see the admin page once.
please tell me , should I change something in the code or configuarion files or what ?
thanks,
your help is appreciated.
Very nice article
ReplyDeleteorg.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 16 in XML document from ServletContext resource [/WEB-INF/spring-security.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 16; columnNumber: 67; cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'security:global-method-security'.
ReplyDeleteat org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:396)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:93)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4791)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Kindly let me know how to resolve this.
Am using Tomcat and Eclipse.
make sure you have com.springsource.org.aopalliance-1.0.0.jar in your lib or in your class path. This jar you have to download it ,if it is not there, as it dose not come with spring bundle.
DeleteI added the said jar but still am facing the same issue...
Deleteerror
ReplyDeleteHello,
ReplyDeleteI tried to replicate the code as yours in my app for a different method with @Secured("ROLE_BRANCHUSER") and there are no Roles specified for other methods. But when i am accessing that secured method...Spring Security is not stopping me for accessing that method ....
@secure doesn't work for me in controller.
Please help.
Hi Krams,
ReplyDeleteCongrats for your article about security mechanisms. I adapted this concept to my existing app. In my case I used annotations config instead XML, but no problem it's working perfectly.
My question is conceptual. I observed the @Secured annotation is based in ROLES of users. Is it possible to implement the @Secured in authorities list instead ROLES? I have a group of authorites for each ROLE, and I need to use the authorities list in Controller the same way you do with users ROLES. Some idea?
Cheers!
Hi Krams - You're great. Your tutorials are very awesome. I follow your tutorials in my project. However I'd like you also develop the tutorials on
ReplyDelete1) micro-services
2) spring boot
3) Spring Cloud etc
I have read your blog its very attractive and impressive. I like it your blog.
ReplyDeleteSpring online training Spring online training Spring Hibernate online training Spring Hibernate online training Java online training
spring training in chennai spring hibernate training in chennai
Nice looking sites and great work. Pretty nice information. it has a better understanding. thanks for spending time on it.
ReplyDeleteBest BCA Colleges in Noida
nike off white
ReplyDeleteggdb outlet
jordan 13
jordan outlet
yeezy gap hoodie
jordan 12
off white t shirt
off white
golden goose
gap yeezy