#21 - Added example for Spring Data REST and Spring Security.

Added example of how to secure a Spring Data REST project with Spring Security both on the method level as well as the URI level.

Original pull request: #22.
This commit is contained in:
Greg Turnquist
2014-10-10 11:40:08 -05:00
committed by Oliver Gierke
parent f4266ac211
commit c5920a64d9
13 changed files with 946 additions and 1 deletions

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2014 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 example.company;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* This example shows various ways to secure Spring Data REST applications using Spring Security
*
* @author Greg Turnquist
*/
@ComponentScan
@EnableAutoConfiguration
public class Application {
@Autowired ItemRepository itemRepository;
@Autowired EmployeeRepository employeeRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
/**
* Pre-load the system with employees and items.
*/
@PostConstruct
public void init() {
employeeRepository.save(new Employee("Bilbo", "Baggins", "thief"));
employeeRepository.save(new Employee("Frodo", "Baggins", "ring bearer"));
employeeRepository.save(new Employee("Gandalf", "the Wizard", "servant of the Secret Fire"));
/**
* Due to method-level protections on {@link example.company.ItemRepository}, the security context must be loaded
* with an authentication token containing the necessary privileges.
*/
SecurityUtils.runAs("system", "system", "ROLE_ADMIN");
itemRepository.save(new Item("Sting"));
itemRepository.save(new Item("the one ring"));
SecurityContextHolder.clearContext();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2014 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 example.company;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.RequiredArgsConstructor;
/**
* Domain object for an employee.
*
* @author Greg Turnquist
*/
@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@RequiredArgsConstructor
public class Employee {
@Id @GeneratedValue private Long id;
private final String firstName, lastName, title;
Employee() {
this.firstName = null;
this.lastName = null;
this.title = null;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014 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 example.company;
import org.springframework.data.repository.CrudRepository;
/**
* This repository has no method-level security annotations. That's because it's secured at the URL level inside
* {@link example.company.SecurityConfiguration}.
*
* @author Greg Turnquist
*/
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2014 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 example.company;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Data;
import lombok.RequiredArgsConstructor;
/**
* Domain object for an item managed by the company.
*
* @author Greg Turnquist
*/
@Entity
@Data
@RequiredArgsConstructor
public class Item {
@Id @GeneratedValue private Long id;
private final String description;
Item() {
this.description = null;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2014 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 example.company;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* This repository shows interface and method-level security. The entire repository requires ROLE_USER, while certain
* operations require ROLE_ADMIN.
*
* @author Greg Turnquist
*/
@PreAuthorize("hasRole('ROLE_USER')")
public interface ItemRepository extends CrudRepository<Item, Long> {
@PreAuthorize("hasRole('ROLE_ADMIN')")
@Override
Item save(Item s);
@PreAuthorize("hasRole('ROLE_ADMIN')")
@Override
void delete(Long aLong);
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2014 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 example.company;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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;
/**
* This application is secured at both the URL level for some parts, and the method level for other parts. The URL
* security is shown inside this code, while method-level annotations are enabled at by
* {@link org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
* EnableGlobalMethodSecurity}
*
* @author Greg Turnquist
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/**
* This section defines the user accounts which can be used for authentication as well as the roles each user has.
*
* @param auth
* @throws Exception
*/
@Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("greg").password("turnquist").roles("USER").and()
.withUser("ollie").password("gierke").roles("USER", "ADMIN");
}
/**
* This section defines the security policy for the app. - BASIC authentication is supported (enough for this
* REST-based demo) - /employees is secured using URL security shown below - CSRF headers are disabled since we are
* only testing the REST interface, not a web one. NOTE: GET is not shown which defaults to permitted.
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/employees").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/employees/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/employees/**").hasRole("ADMIN")
.and()
.csrf().disable();
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2014 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 example.company;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Some convenient security utilities.
*
* @author Greg Turnquist
*/
public class SecurityUtils {
public static void runAs(String username, String password, String... roles) {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(username, password, AuthorityUtils.createAuthorityList(roles)));
}
}