[BAEL-9552] - Create spring-security-modules folder
This commit is contained in:
13
spring-security-modules/spring-security-rest/.gitignore
vendored
Normal file
13
spring-security-modules/spring-security-rest/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.class
|
||||
|
||||
#folders#
|
||||
/target
|
||||
/neoDb*
|
||||
/data
|
||||
/src/main/webapp/WEB-INF/classes
|
||||
*/META-INF/*
|
||||
|
||||
# Packaged files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
18
spring-security-modules/spring-security-rest/README.md
Normal file
18
spring-security-modules/spring-security-rest/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## Spring Security REST
|
||||
|
||||
This module contains articles about REST APIs with Spring Security
|
||||
|
||||
### Courses
|
||||
The "REST With Spring" Classes: http://bit.ly/restwithspring
|
||||
|
||||
The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
|
||||
|
||||
### Relevant Articles:
|
||||
- [Spring REST Service Security](https://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/)
|
||||
- [Setting Up Swagger 2 with a Spring REST API](https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api)
|
||||
- [Custom Error Message Handling for REST API](https://www.baeldung.com/global-error-handler-in-a-spring-rest-api)
|
||||
- [Spring Security Context Propagation with @Async](https://www.baeldung.com/spring-security-async-principal-propagation)
|
||||
- [Servlet 3 Async Support with Spring MVC and Spring Security](https://www.baeldung.com/spring-mvc-async-security)
|
||||
- [Intro to Spring Security Expressions](https://www.baeldung.com/spring-security-expressions)
|
||||
- [Spring Security for a REST API](https://www.baeldung.com/securing-a-restful-web-service-with-spring-security)
|
||||
- [Spring Security Expressions - hasRole Example](https://www.baeldung.com/spring-security-expressions-basic)
|
||||
286
spring-security-modules/spring-security-rest/pom.xml
Normal file
286
spring-security-modules/spring-security-rest/pom.xml
Normal file
@@ -0,0 +1,286 @@
|
||||
<?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>
|
||||
<artifactId>spring-security-rest</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<name>spring-security-rest</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-spring-5</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-spring-5</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Security -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- web -->
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${javax.servlet-api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>${jstl.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>${javax.validation.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- marshalling -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- util -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test scoped -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jayway.restassured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>${rest-assured.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<groupId>commons-logging</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${springfox-swagger.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${springfox-swagger.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>${commons-fileupload.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>spring-security-rest</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven2-plugin</artifactId>
|
||||
<version>${cargo-maven2-plugin.version}</version>
|
||||
<configuration>
|
||||
<container>
|
||||
<containerId>jetty8x</containerId>
|
||||
<type>embedded</type>
|
||||
<systemProperties>
|
||||
<!-- <provPersistenceTarget>cargo</provPersistenceTarget> -->
|
||||
</systemProperties>
|
||||
</container>
|
||||
<configuration>
|
||||
<properties>
|
||||
<cargo.servlet.port>8082</cargo.servlet.port>
|
||||
</properties>
|
||||
</configuration>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>live</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven2-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start-server</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop-server</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>none</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**/*LiveTest.java</include>
|
||||
</includes>
|
||||
<systemPropertyVariables>
|
||||
<webTarget>cargo</webTarget>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<properties>
|
||||
<!-- various -->
|
||||
<javax.validation.version>1.1.0.Final</javax.validation.version>
|
||||
|
||||
<!-- util -->
|
||||
<guava.version>26.0-jre</guava.version>
|
||||
<commons-fileupload.version>1.3.2</commons-fileupload.version>
|
||||
|
||||
<!-- testing -->
|
||||
<rest-assured.version>2.9.0</rest-assured.version>
|
||||
|
||||
<!-- swagger -->
|
||||
<springfox-swagger.version>2.9.2</springfox-swagger.version>
|
||||
|
||||
<!-- Maven plugins -->
|
||||
<cargo-maven2-plugin.version>1.6.1</cargo-maven2-plugin.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.baeldung.errorhandling;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class ApiError {
|
||||
|
||||
private HttpStatus status;
|
||||
private String message;
|
||||
private List<String> errors;
|
||||
|
||||
//
|
||||
|
||||
public ApiError() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ApiError(final HttpStatus status, final String message, final List<String> errors) {
|
||||
super();
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
public ApiError(final HttpStatus status, final String message, final String error) {
|
||||
super();
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
errors = Arrays.asList(error);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public HttpStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(final HttpStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(final String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public List<String> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
public void setErrors(final List<String> errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
public void setError(final String error) {
|
||||
errors = Arrays.asList(error);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package org.baeldung.errorhandling;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
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.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
|
||||
// 400
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final List<String> errors = new ArrayList<String>();
|
||||
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
|
||||
errors.add(error.getField() + ": " + error.getDefaultMessage());
|
||||
}
|
||||
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
|
||||
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
|
||||
}
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
|
||||
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleBindException(final BindException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final List<String> errors = new ArrayList<String>();
|
||||
for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
|
||||
errors.add(error.getField() + ": " + error.getDefaultMessage());
|
||||
}
|
||||
for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
|
||||
errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
|
||||
}
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
|
||||
return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType();
|
||||
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleMissingServletRequestPart(final MissingServletRequestPartException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final String error = ex.getRequestPartName() + " part is missing";
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleMissingServletRequestParameter(final MissingServletRequestParameterException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final String error = ex.getParameterName() + " parameter is missing";
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@ExceptionHandler({ MethodArgumentTypeMismatchException.class })
|
||||
public ResponseEntity<Object> handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName();
|
||||
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
@ExceptionHandler({ ConstraintViolationException.class })
|
||||
public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException ex, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final List<String> errors = new ArrayList<String>();
|
||||
for (final ConstraintViolation<?> violation : ex.getConstraintViolations()) {
|
||||
errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage());
|
||||
}
|
||||
|
||||
final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
// 404
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL();
|
||||
|
||||
final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), error);
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
// 405
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(ex.getMethod());
|
||||
builder.append(" method is not supported for this request. Supported methods are ");
|
||||
ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " "));
|
||||
|
||||
final ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, ex.getLocalizedMessage(), builder.toString());
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
// 415
|
||||
|
||||
@Override
|
||||
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
//
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(ex.getContentType());
|
||||
builder.append(" media type is not supported. Supported media types are ");
|
||||
ex.getSupportedMediaTypes().forEach(t -> builder.append(t + " "));
|
||||
|
||||
final ApiError apiError = new ApiError(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getLocalizedMessage(), builder.substring(0, builder.length() - 2));
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
// 500
|
||||
|
||||
@ExceptionHandler({ Exception.class })
|
||||
public ResponseEntity<Object> handleAll(final Exception ex, final WebRequest request) {
|
||||
logger.info(ex.getClass().getName());
|
||||
logger.error("error", ex);
|
||||
//
|
||||
final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
|
||||
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.baeldung.persistence.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class Foo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5422285893276747592L;
|
||||
|
||||
private long id;
|
||||
|
||||
@Size(min = 5, max = 14)
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.baeldung.security;
|
||||
|
||||
import org.baeldung.security.web.MySavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.baeldung.security.web.RestAuthenticationEntryPoint;
|
||||
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;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@ComponentScan("org.baeldung.security")
|
||||
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private CustomAccessDeniedHandler accessDeniedHandler;
|
||||
|
||||
@Autowired
|
||||
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
|
||||
|
||||
@Autowired
|
||||
private MySavedRequestAwareAuthenticationSuccessHandler mySuccessHandler;
|
||||
|
||||
private SimpleUrlAuthenticationFailureHandler myFailureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||
|
||||
public SecurityJavaConfig() {
|
||||
super();
|
||||
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication()
|
||||
.withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
|
||||
.and()
|
||||
.withUser("user").password(encoder().encode("userPass")).roles("USER");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.and()
|
||||
.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()
|
||||
.formLogin()
|
||||
.successHandler(mySuccessHandler)
|
||||
.failureHandler(myFailureHandler)
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.logout();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder encoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.baeldung.security.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class MySavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
private RequestCache requestCache = new HttpSessionRequestCache();
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws ServletException, IOException {
|
||||
final SavedRequest savedRequest = requestCache.getRequest(request, response);
|
||||
|
||||
if (savedRequest == null) {
|
||||
clearAuthenticationAttributes(request);
|
||||
return;
|
||||
}
|
||||
final String targetUrlParameter = getTargetUrlParameter();
|
||||
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
|
||||
requestCache.removeRequest(request, response);
|
||||
clearAuthenticationAttributes(request);
|
||||
return;
|
||||
}
|
||||
|
||||
clearAuthenticationAttributes(request);
|
||||
}
|
||||
|
||||
public void setRequestCache(final RequestCache requestCache) {
|
||||
this.requestCache = requestCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.baeldung.security.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* The Entry Point will not redirect to any sort of Login - it will return the 401
|
||||
*/
|
||||
@Component
|
||||
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(
|
||||
final HttpServletRequest request,
|
||||
final HttpServletResponse response,
|
||||
final AuthenticationException authException) throws IOException {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.baeldung.spring;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
public class ClientWebConfig implements WebMvcConfigurer {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.baeldung.spring;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
// @Configuration
|
||||
// @ImportResource({ "classpath:webSecurityConfig.xml" })
|
||||
@ComponentScan("org.baeldung.security")
|
||||
public class SecurityXmlConfig {
|
||||
|
||||
public SecurityXmlConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.baeldung.spring;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("org.baeldung.web")
|
||||
@EnableWebMvc
|
||||
@EnableAsync
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
|
||||
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolver viewResolver() {
|
||||
final InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
|
||||
viewResolver.setPrefix("/WEB-INF/view/");
|
||||
viewResolver.setSuffix(".jsp");
|
||||
return viewResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewControllers(final ViewControllerRegistry registry) {
|
||||
registry.addViewController("/csrfAttacker.html");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.baeldung.swagger2;
|
||||
|
||||
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;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public Docket api() {
|
||||
return new Docket(DocumentationType.SWAGGER_2).select()
|
||||
.apis(RequestHandlerSelectors.basePackage("org.baeldung.web.controller"))
|
||||
.paths(PathSelectors.ant("/foos/*"))
|
||||
.build()
|
||||
.apiInfo(apiInfo())
|
||||
.useDefaultResponseMessages(false)
|
||||
.globalResponseMessage(RequestMethod.GET, newArrayList(new ResponseMessageBuilder().code(500)
|
||||
.message("500 message")
|
||||
.responseModel(new ModelRef("Error"))
|
||||
.build(),
|
||||
new ResponseMessageBuilder().code(403)
|
||||
.message("Forbidden!!!!!")
|
||||
.build()));
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.baeldung.web.controller;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.baeldung.web.service.AsyncService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
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 AsyncController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AsyncService.class);
|
||||
|
||||
@Autowired
|
||||
private AsyncService asyncService;
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/async")
|
||||
@ResponseBody
|
||||
public Object standardProcessing() throws Exception {
|
||||
log.info("Outside the @Async logic - before the async call: {}", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
|
||||
asyncService.asyncCall();
|
||||
log.info("Inside the @Async logic - after the async call: {}", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
|
||||
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/async2")
|
||||
@ResponseBody
|
||||
public Callable<Boolean> springMVCAsyncTest() {
|
||||
return asyncService.checkIfPrincipalPropagated();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.baeldung.web.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
@Controller
|
||||
public class CustomController {
|
||||
|
||||
@RequestMapping(value = "/custom", method = RequestMethod.POST)
|
||||
public String custom() {
|
||||
return "custom";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.baeldung.web.controller;
|
||||
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.baeldung.persistence.model.Foo;
|
||||
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;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = "/foos")
|
||||
public class FooController {
|
||||
|
||||
// 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 Lists.newArrayList(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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.baeldung.web.error;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(final HttpServletRequest request, final HttpServletResponse response, final AccessDeniedException ex) throws IOException, ServletException {
|
||||
response.getOutputStream().print("Error Message Goes Here");
|
||||
response.setStatus(403);
|
||||
// response.sendRedirect("/my-error-page");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.baeldung.web.service;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public interface AsyncService {
|
||||
|
||||
void asyncCall();
|
||||
|
||||
Callable<Boolean> checkIfPrincipalPropagated();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.baeldung.web.service;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AsyncServiceImpl implements AsyncService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AsyncService.class);
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void asyncCall() {
|
||||
log.info("Inside the @Async logic: {}", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callable<Boolean> checkIfPrincipalPropagated() {
|
||||
Object before = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
log.info("Before new thread: {}", before);
|
||||
|
||||
return new Callable<Boolean>() {
|
||||
public Boolean call() throws Exception {
|
||||
Object after = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
log.info("New thread: {}", after);
|
||||
return before == after;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework" level="WARN" />
|
||||
<logger name="org.springframework.transaction" level="WARN" />
|
||||
|
||||
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
|
||||
<logger name="org.springframework.web.servlet.mvc" level="WARN" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?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"
|
||||
xmlns:sec="http://www.springframework.org/schema/security"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
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">
|
||||
|
||||
<!-- Spring Security Configurations are managed in SecurityJavaConfig class -->
|
||||
<!--
|
||||
<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" />
|
||||
|
||||
<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>
|
||||
|
||||
<beans:bean id="mySuccessHandler"
|
||||
class="org.baeldung.security.MySavedRequestAwareAuthenticationSuccessHandler" />
|
||||
<beans:bean id="myFailureHandler"
|
||||
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler" />
|
||||
|
||||
<authentication-manager alias="authenticationManager">
|
||||
<authentication-provider>
|
||||
<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>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="multipartResolver"
|
||||
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
|
||||
<property name="maxUploadSize" value="1000000" />
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<head></head>
|
||||
|
||||
<body>
|
||||
<h1>CSRF Attacker</h1>
|
||||
<a href="http://localhost:8080/spring-security-rest-full/transfer?accountNo=5678&amount=1000">Show Kittens Pictures</a>
|
||||
|
||||
<img src="http://localhost:8080/spring-security-rest-full/transfer?accountNo=5678&amount=1000"/>
|
||||
|
||||
<form action="http://localhost:8080/spring-security-rest-full/transfer" method="POST">
|
||||
<input name="accountNo" type="hidden" value="5678"/>
|
||||
<input name="amount" type="hidden" value="1000"/>
|
||||
<input type="submit" value="Show Kittens Picture">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
xsi:schemaLocation="
|
||||
http://java.sun.com/xml/ns/javaee
|
||||
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
id="WebApp_ID" version="3.0">
|
||||
|
||||
<display-name>Spring MVC Application</display-name>
|
||||
|
||||
<!-- Spring root -->
|
||||
<context-param>
|
||||
<param-name>contextClass</param-name>
|
||||
<param-value>
|
||||
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>org.baeldung.spring</param-value>
|
||||
</context-param>
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<!-- Spring child -->
|
||||
<servlet>
|
||||
<servlet-name>api</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>throwExceptionIfNoHandlerFound</param-name>
|
||||
<param-value>true</param-value>
|
||||
</init-param>
|
||||
<async-supported>true</async-supported>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>api</servlet-name>
|
||||
<url-pattern>/api/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<filter>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
|
||||
<async-supported>true</async-supported>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>springSecurityFilterChain</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>ASYNC</dispatcher>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- <welcome-file-list> -->
|
||||
<!-- <welcome-file>index.html</welcome-file> -->
|
||||
<!-- </welcome-file-list> -->
|
||||
|
||||
</web-app>
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.baeldung;
|
||||
|
||||
import org.baeldung.security.SecurityJavaConfig;
|
||||
import org.baeldung.spring.ClientWebConfig;
|
||||
import org.baeldung.spring.WebConfig;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration(classes = { ClientWebConfig.class, SecurityJavaConfig.class, WebConfig.class })
|
||||
public class SpringContextTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.baeldung.errorhandling;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.baeldung.errorhandling.ApiError;
|
||||
import org.baeldung.web.TestConfig;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
import com.jayway.restassured.RestAssured;
|
||||
import com.jayway.restassured.response.Response;
|
||||
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: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);
|
||||
return RestAssured.given()
|
||||
.auth().preemptive()
|
||||
.basic("user", "userPass");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTry_thenOK() {
|
||||
final Response response = givenAuth().get(URL_PREFIX + "/api/foos");
|
||||
assertEquals(200, response.statusCode());
|
||||
System.out.println(response.asString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenMethodArgumentMismatch_thenBadRequest() {
|
||||
final Response response = givenAuth().get(URL_PREFIX + "/api/foos/ccc");
|
||||
final ApiError error = response.as(ApiError.class);
|
||||
assertEquals(HttpStatus.BAD_REQUEST, error.getStatus());
|
||||
assertEquals(1, error.getErrors().size());
|
||||
assertTrue(error.getErrors().get(0).contains("should be of type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoHandlerForHttpRequest_thenNotFound() {
|
||||
final Response response = givenAuth().delete(URL_PREFIX + "/api/xx");
|
||||
final ApiError error = response.as(ApiError.class);
|
||||
assertEquals(HttpStatus.NOT_FOUND, error.getStatus());
|
||||
assertEquals(1, error.getErrors().size());
|
||||
assertTrue(error.getErrors().get(0).contains("No handler found"));
|
||||
System.out.println(response.asString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenHttpRequestMethodNotSupported_thenMethodNotAllowed() {
|
||||
final Response response = givenAuth().delete(URL_PREFIX + "/api/foos/1");
|
||||
final ApiError error = response.as(ApiError.class);
|
||||
assertEquals(HttpStatus.METHOD_NOT_ALLOWED, error.getStatus());
|
||||
assertEquals(1, error.getErrors().size());
|
||||
assertTrue(error.getErrors().get(0).contains("Supported methods are"));
|
||||
System.out.println(response.asString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSendInvalidHttpMediaType_thenUnsupportedMediaType() {
|
||||
final Response response = givenAuth().body("").post(URL_PREFIX + "/api/foos");
|
||||
final ApiError error = response.as(ApiError.class);
|
||||
assertEquals(HttpStatus.UNSUPPORTED_MEDIA_TYPE, error.getStatus());
|
||||
assertEquals(1, error.getErrors().size());
|
||||
assertTrue(error.getErrors().get(0).contains("media type is not supported"));
|
||||
System.out.println(response.asString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.baeldung.web;
|
||||
|
||||
import org.baeldung.security.SecurityJavaConfig;
|
||||
import org.baeldung.spring.ClientWebConfig;
|
||||
import org.baeldung.spring.WebConfig;
|
||||
import org.baeldung.web.controller.AsyncController;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
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.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration(classes = { ClientWebConfig.class, SecurityJavaConfig.class, WebConfig.class })
|
||||
public class AsyncControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
WebApplicationContext wac;
|
||||
@Autowired
|
||||
MockHttpSession session;
|
||||
|
||||
@Mock
|
||||
AsyncController controller;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsync() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/async")).andExpect(status().is5xxServerError());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.baeldung.web;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.jayway.restassured.RestAssured;
|
||||
import com.jayway.restassured.response.Response;
|
||||
|
||||
public class SwaggerLiveTest {
|
||||
private static final String URL_PREFIX = "http://localhost:8080/spring-security-rest/api";
|
||||
|
||||
@Test
|
||||
public void whenVerifySpringFoxIsWorking_thenOK() {
|
||||
final Response response = RestAssured.get(URL_PREFIX + "/v2/api-docs");
|
||||
assertEquals(200, response.statusCode());
|
||||
System.out.println(response.asString());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.baeldung.web;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan({ "org.baeldung.web" })
|
||||
public class TestConfig {
|
||||
|
||||
@Bean
|
||||
public MultipartResolver multipartResolver() {
|
||||
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
|
||||
return multipartResolver;
|
||||
}
|
||||
|
||||
}
|
||||
13
spring-security-modules/spring-security-rest/src/test/resources/.gitignore
vendored
Normal file
13
spring-security-modules/spring-security-rest/src/test/resources/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.class
|
||||
|
||||
#folders#
|
||||
/target
|
||||
/neoDb*
|
||||
/data
|
||||
/src/main/webapp/WEB-INF/classes
|
||||
*/META-INF/*
|
||||
|
||||
# Packaged files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
Reference in New Issue
Block a user