Spring Security Method Level Annotations Example
This tutorial demonstrates how to use Spring Security Method Level Annotations. We can use Spring Security to secure our service layer. We can restrict which roles are able to execute a method by annotating the method with any of spring security annotations or the standard java JSR-250 annotaitons.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.memorynotfound.spring.security</groupId> <artifactId>method-level-security</artifactId> <version>1.0.0-SNAPSHOT</version> <url>http://memorynotfound.com</url> <name>Spring Security - ${project.artifactId}</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.8.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> </dependency> <!-- testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Enable Method Level Security
By annotating the class with @EnableGlobalMethodSecurity, we can enable method level security using annotations. We can optionally configure which annotations we’ll allow. You can enable one of the following.
- securedEnabled – enables the spring @Secured annotation.
- jsr250Enabled – enables the JSR-250 standard java security annotations.
- prePostEnabled – enables the spring @PreAuthorize and PostAuthorize annotations.
package com.memorynotfound.spring.security.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableGlobalMethodSecurity( securedEnabled = true, jsr250Enabled = true, prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.httpBasic(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user").password("password").roles("USER") .and() .withUser("admin").password("password").roles("ADMIN"); } }
The equivalent Spring Security XML Configuration spring-security-config.xml is located in the src/main/resources folder.
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled"/> <http> <http-basic/> </http> <authentication-manager> <authentication-provider> <user-service> <user name="user" password="password" authorities="ROLE_USER" /> <user name="manager" password="password" authorities="ROLE_MANAGER" /> </user-service> </authentication-provider> </authentication-manager> </beans:beans>
Spring Security Secured Annotations
Adding an annotation to a method (on a class or interface) limits the access to that method accordingly. Here we used the @Secured annotation.
package com.memorynotfound.spring.security.web; import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("book") public class BookController { @GetMapping("anonymous") @Secured("ROLE_ANONYMOUS") public String anonymously() { return "Hello, World!"; } @GetMapping("has-role") @Secured("ROLE_ADMIN") public String hasRole() { return "Hello, World!"; } }
Spring Security JSR-250 Annotations
Adding an annotation to a method (on a class or interface) limits the access to that method accordingly. Here we used the @PermitAll and @RolesAllowed annotations.
package com.memorynotfound.spring.security.web; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.security.PermitAll; import javax.annotation.security.RolesAllowed; @RestController @RequestMapping("/user") public class UserController { @GetMapping("anonymous") @PermitAll public String anonymously() { return "Hello, World!"; } @GetMapping("has-role") @RolesAllowed({"ROLE_ADMIN"}) public String hasRole() { return "Hello, World!"; } }
Spring Security Pre Post Annotations
Adding an annotation to a method (on a class or interface) limits the access to that method accordingly. Here we used the @PreAuthorize annotation.
package com.memorynotfound.spring.security.web; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("bank") public class BankController { @GetMapping("anonymous") @PreAuthorize("permitAll()") public String anonymously() { return "Hello, World!"; } @GetMapping("has-role") @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") public String hasRole() { return "Hello, World!"; } }
Demo
Access http://localhost:8080/bank/has-role with user: user.
Access http://localhost:8080/bank/anonymous with user: user.
Spring Security Method Level Annotations Integration Test
We can use MockMvc to write some spring integration tests.
package com.memorynotfound.spring.security.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc @RunWith(SpringJUnit4ClassRunner.class) public class BasicAuthenticationIntegrationTests { @Autowired private MockMvc mockMvc; @Test public void accessAnonymous() throws Exception { this.mockMvc.perform( get("/bank/anonymous") .with(httpBasic("user", "password"))) .andExpect( status().isOk()); } @Test public void accessRoleProtected() throws Exception { this.mockMvc.perform( get("/bank/has-role") .with(httpBasic("user", "password"))) .andExpect( status().is4xxClientError()); } }
Spring Security Integration Test Results
Download
From:一号门
COMMENTS