[BAEL-9552] - Create spring-security-modules folder
This commit is contained in:
+64
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+169
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
+27
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -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 {
|
||||
}
|
||||
+14
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -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;
|
||||
}
|
||||
}
|
||||
+39
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -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";
|
||||
}
|
||||
}
|
||||
+49
@@ -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;
|
||||
}
|
||||
}
|
||||
+30
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.baeldung.web.service;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public interface AsyncService {
|
||||
|
||||
void asyncCall();
|
||||
|
||||
Callable<Boolean> checkIfPrincipalPropagated();
|
||||
|
||||
}
|
||||
+36
@@ -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>
|
||||
+16
@@ -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>
|
||||
Reference in New Issue
Block a user