I am trying to secure my Spring REST Services with Spring security and OAuth2. I managed to get the tokens:
tokens
But when I try to get an secured rest service I get access denied:
denied
Can anybody tell me why this is happening?
AuthorizationServerConfig:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static String REALM="EXAMPLE_REALM";
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler handler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myRestClient") // client id
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("{noop}P#ssw0rd")
.accessTokenValiditySeconds(240).//invalid after 4 minutes.
refreshTokenValiditySeconds(600);//refresh after 10 minutes.
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(handler)
.authenticationManager(authManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm(REALM+"/client");
}
}
ResourceServerConfig:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "SPRING_REST_API";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/api/**")
.and().authorizeRequests()
.antMatchers("/api/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientService;
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception{
auth
.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/about").authenticated();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientService));
handler.setClientDetailsService(clientService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
AboutController (rest service):
#RestController
public class AboutController {
#RequestMapping(value = "/about", method = RequestMethod.GET)
public ResponseEntity<?> home() {
return new ResponseEntity<>("This is a demo application to show how to secure REST API using Spring Security and OAuth2", HttpStatus.OK);
}
}
application.properties:
server.servlet.context-path=/api
security.oauth2.resource.filter-order = 3
pom.xml:
<?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>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Thanks in advance!
Got this working. My problem was that in the application.config file I added /api so that for all my services an api folder is in place like: http://localhost:8080/api/about .. in my ResourceServerConfig class I added /api/** as requestMatchers and antMatchers.... the sercurity configuration is looking for serverice after this api prefix folder.... so exposing my about rest is done in this way:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "SPRING_REST_API";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/about/**")
.and().authorizeRequests()
.antMatchers("/about/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
By the way.. I can request the about service with a parameter like in my request screenshot but it works also if i provide the authorization token als a Bearer Token authorization header....
Regards!
Peter
Related
Has anyone tried adding Swagger-UI to projects running with Tomcat and Spring, but without Spring Boot?
My project runs on a Tomcat server, with spring. I want to add swagger-UI to see all the endpoints but I can't implement it.
I tried with all kinds of dependencies and configurations but I didn't manage to finish it.
This is my configuration:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.betfair"})
#Import(SwaggerConfig.class)
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", "myeaddress#company.com", "License of API", "API license URL");
return apiInfo;
} }
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration implements WebSecurityCustomizer {
#Override
public void customize(WebSecurity web) {
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
}
// My pom.xml dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-schema</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.7.2</version>
</dependency>
What I am missing? Do I need more special configurations? Thanks.
I am trying to configure Keycloak with spring boot. But the endpoints that I configure wether its open or with a Role I walys get a 401
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true,
jsr250Enabled = true)
public class KeycloakSecurityConfig extends
KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/test/anoymous").permitAll();
.antMatchers("/test/user").hasAnyRole("user")
.antMatchers("/test/admin").hasAnyRole("admin")
.antMatchers("/test/all-user").hasAnyRole("user","admin")
.anyRequest()
http.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
When I hit the test endpoint anonymous I get back
{
"timestamp": "2021-07-08T20:39:21.265+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/test/anonymous"
}
However even with the keycloak token I get an error which is not authorized... The same as above but with Bearer Token.
#RestController
#RequestMapping("/test")
public class KeyController {
#RequestMapping(value="/anonymous", method=RequestMethod.GET)
public ResponseEntity<String> AdminEndpoint() {
return ResponseEntity.ok("Hello Anounymous");
}
#RolesAllowed("user")
#RequestMapping(value="/user", method=RequestMethod.GET)
public ResponseEntity<String> getUser(){
return ResponseEntity.ok("Hello User");
}
#RequestMapping(value="/admin", method=RequestMethod.GET)
public ResponseEntity<String> getAdmin(){
return ResponseEntity.ok("Hello Admin");
}
#RequestMapping(value="/all-users", method=RequestMethod.GET)
public ResponseEntity<String> getAllUsers(){
return ResponseEntity.ok("Hello to all");
}
}
It runs normally but I checked every spring security configuration on stack as possible as I can but nothing worked. Please help me.
This is the Pom.xml
<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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.io.keycloak</groupId>
<artifactId>keycloak</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring.version>5.3.4</spring.version>
<keycloak.version>14.0.0</keycloak.version>
<java.version>11</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I figured it out ...
1 - The class RestController was not being found because it was in another package. So at the main class where the main function is I inputed #ComponentScan for where the RestController was.
#SpringBootApplication
#ComponentScan("com.io*") //This was the issue...
public class mainApp {
public static void main(String[] args) {
//Hello
SpringApplication.run(mainApp.class, args);
}
}
Just an FYI. Even with the #RestController, #Component, #Controller and other annotations where automatically spring would find does not actually work that way. You sometimes need to input #ComponentScan as well.
The result:
my Rest API is working fine locally but the moment I put the same JAR and run it from cloud server, I can only call the GET API and none of the POST API from Postmen.
Below are my code.
RestController
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api/test")
public class TestController {
#GetMapping("/all")
public String allAccess() {
return "Public Content.";
}
#PostMapping("/testpostA")
public ResponseEntity<?> testPostA() {
return ResponseEntity.ok(new MessageResponse("Test POST successfully!"));
}
#PostMapping("/testpostB")
public String testPostB() {
return "String return POST successfully.";
}
}
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.eurogain</groupId>
<artifactId>WebPortal</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>WebPortal</name>
<description>Java Spring Boot project for the Eurogain web portal for both internal and external client.</description>
<properties>
<java.version>15</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build></project>
WebSecurityConfigurationAdapter
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
// securedEnabled = true,
// jsr250Enabled = true,
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("============================ Inside WebSecurityConfig ===========================Ken");
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}}
Lastly, the web application:
#SpringBootApplication
public class WebPortalApplication {
public static void main(String[] args) {
SpringApplication.run(WebPortalApplication.class, args);
}}
Running the same JAR file locally, no issue on calling all of the POST and GET API. However, when we move the same JAR over to AWS EC2 cloud instance and run it, we can only call the GET API. The following error was thrown at the Postmen console when calling any of the POST API:
However, no issue when calling the GET API:
Server console output - no error:
UPDATE - Round 1
Tested using wget, error 500 as well. No futher info.
UPDATE - Round 2
Tested using wget POST, and after modifying WebSecurityConfig. Here is the screeshot:
Server console contains warning upon the wget POST:
The updated code for WebSecurityConfig:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
}
}
Try config cors like that:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
};
}
}
The problem was due to the Nginx reverse proxy handling. In localhost, I was using HTTP, but in the dev server, we are only using HTTPS. Thus, I used HTTPS for all the RESTful API URLs.
I'm running the complete version of Spring boot Upload file guide at Spring Guide, but I used Undertow as embedded servlet instead of Tomcat default. And it worked.
When I try to upload file with size larger than the value in config file
spring.http.multipart.max-file-size=128KB
spring.http.multipart.max-request-size=128KB
It raises exception. That is expected behavior
With Tomcat embedded servlet, it can be easily handled by catching SizeLimitExceededException or MultipartException
But with Undertow, it throws RuntimeException event I catch it in global handler:
#ControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({RuntimeException.class, Exception.class, IOException.class})
ResponseEntity<?> handleControllerException(Exception exc, HttpServletRequest request, Throwable ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
The Exception message:
java.lang.RuntimeException: java.io.IOException: UT000020: Connection terminated as request was larger than 131072
at io.undertow.servlet.spec.HttpServletRequestImpl.parseFormData(HttpServletRequestImpl.java:779) ~[undertow-servlet-1.4.20.Final.jar:1.4.20.Final]
at io.undertow.servlet.spec.HttpServletRequestImpl.getParameter(HttpServletRequestImpl.java:653) ~[undertow-servlet-1.4.20.Final.jar:1.4.20.Final]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:75) ~[spring-web-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.12.RELEASE.jar:4.3.12.RELEASE]
Here is the codes:
The controller
#PostMapping("/")
public String handleFileUpload(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
storageService.store(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
pom.xml
<?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>
<groupId>org.springframework</groupId>
<artifactId>gs-uploading-files</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Anyone get the same problem with me? How can I catch the exception when maximum file size upload is exceeded?
Any help would be greatly appreciated
Maybe it is able to help in some particular case.
I have the same problem with undertow and exception handling.
My case is a little bit different, I also have spring-security, so the exceptions like RequestTooBigException are caused in spring-security filters when some of them try to read information from the request.
As a solution, I use an additional filter that is added before the first security filter that is caused that exception. (For my it is BearerTokenAuthenticationFilter.class)
There is an additional filter that handles these exceptions:
#Component
#Slf4j
public class SecurityRequestToBigExceptionHandlerFilter extends OncePerRequestFilter {
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (IllegalStateException | RequestTooBigException | MultiPartParserDefinition.FileTooLargeException ex) {
if (ex instanceof RequestTooBigException
|| ex instanceof MultiPartParserDefinition.FileTooLargeException) {
writeResponse(response, ex);
return;
}
Throwable causeException = ex.getCause();
if (nonNull(causeException)
&& (causeException instanceof RequestTooBigException
|| causeException instanceof MultiPartParserDefinition.FileTooLargeException)) {
writeResponse(response, causeException);
return;
}
throw ex;
}
}
private void writeResponse(HttpServletResponse response, Throwable ex) throws IOException {
log.debug("Handle RequestToBig Exception with {}", ex.getMessage());
response.setStatus(HttpStatus.BAD_REQUEST.value());
response.getWriter().write(ex.getMessage());
}
}
Next what we need to change is security configuration.
There is my simplified security config:
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private final SecurityRequestToBigExceptionHandlerFilter requestToBigExceptionHandlerFilter;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(requestToBigExceptionHandlerFilter, BearerTokenAuthenticationFilter.class)
.csrf().disable()
.cors()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
I am trying to set up my mybatis-spring like shown in the following examples:
1)Code from a previous answer on stackoverflow, a few answer down
(MyBatis-Spring + #Configuration - Can't autowire mapper beans)
#Configuration
#MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig
{
#Bean
public DataSource dataSource()
{
return new EmbeddedDatabaseBuilder().addScript("schema.sql").build();
}
#Bean
public DataSourceTransactionManager transactionManager()
{
return new DataSourceTransactionManager(dataSource());
}
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception
{
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
return sessionFactory.getObject();
}
}
2)Code from their documentation (http://www.mybatis.org/spring/mappers.html)
Usage:
public class FooServiceImpl implements FooService {
private UserMapper userMapper;
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public User doSomeBusinessStuff(String userId) {
return this.userMapper.getUser(userId);
}
}
Registering Mapper with #MapperScan:
#Configuration
#MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().addScript("schema.sql").build()
}
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
return sessionFactory.getObject();
}
}
My code which isn't working is as shown below:
My Application with nested AppConfig:
#SpringBootApplication
#MapperScan(basePackages="com.tjwhalen.game.service.dao")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
//#MapperScan(basePackages="com.tjwhalen.game.service.dao")
public class AppConfig {
#Autowired
DataSource datasource;
#Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(datasource);
}
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(datasource);
//sqlSessionFactory.setTypeAliasesPackage("com.tjwhalen.game.model");
return (SqlSessionFactory) sqlSessionFactory.getObject();
}
#Bean
public ItemSummaryDbService itemSummaryDbService() {
return new ItemSummaryDbServiceImpl();
}
}
}
My service:
public class ItemSummaryDbServiceImpl implements ItemSummaryDbService {
#Autowired
private ItemSummaryMapper itemSummaryMapper;
public void setItemSummaryMapper(ItemSummaryMapper itemSummaryMapper) {
this.itemSummaryMapper = itemSummaryMapper;
}
public void writeItemSummarys(List<ItemSummary> itemSummarys) {
for(ItemSummary itemSummary : itemSummarys) {
itemSummaryMapper.insertItemSummary(itemSummary);
}
}
public List<ItemSummary> lookupItemSummarys() {
return itemSummaryMapper.selectItemSummarys();
}
}
My mapper in the package indicated by the #MapperScan annotaion:
package com.tjwhalen.game.service.dao;
import java.util.List;
import com.tjwhalen.game.model.ItemSummary;
public interface ItemSummaryMapper {
public void insertItemSummary(ItemSummary itemSummary);
public List<ItemSummary> selectItemSummarys();
}
My usage:
public class LoadItems implements CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(LoadItems.class);
#Autowired
private ItemSummaryDbService service;
public void run(String... arg0) throws Exception {
logger.info("LoadItems is running");
ArrayList<ItemSummary> list = new ArrayList<ItemSummary>();
list.add(new ItemSummary(1, "one", 1));
service.writeItemSummarys(list);
}
}
My stacktrace:
java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransactionFactory.newTransaction(Ljava/sql/Connection;Z)Lorg/apache/ibatis/transaction/Transaction;
at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(DefaultSqlSessionFactory.java:77) ~[ibatis-core-3.0.jar:na]
at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession(DefaultSqlSessionFactory.java:40) ~[ibatis-core-3.0.jar:na]
at org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:102) ~[mybatis-spring-1.3.0.jar:1.3.0]
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:429) ~[mybatis-spring-1.3.0.jar:1.3.0]
at com.sun.proxy.$Proxy41.insert(Unknown Source) ~[na:na]
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:279) ~[mybatis-spring-1.3.0.jar:1.3.0]
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:54) ~[ibatis-core-3.0.jar:na]
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:35) ~[ibatis-core-3.0.jar:na]
at com.sun.proxy.$Proxy42.insertItemSummary(Unknown Source) ~[na:na]
at com.tjwhalen.game.service.impl.ItemSummaryDbServiceImpl.writeItemSummarys(ItemSummaryDbServiceImpl.java:32) ~[classes/:na]
at com.tjwhalen.game.loader.LoadItems.run(LoadItems.java:28) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:782) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:769) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1185) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1174) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at com.tjwhalen.game.Application.main(Application.java:37) [classes/:na]
And finally my pom.xml:
<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>
<groupId>com.tjwhalen.game</groupId>
<artifactId>Runescape-App</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/postgresql/postgresql -->
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-702.jdbc4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.ibatis/ibatis-core -->
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<!-- <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.2.RELEASE</version>
</dependency> -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.batch/spring-batch-infrastructure -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-infrastructure</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I've had a lot of problems in the past regarding incorrect pom.xml, so I looked at each dependency involving database access and made sure the project had the correct provided dependencies. I also checked that the versions were the same as mentioned in the provided dependencies.
What does the error message AbstractMethodError indicate?
Feel free to ask any clarifying questions
I figured it out. I added a dependency
<!-- https://mvnrepository.com/artifact/org.apache.ibatis/ibatis-core -->
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
and removing it fixed. I don't know why this dependency was here, I went through and checked the rest of my dependencies to see if they depended on ibatis-core and they didn't.
So it was an oversight on my part, and shows that AbstractMethodError is likely a dependency issue, and that is the first thing that should be checked when facing it