SEC-3076: Add Method Level Security Meta Annotations

This commit is contained in:
Rob Winch 2015-08-19 15:39:15 -05:00
parent 7708129aad
commit cbed1d75ee
11 changed files with 358 additions and 1 deletions

View File

@ -0,0 +1,39 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
/**
* @author Rob Winch
*
*/
public class Contact {
private String name;
/**
* @param name
*/
public Contact(String name) {
super();
this.name = name;
}
/**
* @return the name
*/
public String getName() {
return name;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* @author Rob Winch
*
*/
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#contact.name == authentication.name")
public @interface ContactPermission {}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* @author Rob Winch
*
*/
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN')")
public @interface PreAuthorizeAdminRole {
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
/**
* @author Rob Winch
*
*/
public class PreAuthorizeServiceImpl {
@PreAuthorizeAdminRole
public void preAuthorizeAdminRole() {}
@ContactPermission
public void contactPermission(Contact contact) {}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* @author Rob Winch
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class PreAuthorizeTests {
@Autowired
PreAuthorizeServiceImpl service;
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
@Test(expected = AccessDeniedException.class)
public void preAuthorizeAdminRoleDenied() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER"));
service.preAuthorizeAdminRole();
}
@Test
public void preAuthorizeAdminRoleGranted() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN"));
service.preAuthorizeAdminRole();
}
@Test
public void preAuthorizeContactPermissionGranted() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN"));
service.contactPermission(new Contact("user"));
}
@Test(expected = AccessDeniedException.class)
public void preAuthorizeContactPermissionDenied() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN"));
service.contactPermission(new Contact("admin"));
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.annotation.Secured;
/**
* @author Rob Winch
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Secured("ROLE_ADMIN")
public @interface SecuredAdminRole { }

View File

@ -0,0 +1,26 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
/**
*
* @author Rob Winch
*
*/
public class SecuredServiceImpl {
@SecuredAdminRole
public void securedAdminRole() {}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.method;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* @author Rob Winch
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SecuredTests {
@Autowired
SecuredServiceImpl service;
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
@Test(expected = AccessDeniedException.class)
public void securedAdminRoleDenied() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER"));
service.securedAdminRole();
}
@Test
public void securedAdminRoleGranted() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_ADMIN"));
service.securedAdminRole();
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<global-method-security pre-post-annotations="enabled"/>
<b:bean class="org.springframework.security.config.method.PreAuthorizeServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<global-method-security secured-annotations="enabled"/>
<b:bean class="org.springframework.security.config.method.SecuredServiceImpl"/>
</b:beans>

View File

@ -369,7 +369,9 @@ This will give you access to the entire project history (including all releases
[[new]]
== What's new in Spring Security 4.1
* <<test-method-meta-annotations>>
* Meta Annotation Support
** <<test-method-meta-annotations>>
** <<method-security-meta-annotations>>
=== What's new in Spring Security 4.0
@ -4727,6 +4729,29 @@ To use `hasPermission()` expressions, you have to explicitly configure a `Permis
Where `myPermissionEvaluator` is the bean which implements `PermissionEvaluator`. Usually this will be the implementation from the ACL module which is called`AclPermissionEvaluator`. See the "Contacts" sample application configuration for more details.
===== Method Security Meta Annotations
You can make use of meta annotations for method security to make your code more readable.
This is especially convenient if you find that you are repeating the same complex expression throughout your code base.
For example, consider the following:
[source,java]
----
@PreAuthorize("#contact.name == authentication.name")
----
Instead of repeating this everywhere, we can create a meta annotation that can be used instead.
[source,java]
----
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#contact.name == authentication.name")
public @interface ContactPermission {}
----
Meta annotations can be used for any of the Spring Security method security annotations.
In order to remain compliant with the specification JSR-250 annotations do not support meta annotations.
[[advanced-topics]]
= Additional Topics
In this part we cover features which require a knowledge of previous chapters as well as some of the more advanced and less-commonly used features of the framework.