I'm quite new to Spring and I have created a small webservice with Spring-boot, Hibernate and Swagger. Here is my HomeController:
package io.swagger.configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Home redirection to swagger api documentation
*/
#Controller
public class HomeController {
#RequestMapping(value = "/")
public String index() {
System.out.println("swagger-ui.html");
return "redirect:swagger-ui.html";
}
}
Everything is working well, excepting I don't understand why when I have an Internal Server Error I get a status 200 with this body for example:
{
"timestamp": "2017-12-12T23:52:02.306+0000",
"status": 500,
"error": "Internal Server Error",
"exception": "javax.persistence.PersistenceException",
"message": "org.hibernate.exception.ConstraintViolationException: could not execute statement",
"path": "/example/members/1031/subscriptions"
}
And there is the launcher :
package io.swagger;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#SpringBootApplication
#EnableSwagger2
#ComponentScan(basePackages = "io.swagger")
public class Swagger2SpringBoot extends SpringBootServletInitializer implements CommandLineRunner {
#Override
public void run(String... arg0) throws Exception {
if (arg0.length > 0 && arg0[0].equals("exitcode")) {
throw new ExitException();
}
}
public static void main(String[] args) throws Exception {
LogFactory.getLog(Swagger2SpringBoot.class).warn("test");
new SpringApplication(Swagger2SpringBoot.class).run(args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Swagger2SpringBoot.class);
}
class ExitException extends RuntimeException implements ExitCodeGenerator {
private static final long serialVersionUID = 1L;
#Override
public int getExitCode() {
return 10;
}
}
}
with the configuration :
package io.swagger;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
/**
* Make sure dates are serialised in
* ISO-8601 format instead as timestamps
*/
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) converter;
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.disable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
);
break;
}
}
}
}
Another one for Swagger:
package io.swagger.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
#Configuration
public class SwaggerDocumentationConfig {
ApiInfo apiInfo() {
return new ApiInfoBuilder().title("**** API").description("No description").license("").licenseUrl("")
.termsOfServiceUrl("www.****.com").version("1.0.0")
.contact(new Contact("", "", "****#****.com")).build();
}
#Bean
public Docket customImplementation() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("io.swagger.api")).build().apiInfo(apiInfo());
}
}
So why do I have this 200 status if I got a server error? At least I'd like to generate a status 500, 200 means everything was fine, and obviously it's not. Any idea ?
It appears I had implemented a custom error controller generating code from my swagger file, I deleted it and now everything is ok.
Related
I'm developing a Spring boot application and my endpoints are not working at all. I'm getting the next issue:
{
"timestamp": "2020-06-21T21:12:39.716+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/prduct/jjjk"
}
This is the structure of my project:
As you can see the controller is in the same path a subpackage of the Application class, but it is not scanned.
package com.mercadolibre.mutants.controllers;
import com.mercadolibre.mutants.entities.Product;
import com.mercadolibre.mutants.repositories.ProductRepository;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/prduct/{id}")
public ResponseEntity<Product> findById(#PathVariable("id") String id) {
Optional<Product> p = productRepository.findById(id);
return ResponseEntity.ok(p.orElse(null));
}
#PostMapping(value = "/products", consumes = "application/json")
public ResponseEntity<Product> save(#RequestBody Product p) {
Product result = productRepository.save(p);
return ResponseEntity.ok(result);
}
}
This is my Application class:
package com.mercadolibre.mutants;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.mercadolibre.mutants")
public class MutantsApplication {
public static void main(String[] args) {
SpringApplication.run(MutantsApplication.class, args);
}
}
I'm not able to identify the issue or what I'm missing. Any ideas?
I recently added CORS configuration in my springboot app. Below is the code for this configuration.
package com.turtlemint.verticals.commonverticals;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
/**
* #author praveenkamath
**/
#Configuration
#EnableWebFlux
public class CorsConfiguration implements WebFluxConfigurer {
#Value("${cors.allowed.origins}")
private String[] corsSites;
#Value("${cors.enabled}")
private boolean isCorsEnabled;
#Override
public void addCorsMappings(final CorsRegistry registry) {
if(isCorsEnabled) {
registry.addMapping("/api/device/**").allowedOrigins(corsSites).allowedMethods("*");
registry.addMapping("/api/sehat/**").allowedOrigins(corsSites).allowedMethods("*");
registry.addMapping("/api/dukandaar/**").allowedOrigins(corsSites).allowedMethods("*");
return;
} registry.addMapping("/**");
}
}
My application.properties
### CORS ###
cors.enabled=true
cors.allowed.origins=*
### CORS ###
After adding it, I ran into 2 issues:
A PUT request with nested JSON gives below error
Internal Server Error","message":"Type definition error: [simple type, class com.turtlemint.verticals.commonverticals.common.containers.v1.collections.ReviewPayment]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.turtlemint.verticals.commonverticals.common.containers.v1.collections.ReviewPayment` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.turtlemint.verticals.commonverticals.dukandaar.containers.dto.DukandaarCheckoutDTO[\"reviewPayment\"])
ReviewPayment Java class:
package com.turtlemint.verticals.commonverticals.common.containers.v1.collections;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.turtlemint.verticals.commonverticals.common.enums.v1.ReviewPaymentStatusEnum;
import lombok.Data;
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ReviewPayment {
private ReviewPaymentStatusEnum status;
private String rejectionReason;
public ReviewPayment(ReviewPaymentStatusEnum status, String rejectionReason) {
this.status = status;
this.rejectionReason = rejectionReason;
}
}
I added a default constructor in above class and it worked.
Getting below error when redirecting to a view from backend
{
"timestamp": 1576568268085,
"path": "/api/dukandaar/v1/payment/callback/HDFC",
"status": 500,
"error": "Internal Server Error",
"message": "Could not resolve view with name 'get_redirect'."
}
My controller:
package com.turtlemint.verticals.commonverticals.common.controllers.v1;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.turtle.enums.VerticalEnum;
import com.turtlemint.verticals.commonverticals.common.constants.v1.Constants;
import com.turtlemint.verticals.commonverticals.common.constants.v1.ThymeConstants;
import com.turtlemint.verticals.commonverticals.common.containers.v1.mappers.PaymentCallbackDTO;
import com.turtlemint.verticals.commonverticals.common.containers.v1.mappers.PaymentRedirectRequestDTO;
import com.turtlemint.verticals.commonverticals.common.containers.v1.mappers.ReviewStatusUpdateRequestDTO;
import com.turtlemint.verticals.commonverticals.common.containers.v1.mappers.ReviewStatusUpdateResponseDTO;
import com.turtlemint.verticals.commonverticals.common.containers.v1.mappers.notification.share.ShareRequestMapper;
import com.turtlemint.verticals.commonverticals.common.enums.v1.ReviewPaymentStatusEnum;
import com.turtlemint.verticals.commonverticals.common.services.CommonServiceFactory;
import com.turtlemint.verticals.commonverticals.common.utils.v1.BrokerUtils;
import com.turtlemint.verticals.commonverticals.common.utils.v1.MapUtils;
import com.turtlemint.verticals.commonverticals.deviceprotection.services.v1.DeviceProtectionServiceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* #author praveenkamath
**/
#Controller
#RequestMapping("/api/{apiVertical}")
public class PaymentController {
private static final Logger LOG = LoggerFactory.getLogger(PaymentController.class);
private static final String REDIRECTION_PARAMS = "redirectionParams";
#Autowired
private CommonServiceFactory commonServiceFactory;
#Autowired
private DeviceProtectionServiceFactory deviceProtectionServiceFactory;
#GetMapping(value = "/v1/payment/callback/{insurer}")
public String processPayment(#PathVariable final String insurer, #PathVariable final String apiVertical, final Model model, final ServerHttpRequest serverRequest){
LOG.info("[processPayment] request received {} for insurer {}",serverRequest.getQueryParams(), insurer);
Map<String,Object> requestMap= MapUtils.convertMultiToRegularMap(serverRequest.getQueryParams());
VerticalEnum verticalEnum = VerticalEnum.getValue(apiVertical);
final String url = commonServiceFactory.getCheckoutService().processPayment(BrokerUtils.determineBroker(serverRequest.getURI().getHost()), requestMap, verticalEnum, insurer);
final Map<String, String> redirectionParams = new HashMap<>();
redirectionParams.put("url", url);
model.addAttribute(REDIRECTION_PARAMS, redirectionParams);
return ThymeConstants.REDIRECT_GET_TEMPLATE;
}
}
public class ThymeConstants {
private ThymeConstants() {}
public static final String REDIRECT_GET_TEMPLATE = "get_redirect";
}
My GET request:
https://<DOMAIN>/api/dukandaar/v1/payment/callback/HDFC?<query_params>
I comment out the CORS configuration and everything works fine.
I am still pulling my hair trying to find out the 2nd issue. Some help would be appreciated.
Finally found the solution for the 2nd issue. Had to override the thymeleaf configuration.
CorsConfiguration now looks like this:
package com.turtlemint.verticals.commonverticals.common.security.cors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.ViewResolverRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.thymeleaf.spring5.ISpringWebFluxTemplateEngine;
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
/**
* #author praveenkamath
**/
#Configuration
#EnableWebFlux
public class CorsConfiguration implements ApplicationContextAware, WebFluxConfigurer {
#Value("${cors.allowed.origins}")
private String[] corsSites;
#Value("${cors.enabled}")
private boolean isCorsEnabled;
private ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext context) {
this.ctx = context;
}
#Override
public void addCorsMappings(final CorsRegistry registry) {
if(isCorsEnabled) {
registry.addMapping("/api/device/**").allowedOrigins(corsSites).allowedMethods("PUT", "PATCH", "DELETE", "OPTIONS");
registry.addMapping("/api/sehat/**").allowedOrigins(corsSites).allowedMethods("PUT", "PATCH", "DELETE", "OPTIONS");
registry.addMapping("/api/dukandaar/**").allowedOrigins(corsSites).allowedMethods("PUT", "PATCH", "DELETE", "OPTIONS");
return;
} registry.addMapping("/**");
}
#Bean
SpringResourceTemplateResolver thymeleafTemplateResolver() {
final SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(this.ctx);
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
resolver.setCheckExistence(false);
return resolver;
}
#Bean
ISpringWebFluxTemplateEngine thymeleafTemplateEngine() {
final SpringWebFluxTemplateEngine templateEngine = new SpringWebFluxTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
}
#Bean
ThymeleafReactiveViewResolver thymeleafChunkedAndDataDrivenViewResolver() {
final ThymeleafReactiveViewResolver viewResolver = new ThymeleafReactiveViewResolver();
viewResolver.setTemplateEngine(thymeleafTemplateEngine());
viewResolver.setResponseMaxChunkSizeBytes(8192); // OUTPUT BUFFER size limit
return viewResolver;
}
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(thymeleafChunkedAndDataDrivenViewResolver());
}
}
In Login class #Autowired is not null but when I try to use postman call Logout class #Autowired get null. I copy code from Login to Logout class just change variable name all code is same pattern I don't know what happening
package xxx.api.login
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import xxx.api.domain.logout.LoginRequest;
import xxx.api.domain.logout.LoginResponse;
import xxx.converter.FieldValidation;
import xxx.dto.log.MessageLog;
import xxx.logging.LogFactory;
#Component
public class LoginClient {
#Autowired
FieldValidation fieldValidation;
public LoginResponse login(LoginRequest loginRequest) throws Exception {
ObjectMapper mapper = new ObjectMapper();
final String url = "http://localhost:xxx/xxx/login";
LoginResponse responseObject = null;
try {
String requestData = mapper.writeValueAsString(loginRequest);
responseObject = login(url, requestData);
fieldValidation.validateResponse(responseObject, "login");
// 'fieldValidation' not null
This is my Login class. Return result as I expect.
package xxx.api.logout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import xxx.api.domain.logout.LogoutRequest;
import xxx.api.domain.logout.LogoutResponse;
import xxx.converter.FieldValidation;
import xxx.dto.log.MessageLog;
import xxx.logging.LogFactory;
#Component
public class LogoutClient {
#Autowired
FieldValidation fieldValidation;
public LogoutResponse logout(LogoutRequest logoutRequest) throws Exception {
ObjectMapper mapper = new ObjectMapper();
final String url = "http://localhost:xxx/xxx/logout";
LogoutResponse responseObject = null;
try {
String requestData = mapper.writeValueAsString(logoutRequest);
responseObject = logout(url, requestData);
fieldValidation.validateResponse(responseObject, "logout");
// 'fieldValidation' is null then throws nullPointerException
This is my Logout class
package xxx.api.logout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xxx.api.domain.logout.LogoutRequest;
import xxx.api.domain.logout.LogoutResponse;
import xxx.dto.log.MessageLog;
import xxx.exception.common.ErrorException;
import xxx.api.logout.LogoutClient;
import xxx.converter.FieldValidation;
#RestController
#RequestMapping(path = "/xxx")
public class LogoutServer {
#Autowired
FieldValidation fieldValidation;
#Autowired
WSLogFactory wsLog;
#PostMapping(value = "/logout", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> logout(#RequestHeader(value = "requestId") String requestId,
#RequestBody(required = true) LogoutRequest logoutRequest) throws ErrorException, Exception {
writeLogRequest(logoutRequest, logoutRequest.getClientIP(), "Request", "/xxx/logout");
fieldValidation.validateRequest(logoutRequest, "logout");
try {
LogoutClient logoutClient = new LogoutClient();
LogoutResponse response = logoutClient.logout(logoutRequest);
return new ResponseEntity<LogoutResponse>(response, HttpStatus.OK);
} catch (Exception e) {
throw e;
}
}
This is Logout Controller
#org.springframework.context.annotation.Configuration
#PropertySource("file:${layout.properties_file}")
public class FieldValidation {
// do somethings
}
This is my FieldValidation class.
{
"timestamp": 1550490230074,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.NullPointerException",
"message": "No message available",
"path": "/umm/logout"
}
This is return when I call my Logout class.
This is my project structure
xxx.api.login <<< LoginClient.java
xxx.api.logout <<< LogoutClient.java
xxx.converter <<< FieldValidation.java
This is my main program
package xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I try to replace #Component to #Configuration but still doesn't works.
I don't need 'fieldValidation' in Logout class or another class be a null value.
package xxx.converter;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.springframework.context.annotation.Configuration;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.validator.routines.BigDecimalValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;
import xxx.exception.common.ErrorException;
#Configuration
#PropertySource("file:${layout.properties_file}")
public class FieldValidation {
public void validateObject(Object objResponse, String config) throws Exception {
try {
List<Configuration> confs = objectMapper.readValue(config,
objectMapper.getTypeFactory().constructCollectionType(List.class, Configuration.class));
for (int i = 0; i < confs.size(); i++) {
Configuration configuration = confs.get(i);
Object objValue = getValue(configuration.getFieldName(), objResponse);
Validation validation = new Validation();
BeanUtils.copyProperties(validation, configuration);
isValid(objValue, validation);
if (configuration.getType().equalsIgnoreCase("object")) {
List<Validation> validations = configuration.getValidation();
Class<?> act = Class.forName(configuration.getClassName());
Object objectConfig = act.cast(objValue);
validateObject(objectConfig, validations);
}
}
} catch (Exception e) {
throw e;
}
}
This is full import of FieldValidation class and replace #Configuration and I get this error.
configuration.getFieldName() << error The method is undefined for type Configuration
configuration.getType() << error The method is undefined for type Configuration
configuration.getValidation() << error The method is undefined for type Configuration
configuration.getClassName() << error The method is undefined for type Configuration
I have the following code:
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.restlet.RestletComponent;
import org.restlet.ext.spring.SpringServerServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.HashMap;
#Component
public class DummyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
rest("/upload").post().to("file://rest_files");
}
#Bean
public ServletRegistrationBean servletRegistrationBean() {
SpringServerServlet serverServlet = new SpringServerServlet();
ServletRegistrationBean regBean = new ServletRegistrationBean( serverServlet, "/rest/*");
Map<String,String> params = new HashMap<>();
params.put("org.restlet.component", "restletComponent");
regBean.setInitParameters(params);
return regBean;
}
#Bean
public org.restlet.Component restletComponent() {
return new org.restlet.Component();
}
#Bean
public RestletComponent restletComponentService() {
return new RestletComponent(restletComponent());
}
}
I load file using postman and app stores file inside the folder rest_files
But content of file and its name looks strange.
file name: ID-username-1508923387747-0-2
content:
How to make it to store at least correct content?
I have configured all properties, but my app still loads without spring security as if it does not exist... Please help me, what I am doing wrong.
Here I get my rooms without auth with postman:
Here below are my classes:
SecurityConfiguration:
package com.vidaflo.config;
import org.springframework.beans.factory.annotation.Autowired;
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.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.config.http.SessionCreationPolicy;
#Configuration
#EnableWebSecurity
#ComponentScan("com.vidaflo")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("ADMIN");
auth.inMemoryAuthentication().withUser("tom").password("abc123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/room/**").hasRole("ADMIN")
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
SecurityInitializer:
package com.vidaflo.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
WebConfiguration:
package com.vidaflo.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.vidaflo.controllers")
public class WebConfiguration extends WebMvcConfigurationSupport {
}
Tomcat embedded:
package com.vidaflo.server;
import com.vidaflo.config.ApplicationConfiguration;
import com.vidaflo.config.DatabaseConfiguration;
import com.vidaflo.config.SecurityConfiguration;
import com.vidaflo.config.WebConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
#Slf4j
public class Application {
private static final String APPLICATION_PROPERTIES = System.getProperty("app.properties");
private static final int DEFAULT_PORT = 8080;
private static final String DEFAULT_CONTEXT_PATH = "/app";
private AppProperties appProperties;
private AnnotationConfigWebApplicationContext ctx;
public static void main(String[] args) throws LifecycleException {
Application app = new Application(APPLICATION_PROPERTIES);
Server server = new TomcatServer(new Tomcat());
app.run(server);
}
public Application(String fieldName) {
loadProperties(fieldName);
}
public void run(Server server) {
initApplicationContext();
server.run(getConfig());
}
private void loadProperties(String fieldName) {
appProperties = new AppProperties();
appProperties.load(fieldName);
}
private void initApplicationContext() {
log.info("Initialize application context...");
ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SecurityConfiguration.class);
ctx.register(ApplicationConfiguration.class);
ctx.register(WebConfiguration.class);
ctx.register(DatabaseConfiguration.class);
ctx.getEnvironment()
.getPropertySources()
.addLast(new PropertiesPropertySource("applicationEnvironment", appProperties.getProperties()));
}
private ServerConfig getConfig() {
ServerConfig serverConfig = new ServerConfig();
serverConfig.setPort(appProperties.getPort(DEFAULT_PORT));
serverConfig.setContextPath(appProperties.getContextPath(DEFAULT_CONTEXT_PATH));
serverConfig.setServlet(getServlet());
return serverConfig;
}
private DispatcherServlet getServlet() {
return new DispatcherServlet(ctx);
}
}
Rest controller:
package com.vidaflo.controllers;
import com.vidaflo.dto.RoomDto;
import com.vidaflo.model.location.Room;
import com.vidaflo.repositories.LocationRepository;
import com.vidaflo.services.RoomService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
#RestController
public class RoomController {
#Autowired
private RoomService roomService;
#Autowired
private LocationRepository locationService;
#PostMapping("/room/save")
public String save(#RequestParam(name = "name") String name,
#RequestParam(name = "location_id") Long locationId) {
roomService.save(name, locationService.findOne(locationId));
return "room added";
}
#GetMapping("/room/all")
public List<RoomDto> findAll() {
return roomService.findAll().stream()
.map(this::toDto)
.collect(Collectors.toList());
}
private RoomDto toDto(Room room) {
return RoomDto.builder()
.id(room.getId())
.name(room.getName())
.build();
}
}
Please tell me if I should add additional details. I rly need help and I can't understand what I'm doing wrong.
Found an answer, we should manually add filter for spring security in tomcat embedded config like this:
FilterDef filterDef = new FilterDef();
filterDef.setFilterName("springSecurityFilterChain");
filterDef.setFilterClass("org.springframework.web.filter.DelegatingFilterProxy");
container.addFilterDef(filterDef);
FilterMap filterMapping = new FilterMap();
filterMapping.setFilterName("springSecurityFilterChain");
filterMapping.addURLPattern("/*");
container.addFilterMap(filterMapping);
Try to change role "ADMIN" "USER" to "ROLE_ADMIN" "ROLE_USER" in configureGlobalSecurity method and in the enum "Roles", but in configure method don't change.