move security content from spring-security-rest-full (#2731)

* move security content from spring-security-rest-full

* swagger update
This commit is contained in:
Doha2012 2017-10-22 19:35:08 +03:00 committed by Grzegorz Piwowarek
parent e7252f61aa
commit 3ed7609758
41 changed files with 419 additions and 230 deletions

View File

@ -8,7 +8,10 @@ The "REST With Spring" Classes: http://github.learnspringsecurity.com
### Relevant Articles:
- [Spring Security Remember Me](http://www.baeldung.com/spring-security-remember-me)
- [Redirect to different pages after Login with Spring Security](http://www.baeldung.com/spring_redirect_after_login)
- [Changing Spring Model Parameters with Handler Interceptor](http://www.baeldung.com/spring-model-parameters-with-handler-interceptor)
- [Introduction to Spring MVC HandlerInterceptor](http://www.baeldung.com/spring-mvc-handlerinterceptor)
- [Using a Custom Spring MVCs Handler Interceptor to Manage Sessions](http://www.baeldung.com/spring-mvc-custom-handler-interceptor)
- [A Guide to CSRF Protection in Spring Security](http://www.baeldung.com/spring-security-csrf)
### Build the Project
```

View File

@ -113,6 +113,40 @@
<!-- <version>3.0.1</version> -->
<!-- </dependency> -->
<!-- util -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>${org.springframework.security.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -174,7 +208,8 @@
<!-- util -->
<guava.version>19.0</guava.version>
<commons-lang3.version>3.5</commons-lang3.version>
<jackson-databind.version>2.9.1</jackson-databind.version>
<httpclient.version>4.5.2</httpclient.version>
<httpcore.version>4.4.5</httpcore.version>

View File

@ -1,9 +1,14 @@
package org.baeldung.spring;
import org.baeldung.web.interceptor.LoggerInterceptor;
import org.baeldung.web.interceptor.SessionTimerInterceptor;
import org.baeldung.web.interceptor.UserInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@ -11,6 +16,7 @@ import org.springframework.web.servlet.view.JstlView;
@EnableWebMvc
@Configuration
@ComponentScan("org.baeldung.web.controller")
public class MvcConfig extends WebMvcConfigurerAdapter {
public MvcConfig() {
@ -28,6 +34,7 @@ public class MvcConfig extends WebMvcConfigurerAdapter {
registry.addViewController("/login.html");
registry.addViewController("/homepage.html");
registry.addViewController("/console.html");
registry.addViewController("/csrfHome.html");
}
@Bean
@ -40,4 +47,11 @@ public class MvcConfig extends WebMvcConfigurerAdapter {
return bean;
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
registry.addInterceptor(new UserInterceptor());
registry.addInterceptor(new SessionTimerInterceptor());
}
}

View File

@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
// to test csrf
@Controller
@RequestMapping(value = "/auth/")
public class BankController {
private final Logger logger = LoggerFactory.getLogger(getClass());

View File

@ -0,0 +1,59 @@
package org.baeldung.web.controller;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.baeldung.web.dto.Foo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.util.UriComponentsBuilder;
@Controller
@RequestMapping(value = "/auth/foos")
public class FooController {
@Autowired
private ApplicationEventPublisher eventPublisher;
public FooController() {
super();
}
// API
// read - single
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public Foo findById(@PathVariable("id") final Long id, final UriComponentsBuilder uriBuilder, final HttpServletResponse response) {
return new Foo(randomAlphabetic(6));
}
// read - multiple
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<Foo> findAll() {
return Arrays.asList(new Foo(randomAlphabetic(6)));
}
// write - just for test
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
@ResponseBody
public Foo create(@RequestBody final Foo foo) {
return foo;
}
}

View File

@ -0,0 +1,80 @@
package org.baeldung.web.dto;
import java.io.Serializable;
public class Foo implements Serializable {
private long id;
private String name;
public Foo() {
super();
}
public Foo(final String name) {
super();
this.name = name;
}
// API
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
//
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Foo other = (Foo) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Foo [name=")
.append(name)
.append("]");
return builder.toString();
}
}

View File

@ -33,4 +33,8 @@
</authentication-provider>
</authentication-manager>
<!-- <mvc:interceptors>
<bean id="loggerInterceptor" class="org.baeldung.web.interceptor.LoggerInterceptor" />
</mvc:interceptors> -->
</beans:beans>

View File

@ -5,7 +5,7 @@ import static org.springframework.security.test.web.servlet.request.SecurityMock
import javax.servlet.Filter;
import org.baeldung.persistence.model.Foo;
import org.baeldung.web.dto.Foo;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@ -14,15 +14,15 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@Transactional
public abstract class CsrfAbstractIntegrationTest {
@Autowired

View File

@ -0,0 +1,25 @@
package org.baeldung.security.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.baeldung.security.spring.SecurityWithoutCsrfConfig;
import org.baeldung.spring.MvcConfig;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class })
public class CsrfDisabledIntegrationTest extends CsrfAbstractIntegrationTest {
@Test
public void givenNotAuth_whenAddFoo_thenUnauthorized() throws Exception {
mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo())).andExpect(status().isUnauthorized());
}
@Test
public void givenAuth_whenAddFoo_thenCreated() throws Exception {
mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo()).with(testUser())).andExpect(status().isCreated());
}
}

View File

@ -5,13 +5,12 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.baeldung.security.spring.SecurityWithCsrfConfig;
import org.baeldung.spring.PersistenceConfig;
import org.baeldung.spring.WebConfig;
import org.baeldung.spring.MvcConfig;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = { SecurityWithCsrfConfig.class, PersistenceConfig.class, WebConfig.class })
@ContextConfiguration(classes = { SecurityWithCsrfConfig.class, MvcConfig.class })
public class CsrfEnabledIntegrationTest extends CsrfAbstractIntegrationTest {
@Test

View File

@ -1,8 +1,5 @@
package org.baeldung.security.spring;
import org.baeldung.web.error.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@ -12,14 +9,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableAutoConfiguration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityWithCsrfConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
public SecurityWithCsrfConfig() {
super();
}
@ -46,8 +39,6 @@ public class SecurityWithCsrfConfig extends WebSecurityConfigurerAdapter {
.and()
.httpBasic()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
.and()
.headers().cacheControl().disable()
;
// @formatter:on

View File

@ -1,8 +1,5 @@
package org.baeldung.security.spring;
import org.baeldung.web.error.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@ -12,16 +9,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableAutoConfiguration
//
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
// @ImportResource({ "classpath:webSecurityConfig.xml" })
public class SecurityWithoutCsrfConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
public SecurityWithoutCsrfConfig() {
super();
}
@ -42,18 +33,15 @@ public class SecurityWithoutCsrfConfig extends WebSecurityConfigurerAdapter {
protected void configure(final HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/auth/admin/*").hasRole("ADMIN")
.antMatchers("/auth/*").hasAnyRole("ADMIN","USER")
.antMatchers("/*").permitAll()
.antMatchers("/auth/admin/*").hasAnyRole("ROLE_ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
// .exceptionHandling().accessDeniedPage("/my-error-page")
.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
.and()
.headers().cacheControl().disable()
.and()
.csrf().disable()
;
// @formatter:on
}

View File

@ -4,8 +4,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.baeldung.security.spring.SecurityWithoutCsrfConfig;
import org.baeldung.spring.PersistenceConfig;
import org.baeldung.spring.WebConfig;
import org.baeldung.spring.MvcConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -16,13 +15,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@Transactional
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class })
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class })
public class LoggerInterceptorIntegrationTest {
@Autowired
@ -46,7 +43,8 @@ public class LoggerInterceptorIntegrationTest {
*/
@Test
public void testInterceptors() throws Exception {
mockMvc.perform(get("/graph.html")).andExpect(status().isOk());
mockMvc.perform(get("/login.html"))
.andExpect(status().isOk());
}
}

View File

@ -6,8 +6,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import javax.servlet.http.HttpSession;
import org.baeldung.security.spring.SecurityWithoutCsrfConfig;
import org.baeldung.spring.PersistenceConfig;
import org.baeldung.spring.WebConfig;
import org.baeldung.spring.MvcConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -20,13 +19,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@Transactional
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class })
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class })
@WithMockUser(username = "admin", roles = { "USER", "ADMIN" })
public class SessionTimerInterceptorIntegrationTest {
@ -47,9 +44,14 @@ public class SessionTimerInterceptorIntegrationTest {
*/
@Test
public void testInterceptors() throws Exception {
HttpSession session = mockMvc.perform(get("/auth/admin")).andExpect(status().is2xxSuccessful()).andReturn().getRequest().getSession();
HttpSession session = mockMvc.perform(get("/auth/foos"))
.andExpect(status().is2xxSuccessful())
.andReturn()
.getRequest()
.getSession();
Thread.sleep(51000);
mockMvc.perform(get("/auth/admin").session((MockHttpSession) session)).andExpect(status().is2xxSuccessful());
mockMvc.perform(get("/auth/foos").session((MockHttpSession) session))
.andExpect(status().is2xxSuccessful());
}
}

View File

@ -1,8 +1,10 @@
package org.baeldung.web.interceptor;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.baeldung.security.spring.SecurityWithoutCsrfConfig;
import org.baeldung.spring.PersistenceConfig;
import org.baeldung.spring.WebConfig;
import org.baeldung.spring.MvcConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -14,16 +16,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@Transactional
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class })
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, MvcConfig.class })
@WithMockUser(username = "admin", roles = { "USER", "ADMIN" })
public class UserInterceptorIntegrationTest {
@ -46,7 +43,8 @@ public class UserInterceptorIntegrationTest {
*/
@Test
public void testInterceptors() throws Exception {
mockMvc.perform(get("/auth/admin")).andExpect(status().is2xxSuccessful());
mockMvc.perform(get("/auth/foos"))
.andExpect(status().is2xxSuccessful());
}
}

View File

@ -1,18 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[3.8.2.201610040608-RELEASE]]></pluginVersion>
<pluginVersion><![CDATA[3.6.3.201411271034-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>java:org.baeldung.security.spring.SecurityWithoutCsrfConfig</config>
</configs>
<autoconfigs>
<config>src/main/webapp/WEB-INF/api-servlet.xml</config>
<config>java:org.baeldung.spring.Application</config>
<config>java:org.baeldung.security.spring.SecurityWithCsrfConfig</config>
</autoconfigs>
<configSets>
</configSets>

View File

@ -8,12 +8,10 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
### Relevant Articles:
- [Spring Security Expressions - hasRole Example](http://www.baeldung.com/spring-security-expressions-basic)
- [REST Pagination in Spring](http://www.baeldung.com/2012/01/18/rest-pagination-in-spring/)
- [HATEOAS for a Spring REST Service](http://www.baeldung.com/2011/11/13/rest-service-discoverability-with-spring-part-5/)
- [REST API Discoverability and HATEOAS](http://www.baeldung.com/2011/11/06/restful-web-service-discoverability-part-4/)
- [ETags for REST with Spring](http://www.baeldung.com/2013/01/11/etags-for-rest-with-spring/)
- [Error Handling for REST with Spring 3](http://www.baeldung.com/2013/01/31/exception-handling-for-rest-with-spring-3-2/)
- [Integration Testing with the Maven Cargo plugin](http://www.baeldung.com/2011/10/16/how-to-set-up-integration-testing-with-the-maven-cargo-plugin/)
- [Introduction to Spring Data JPA](http://www.baeldung.com/2011/12/22/the-persistence-layer-with-spring-data-jpa/)
- [Project Configuration with Spring](http://www.baeldung.com/2012/03/12/project-configuration-with-spring/)
@ -24,11 +22,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
- [Metrics for your Spring REST API](http://www.baeldung.com/spring-rest-api-metrics)
- [REST Query Language with RSQL](http://www.baeldung.com/rest-api-search-language-rsql-fiql)
- [Spring RestTemplate Tutorial](http://www.baeldung.com/rest-template)
- [A Guide to CSRF Protection in Spring Security](http://www.baeldung.com/spring-security-csrf)
- [Intro to Spring Security Expressions](http://www.baeldung.com/spring-security-expressions)
- [Changing Spring Model Parameters with Handler Interceptor](http://www.baeldung.com/spring-model-parameters-with-handler-interceptor)
- [Introduction to Spring MVC HandlerInterceptor](http://www.baeldung.com/spring-mvc-handlerinterceptor)
- [Using a Custom Spring MVCs Handler Interceptor to Manage Sessions](http://www.baeldung.com/spring-mvc-custom-handler-interceptor)
- [Bootstrap a Web Application with Spring 4](http://www.baeldung.com/bootstraping-a-web-application-with-spring-and-java-based-configuration)
- [REST Query Language Implementing OR Operation](http://www.baeldung.com/rest-api-query-search-or-operation)

View File

@ -38,17 +38,6 @@
<scope>provided</scope>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<!-- Spring -->
<dependency>
@ -220,10 +209,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
<!-- <dependency> -->
<!-- <groupId>org.hamcrest</groupId> -->

View File

@ -10,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -36,6 +35,7 @@ public class FooService extends AbstractService<Foo> implements IFooService {
// custom methods
@Override
public Foo retrieveByName(final String name) {
return dao.retrieveByName(name);
}
@ -44,7 +44,6 @@ public class FooService extends AbstractService<Foo> implements IFooService {
@Override
@Transactional(readOnly = true)
@PreAuthorize("hasRole('ROLE_ADMIN')")
public List<Foo> findAll() {
return Lists.newArrayList(getDao().findAll());
}

View File

@ -1,14 +1,10 @@
package org.baeldung.spring;
import org.baeldung.web.interceptor.LoggerInterceptor;
import org.baeldung.web.interceptor.SessionTimerInterceptor;
import org.baeldung.web.interceptor.UserInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@ -35,15 +31,7 @@ public class WebConfig extends WebMvcConfigurerAdapter {
public void addViewControllers(final ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/graph.html");
registry.addViewController("/csrfHome.html");
registry.addViewController("/homepage.html");
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
registry.addInterceptor(new UserInterceptor());
registry.addInterceptor(new SessionTimerInterceptor());
}
}

View File

@ -11,7 +11,6 @@ import org.baeldung.web.metric.IMetricService;
import org.baeldung.web.util.LinkUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@ -53,7 +52,6 @@ public class RootController {
return metricService.getFullMetric();
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/status-metric", method = RequestMethod.GET)
@ResponseBody
public Map getStatusMetric() {
@ -70,16 +68,5 @@ public class RootController {
return result;
}
@RequestMapping(value = "/admin/x", method = RequestMethod.GET)
@ResponseBody
public String sampleAdminPage() {
return "Hello";
}
@RequestMapping(value = "/my-error-page", method = RequestMethod.GET)
@ResponseBody
public String sampleErrorPage() {
return "Error Occurred";
}
}

View File

@ -11,13 +11,11 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
//import org.springframework.security.access.AccessDeniedException;
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@ -55,12 +53,6 @@ public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionH
return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request);
}
// 403
@ExceptionHandler({ AccessDeniedException.class })
public ResponseEntity<Object> handleAccessDeniedException(final Exception ex, final WebRequest request) {
System.out.println("request" + request.getUserPrincipal());
return new ResponseEntity<Object>("Access denied message here", new HttpHeaders(), HttpStatus.FORBIDDEN);
}
// 404

View File

@ -1,40 +0,0 @@
<?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"
>
<http pattern="/securityNone" security="none"/>
<http use-expressions="true" >
<intercept-url pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/>
<intercept-url pattern="/**" access="isAuthenticated()"/>
<http-basic/>
<csrf disabled="true"/>
<!-- <access-denied-handler error-page="/my-error-page" /> -->
<access-denied-handler ref="customAccessDeniedHandler" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="user1" password="user1Pass" authorities="ROLE_USER"/>
<user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>
<global-method-security pre-post-annotations="enabled"/>
<!-- <mvc:interceptors>
<bean id="loggerInterceptor" class="org.baeldung.web.interceptor.LoggerInterceptor" />
</mvc:interceptors> -->
</beans:beans>

View File

@ -1,7 +1,6 @@
package org.baeldung;
import org.baeldung.persistence.PersistenceTestSuite;
import org.baeldung.security.SecurityTestSuite;
import org.baeldung.web.LiveTestSuite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@ -10,7 +9,6 @@ import org.junit.runners.Suite;
@Suite.SuiteClasses({
// @formatter:off
PersistenceTestSuite.class
,SecurityTestSuite.class
,LiveTestSuite.class
}) //
public class TestSuite {

View File

@ -1,16 +0,0 @@
package org.baeldung.security;
import org.baeldung.security.csrf.CsrfDisabledIntegrationTest;
import org.baeldung.security.csrf.CsrfEnabledIntegrationTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
// @formatter:off
CsrfEnabledIntegrationTest.class
,CsrfDisabledIntegrationTest.class
}) //
public class SecurityTestSuite {
}

View File

@ -1,44 +0,0 @@
package org.baeldung.security.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.baeldung.security.spring.SecurityWithoutCsrfConfig;
import org.baeldung.spring.PersistenceConfig;
import org.baeldung.spring.WebConfig;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, PersistenceConfig.class, WebConfig.class })
public class CsrfDisabledIntegrationTest extends CsrfAbstractIntegrationTest {
@Test
public void givenNotAuth_whenAddFoo_thenUnauthorized() throws Exception {
mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo())).andExpect(status().isUnauthorized());
}
@Test
public void givenAuth_whenAddFoo_thenCreated() throws Exception {
mvc.perform(post("/auth/foos").contentType(MediaType.APPLICATION_JSON).content(createFoo()).with(testUser())).andExpect(status().isCreated());
}
@Test
public void accessMainPageWithoutAuthorization() throws Exception {
mvc.perform(get("/graph.html").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
}
@Test
public void accessOtherPages() throws Exception {
mvc.perform(get("/auth/transfer").contentType(MediaType.APPLICATION_JSON).param("accountNo", "1").param("amount", "100")).andExpect(status().isUnauthorized()); // without authorization
mvc.perform(get("/auth/transfer").contentType(MediaType.APPLICATION_JSON).param("accountNo", "1").param("amount", "100").with(testUser())).andExpect(status().isOk()); // with authorization
}
@Test
public void accessAdminPage() throws Exception {
mvc.perform(get("/auth/admin/x").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isUnauthorized()); // without authorization
mvc.perform(get("/auth/admin/x").contentType(MediaType.APPLICATION_JSON).with(testAdmin())).andExpect(status().isOk()); // with authorization
}
}

View File

@ -14,3 +14,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
- [An Intro to Spring HATEOAS](http://www.baeldung.com/spring-hateoas-tutorial)
- [Spring Security Context Propagation with @Async](http://www.baeldung.com/spring-security-async-principal-propagation)
- [Servlet 3 Async Support with Spring MVC and Spring Security](http://www.baeldung.com/spring-mvc-async-security)
- [Intro to Spring Security Expressions](http://www.baeldung.com/spring-security-expressions)
- [Spring Security Expressions - hasRole Example](http://www.baeldung.com/spring-security-expressions-basic)
- [Error Handling for REST with Spring 3](http://www.baeldung.com/2013/01/31/exception-handling-for-rest-with-spring-3-2/)

View File

@ -28,7 +28,7 @@
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.security.version}</version>
</dependency>
<!-- Spring -->
<dependency>

View File

@ -1,10 +1,13 @@
package org.baeldung.spring;
import org.baeldung.security.MySavedRequestAwareAuthenticationSuccessHandler;
import org.baeldung.web.error.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@ -13,9 +16,13 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan("org.baeldung.security")
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
// @Autowired
// private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@ -40,14 +47,15 @@ public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
.csrf().disable()
.authorizeRequests()
.and()
.exceptionHandling()
// .authenticationEntryPoint(restAuthenticationEntryPoint)
.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
// .authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/csrfAttacker*").permitAll()
.antMatchers("/api/customer/**").permitAll()
.antMatchers("/api/foos/**").authenticated()
.antMatchers("/api/async/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.and()
.httpBasic()
// .and()

View File

@ -1,19 +1,23 @@
package org.baeldung.spring;
import static com.google.common.collect.Lists.newArrayList;
import java.util.Collections;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import static com.google.common.collect.Lists.newArrayList;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@ -25,7 +29,7 @@ public class SwaggerConfig {
}
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", "myeaddress@company.com", "License of API", "API license URL");
ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", new Contact("John Doe", "www.example.com", "myeaddress@company.com"), "License of API", "API license URL", Collections.emptyList());
return apiInfo;
}
}

View File

@ -0,0 +1,30 @@
package org.baeldung.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class RootController {
public RootController() {
super();
}
// API
@RequestMapping(value = "/admin/x", method = RequestMethod.GET)
@ResponseBody
public String sampleAdminPage() {
return "Hello";
}
@RequestMapping(value = "/my-error-page", method = RequestMethod.GET)
@ResponseBody
public String sampleErrorPage() {
return "Error Occurred";
}
}

View File

@ -0,0 +1,74 @@
package org.baeldung.web.error;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
//import org.springframework.security.access.AccessDeniedException;
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
// API
// 400
@ExceptionHandler({ DataIntegrityViolationException.class })
public ResponseEntity<Object> handleBadRequest(final DataIntegrityViolationException ex, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(final HttpMessageNotReadableException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
// ex.getCause() instanceof JsonMappingException, JsonParseException // for additional information later on
return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request);
}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request);
}
// 403
@ExceptionHandler({ AccessDeniedException.class })
public ResponseEntity<Object> handleAccessDeniedException(final Exception ex, final WebRequest request) {
System.out.println("request" + request.getUserPrincipal());
return new ResponseEntity<Object>("Access denied message here", new HttpHeaders(), HttpStatus.FORBIDDEN);
}
// 409
@ExceptionHandler({ InvalidDataAccessApiUsageException.class, DataAccessException.class })
protected ResponseEntity<Object> handleConflict(final RuntimeException ex, final WebRequest request) {
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
}
// 412
// 500
@ExceptionHandler({ NullPointerException.class, IllegalArgumentException.class, IllegalStateException.class })
/*500*/public ResponseEntity<Object> handleInternal(final RuntimeException ex, final WebRequest request) {
logger.error("500 Status Code", ex);
final String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
}

View File

@ -0,0 +1,21 @@
package org.baeldung.web.exception;
public final class MyResourceNotFoundException extends RuntimeException {
public MyResourceNotFoundException() {
super();
}
public MyResourceNotFoundException(final String message, final Throwable cause) {
super(message, cause);
}
public MyResourceNotFoundException(final String message) {
super(message);
}
public MyResourceNotFoundException(final Throwable cause) {
super(cause);
}
}

View File

@ -10,6 +10,8 @@
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<http use-expressions="true" entry-point-ref="restAuthenticationEntryPoint">
<intercept-url pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/>
<intercept-url pattern="/api/**" access="isAuthenticated()" />
<csrf disabled="true" />
@ -17,6 +19,11 @@
<form-login authentication-success-handler-ref="mySuccessHandler"
authentication-failure-handler-ref="myFailureHandler" />
<!-- <access-denied-handler error-page="/my-error-page" /> -->
<access-denied-handler ref="customAccessDeniedHandler" />
<logout />
</http>
@ -30,8 +37,12 @@
<user-service>
<user name="temporary" password="temporary" authorities="ROLE_ADMIN" />
<user name="user" password="userPass" authorities="ROLE_USER" />
<user name="user1" password="user1Pass" authorities="ROLE_USER"/>
<user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>
<global-method-security pre-post-annotations="enabled"/>
</beans:beans>

View File

@ -17,17 +17,20 @@ import com.jayway.restassured.specification.RequestSpecification;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class }, loader = AnnotationConfigContextLoader.class)
public class FooLiveTest {
private static final String URL_PREFIX = "http://localhost:8082/spring-security-rest";
private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest";
// private FormAuthConfig formConfig = new FormAuthConfig(URL_PREFIX + "/login", "temporary", "temporary");
private String cookie;
private RequestSpecification givenAuth() {
// return RestAssured.given().auth().form("user", "userPass", formConfig);
if (cookie == null) {
cookie = RestAssured.given().contentType("application/x-www-form-urlencoded").formParam("password", "userPass").formParam("username", "user").post(URL_PREFIX + "/login").getCookie("JSESSIONID");
}
return RestAssured.given().cookie("JSESSIONID", cookie);
// if (cookie == null) {
// cookie = RestAssured.given().contentType("application/x-www-form-urlencoded").formParam("password", "userPass").formParam("username", "user").post(URL_PREFIX + "/login").getCookie("JSESSIONID");
// }
// return RestAssured.given().cookie("JSESSIONID", cookie);
return RestAssured.given()
.auth()
.basic("user", "userPass");
}
@Test

View File

@ -8,7 +8,7 @@ import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
public class SwaggerLiveTest {
private static final String URL_PREFIX = "http://localhost:8082/spring-security-rest/api";
private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest/api";
@Test
public void whenVerifySpringFoxIsWorking_thenOK() {