Spring Webflux Security blocking Kafka GET /connectors calls - spring-boot

I have a spring boot Webflux application. I am using Kafka with Apache camel and also using websockets.
I recently added Spring security. For I do not need Authentication therefore I am not authenticating but only authorizing the incoming requests.
Since adding the security it is blocking Kafka Connect's REST API endpoints eg. GET /connectors call. IMO, it should not be blocking them as they would be raised from inside the applications.
Error Logs
2021-07-29 22:20:51.654 DEBUG 68377 --- [ctor-http-nio-4] o.s.w.s.adapter.HttpWebHandlerAdapter : [adf2f930-1, L:/127.0.0.1:8083 - R:/127.0.0.1:60653] HTTP GET "/connectors"
2021-07-29 22:20:51.659 DEBUG 68377 --- [ parallel-3] o.s.w.s.s.DefaultWebSessionManager : Created new WebSession.
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/weather-data/api/**', method=GET}
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] athPatternParserServerWebExchangeMatcher : Request 'GET /connectors' doesn't match 'GET /weather-data/api/**'
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/weather-data/api/**', method=POST}
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] athPatternParserServerWebExchangeMatcher : Request 'GET /connectors' doesn't match 'POST /weather-data/api/**'
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/weather-data/api/events', method=GET}
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] athPatternParserServerWebExchangeMatcher : Request 'GET /connectors' doesn't match 'GET /weather-data/api/events'
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.a.AuthorizationWebFilter : Authorization failed: Access Denied
2021-07-29 22:20:51.669 INFO 68377 --- [ parallel-3] c.s.M.w.WebFluxSecurityConfiguration : Authentication exception
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: Not Authenticated
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:70) ~[spring-security-web-5.5.0.jar:5.5.0]
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.lambda$filter$1(ExceptionTranslationWebFilter.java:45) ~[spring-security-web-5.5.0.jar:5.5.0]
1. Webflux Security configuration
package com.reactive.sse.security
import mu.KotlinLogging
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.SecurityWebFiltersOrder
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.authentication.AuthenticationWebFilter
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
#Configuration
#EnableWebFluxSecurity
class SecurityConfiguration {
private val logger = KotlinLogging.logger {}
val READ_ROLE = "DATA_READ"
#Bean
fun securityWebFilterChain(
http: ServerHttpSecurity,
weatherAuthenticatedRequestManager: WeatherAuthenticatedRequestManager,
authenticationConverter: ServerAuthenticationConverter
): SecurityWebFilterChain {
val authenticationWebFilter = AuthenticationWebFilter(weatherAuthenticatedRequestManager)
authenticationWebFilter.setServerAuthenticationConverter(authenticationConverter)
return http
.exceptionHandling()
.authenticationEntryPoint { swe: ServerWebExchange, ex: AuthenticationException? ->
logger.error(ex) { "Authentication exception" }
Mono.fromRunnable { swe.response.statusCode = HttpStatus.UNAUTHORIZED }
}.accessDeniedHandler { swe: ServerWebExchange, ex: AccessDeniedException? ->
logger.error(ex) { "Authorization exception" }
Mono.fromRunnable { swe.response.statusCode = HttpStatus.FORBIDDEN }
}.and()
.authorizeExchange()
.pathMatchers(HttpMethod.GET,"/weather-data/api/**").hasRole(READ_ROLE)
.and()
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
.httpBasic().disable()
.csrf().disable()
.formLogin().disable()
.logout().disable()
.build()
}
}
2. WeatherAuthenticationConverter
package com.reactive.sse.security
import org.springframework.http.HttpCookie
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
#Component
class WeatherAuthenticationConverter : ServerAuthenticationConverter {
override fun convert(exchange: ServerWebExchange?): Mono<Authentication> {
return Mono.justOrEmpty(exchange)
.flatMap { serverWebExchange: ServerWebExchange ->
Mono.justOrEmpty(serverWebExchange.request.cookies["X-Auth"])
}
.filter { cookies: List<HttpCookie> -> cookies.isNotEmpty() }
.map { cookies: List<HttpCookie> -> cookies[0].value }
.map { authenticationStr: String ->
UsernamePasswordAuthenticationToken(
authenticationStr,
authenticationStr
)
}
}
}
3. WeatherAuthenticatedRequestManager
package com.reactive.sse.security
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
#Component
class WeatherAuthenticatedRequestManager : ReactiveAuthenticationManager {
/**
* Authenticate based on the Jwt token
*
* #property authentication
* #return Authentication Publisher
*/
override fun authenticate(authentication: Authentication): Mono<Authentication> {
return Mono.just(authentication)
.map { authenticationObj: Authentication ->
val roles: List<String> = (authenticationObj.credentials as List<*>).filterIsInstance<String>()
val rolesAuthority = roles.map { value -> SimpleGrantedAuthority(value) }
UsernamePasswordAuthenticationToken(
roles,
roles,
rolesAuthority
)
}
}
}
How should I configure by spring security so that it does not block the internal calls like this.

After doing google I found /connectors API call initiated by Kafka. It runs on port 8083. My spring boot application unfortunately running on 8083. Therefore It was blocked. I am still confused why it was blocking the internal calls.
As soon as I changed the port to 8089 or something else, error goes away

Related

Custom JWT Auth with Spring Webflux

I'm trying to setup authentication with JWT with Spring Security inside a Spring WebFlux application. I'm using a custom authorization scheme based on custom JWT claims. The problem I'm having is that when I try to invoke a secured endpoint authentication fails with Authentication failed: An Authentication object was not found in the SecurityContext.
Here is the SecurityWebFilterChain I'm using:
#Configuration
#EnableWebFluxSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfiguration {
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
jwt {
jwtAuthenticationConverter = grantedAuthoritiesExtractor()
}
}
}
}
fun grantedAuthoritiesExtractor(): Converter<Jwt, Mono<AbstractAuthenticationToken>> {
val jwtAuthenticationConverter = JwtAuthenticationConverter()
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(MappingJwtAuthoritiesConverter())
// custom JWT -> Collection<GrantedAuthority> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter)
}
#Bean
fun jwtDecoder(): ReactiveJwtDecoder {
val secretKey: SecretKey = SecretKeySpec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toByteArray(), "HMACSHA256")
return NimbusReactiveJwtDecoder.withSecretKey(secretKey).macAlgorithm(MacAlgorithm.HS256).build()
}
#Bean
fun jwtValidator(): OAuth2TokenValidator<Jwt> {
return OAuth2TokenValidator<Jwt> { OAuth2TokenValidatorResult.success() }
}
fun jwtAuthenticationManager(jwtDecoder: ReactiveJwtDecoder): JwtReactiveAuthenticationManager {
return JwtReactiveAuthenticationManager(jwtDecoder).apply {
this.setJwtAuthenticationConverter(grantedAuthoritiesExtractor())
}
}
}
Here is MappingJwtAuthoritiesConverter:
class MappingJwtAuthoritiesConverter : Converter<Jwt, Collection<GrantedAuthority>> {
companion object {
private val WELL_KNOWN_CLAIMS: Set<String> = setOf("myCustomClaim")
}
override fun convert(jwt: Jwt): Collection<GrantedAuthority> {
val authorities = jwt.claims.entries
.filter { (key, _) -> key in WELL_KNOWN_CLAIMS }
.map { (key, value) ->
return#map SimpleGrantedAuthority("$key:$value")
}
return authorities
}
}
I searched online but many JWT/Spring Webflux implementation hand-roll JWT validation and handling, and I'd rather use what's already offered by Spring under OAuth integration. Right now the only custom piece I'm using is the converter from JWT to GrantedAuthority, but I still can't get authentication to work.
With the following JWT:
header:
{
"typ": "JWT",
"alg": "HS256"
}
payload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1658474926,
"exp": 1668478526,
"myCustomClaim": "READ"
}
Encoded:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjU4NDc0OTI2LCJleHAiOjE2Njg0Nzg1MjYsIm15Q3VzdG9tQ2xhaW0iOiJSRUFEIn0.lh66pHMd_xvXAF2itblHeHbZReJQA5xkGLKqXZV6MjI
The endpoint I'm trying to secure is defined as:
#RestController
class FooController {
#PreAuthorize("hasAuthority('myCustomClaim:READ')")
#RequestMapping(
method = [RequestMethod.GET],
value = ["/foo"],
)
override suspend fun getFoo(): ResponseEntity<String> {
return ResponseEntity.ok("Got foo")
}
}
Spring logs:
2022-07-22 09:43:35.138 DEBUG 508191 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [b7fe0c9e-1] HTTP GET "/foo"
2022-07-22 09:43:35.313 DEBUG 508191 --- [ parallel-2] o.s.w.s.s.DefaultWebSessionManager : Created new WebSession.
2022-07-22 09:43:35.319 DEBUG 508191 --- [ parallel-2] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers$1#3602a026
2022-07-22 09:43:35.319 DEBUG 508191 --- [ parallel-2] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : matched
2022-07-22 09:43:35.319 DEBUG 508191 --- [ parallel-2] a.DelegatingReactiveAuthorizationManager : Checking authorization on '/foo' using org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager#2668fdab
2022-07-22 09:43:35.320 DEBUG 508191 --- [ parallel-2] o.s.s.w.s.a.AuthorizationWebFilter : Authorization successful
2022-07-22 09:43:35.325 DEBUG 508191 --- [ parallel-2] s.w.r.r.m.a.RequestMappingHandlerMapping : [b7fe0c9e-1] Mapped to it.project.backend.controllers.FooController#getFoo(Continuation)
2022-07-22 09:43:35.665 DEBUG 508191 --- [ parallel-2] o.s.s.w.s.a.AuthenticationWebFilter : Authentication failed: An Authentication object was not found in the SecurityContext
2022-07-22 09:43:35.694 DEBUG 508191 --- [ parallel-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [b7fe0c9e-1] Completed 401 UNAUTHORIZED
Any ideas?
The problem was using #EnableGlobalMethodSecurity instead of #EnableReactiveMethodSecurity in the configuration bean. After fixing that everything started to work.
Looks like the secret key you constructed your jwtDecoder with does not correspond to the signature of the token you posted and thus the token validation fails.
You can use https://jwt.io/ to check that.

Why is AOP Logging not working in my project

I'm wasting a lot of time right now with AOP logging setup.
I don't know why AOP isn't working in my project.
I think I've done all the settings I can.
Please let me know if you guys have a solutions.
Thank you.
application.java
#EnableAspectJAutoProxy
#SpringBootApplication
#ComponentScan(basePackages = "com.demo.apiservice")
#MapperScan("com.demo.apiservice.mapper")
public class ApiServiceApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ApiServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(ApiServiceApplication.class, args);
}
#Bean
public ModelMapper modelMapper() {
return new CustmizedModelMapper();
}
#Bean
public AopLoggingConfig loggingAspect(){
return new AopLoggingConfig();
}
}
build.gradle
configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
}
dependencies {
//implementation 'org.springframework.boot:spring-boot-starter-security:2.5.5'
implementation 'org.springframework.boot:spring-boot-starter-web:2.5.5'
implementation 'org.springframework.boot:spring-boot-starter-log4j2:2.5.5'
implementation 'org.springframework.boot:spring-boot-starter-mail:2.5.5'
implementation 'org.springframework.boot:spring-boot-starter-aop:2.5.5'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.1.3'
implementation 'org.springframework.boot:spring-boot-starter-data-rest:2.5.5'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.projectlombok:lombok:1.18.8'
implementation 'org.modelmapper:modelmapper:2.3.8'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.5'
implementation 'org.springframework.boot:spring-boot-starter-jdbc:2.5.5'
implementation 'com.h2database:h2:1.4.200'
implementation 'org.springframework.boot:spring-boot-configuration-processor:2.5.5'
implementation 'org.springframework.security:spring-security-core:5.4.2'
implementation 'com.fasterxml.jackson.core:jackson-core:2.12.3'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.12.3'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.3'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
implementation 'com.nimbusds:nimbus-jose-jwt:9.7'
implementation 'io.springfox:springfox-swagger2:3.0.0'
implementation 'io.springfox:springfox-swagger-ui:2.9.2'
implementation 'joda-time:joda-time:2.10.10'
implementation 'io.jsonwebtoken:jjwt-api:0.11.1'
implementation 'javax.inject:javax.inject:1'
implementation 'com.googlecode.json-simple:json-simple:1.1'
implementation 'de.mkammerer:argon2-jvm:2.7'
implementation 'org.bouncycastle:bcprov-jdk15on:1.68'
implementation 'org.apache.maven.plugins:maven-surefire-plugin:2.22.0'
implementation 'javax.validation:validation-api:2.0.1.Final'
implementation 'org.postgresql:postgresql:42.1.4'
implementation 'org.hibernate:hibernate-gradle-plugin:5.6.1.Final'
implementation 'com.jayway.jsonpath:json-path:2.6.0'
compileOnly 'org.projectlombok:lombok:1.18.8'
testCompileOnly 'org.projectlombok:lombok:1.18.8'
runtimeOnly 'org.springframework.boot:spring-boot-devtools:2.5.5'
annotationProcessor 'org.projectlombok:lombok:1.18.8'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.8'
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.5.5'
testImplementation 'org.springframework.security:spring-security-test:5.5.2'
}
AopLoggingComponent.java
package com.demo.apiservice.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class AopLoggingConfig {
Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class);
static String name = "";
static String type = "";
/**
* AspectJ is applied only to a specific class/method in package.
*/
#Around("execution(* com.demo.apiservice.customer.*Controller.*(..))")
public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable {
type = joinPoint.getSignature().getDeclaringTypeName();
if (type.indexOf("Controller") -1) {
name = "Controller \t: ";
}
else if (type.indexOf("Service") -1) {
name = "ServiceImpl \t: ";
}
else if (type.indexOf("DAO") -1) {
name = "DAO \t\t: ";
}
logger.debug(name + type + ".######################### " + joinPoint.getSignature().getName() + "()");
return joinPoint.proceed();
}
}
controller.java
package com.demo.apiservice.customer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
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.PutMapping;
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.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.demo.apiservice.constant.Constants;
import com.demo.apiservice.customer.service.CustomerService;
import com.demo.apiservice.customer.service.impl.CustomerServiceImpl;
import com.demo.apiservice.request.CustomerRequest;
import com.demo.apiservice.request.LoginRequest;
import com.demo.apiservice.response.GetCustomerResponse;
import com.demo.apiservice.response.SuccessResponse;
import com.demo.apiservice.utils.ResponseHandler;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import javax.validation.Valid;
#Slf4j
#RestController
#Api(tags = "Customers APIs")
#RequestMapping("/apiservice/v1/customers")
public class CustomerController {
#Autowired
private CustomerService customerService;
#GetMapping
#ApiOperation(value = "List of Customers API")
#ApiResponses(value = {
#ApiResponse(code = 400, message = Constants.BAD_REQUEST),
#ApiResponse(code = 403, message = Constants.ACCESS_DENIED)})
public ResponseEntity<Object retrieveAll() {
log.info("Start of CustomerController::retrieveAll method");
return customerService.retrieveAll();
}
}
application.yml
logging:
level:
org:
springframework.web: DEBUG
hibernat: DEBUG
com:
atoz_develop:
mybatissample:
repository: TRACE
mybatis:
mapper-locations: classpath:/mappers/*.xml
type-aliases-package: com.demo.apiservice.entity
configuration:
map-underscore-to-camel-case: 'true'
debug: 'true'
spring:
datasource:
driver-class-name: org.postgresql.Driver
username: postgres
url: jdbc:postgresql://localhost:5432/postgres
platform: postgres
password: postgres
jpa:
generate-ddl: 'false'
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQL10Dialect
format_sql: 'true'
hibernate:
ddl-auto: update
show-sql: 'true'
stack trace log
2021-11-17 16:05:19.992 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.DispatcherServlet : GET /apiservice/v1/customers, parameters={} 2021-11-17 16:05:19.992 DEBUG 23300 --- [nio-8080-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.demo.apiservice.customer.CustomerController#retrieveAll()
2021-11-17 16:05:19.993 INFO 23300 --- [nio-8080-exec-8] c.l.a.c.CustomerController : Start of CustomerController::retrieveAll method
2021-11-17 16:05:19.993 INFO 23300 --- [nio-8080-exec-8] c.l.a.c.s.i.CustomerServiceImpl : Inside of the CustomerServiceImpl :: retrieveAll method
2021-11-17 16:05:19.996 INFO 23300 --- [nio-8080-exec-8] c.l.a.c.s.i.CustomerServiceImpl : End of the CustomerServiceImpl :: retrieveAll method
2021-11-17 16:05:19.996 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using application/xhtml+xml, given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json, application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8, application/xml;charset=UTF-8, text/xml;charset=UTF-8, application/*+xml;charset=UTF-8]
2021-11-17 16:05:19.996 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{data=[Customer(email=test1#test.com, password=null, customerId=null, storeName=eos, firstname=test1 (truncated)...]
2021-11-17 16:05:19.998 DEBUG 23300 --- [nio-8080-exec-8] o.s.w.s.DispatcherServlet : Completed 200 OK
Your aspect is triggered. I added an explicit controller method call in order to check:
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ApiServiceApplication.class, args);
context.getBean(CustomerController.class).retrieveAll();
}
Then I fixed some typos in your code like the one mentioned in my previous comment.
Probably your problem was that you simply did not see the log output because you forgot the log configuration for your application package com.demo.apiservice:
logging:
level:
org:
springframework.web: DEBUG
hibernate: DEBUG
com:
atoz_develop:
mybatissample:
repository: TRACE
demo.apiservice: DEBUG
BTW, I also corrected your typo hibernat to hibernate, but that is unrelated to the problem at hand.
Then I see this in the log:
[ restartedMain] o.s.b.w.e.t.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
[ restartedMain] c.d.a.ApiServiceApplication : Started ApiServiceApplication in 5.101 seconds (JVM running for 6.117)
[ restartedMain] c.d.a.c.AopLoggingConfig : Controller : com.demo.apiservice.customer.CustomerController.######################### retrieveAll()
[ restartedMain] c.d.a.c.AopLoggingConfig : Controller : com.demo.apiservice.customer.CustomerController.######################### retrieveAll()
[ restartedMain] c.d.a.c.CustomerController : Start of CustomerController::retrieveAll method
Do you see the problem? You get duplicate logging, because the aspect is picked up once by component scan and instantiated one more time as a bean in your application configuration. So you need to remove this part from ApiServiceApplication:
#Bean
public AopLoggingConfig loggingAspect() {
return new AopLoggingConfig();
}
Now the duplicate logging is gone.
Next, maybe you want to simplify your aspect a bit and simply log joinPoint or joinPoint.getSignature(). You also want to make name and type local variables, because the static fields are not thread-safe. Instead, probably you want a static logger in the aspect.
#Component
#Aspect
public class AopLoggingConfig {
private static Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class);
#Around("execution(* com.demo.apiservice.customer.*Controller.*(..))")
public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable {
String type = joinPoint.getSignature().getDeclaringTypeName();
String name = "";
if (type.contains("Controller")) {
name = "Controller \t: ";
}
else if (type.contains("Service")) {
name = "ServiceImpl \t: ";
}
else if (type.contains("DAO")) {
name = "DAO \t\t: ";
}
logger.debug(name + joinPoint.getSignature());
return joinPoint.proceed();
}
}
The log line becomes:
Controller : ResponseEntity com.demo.apiservice.customer.CustomerController.retrieveAll()
But actually, both the package name and the class name indicate that we are dealing with a controller, DAO or service. So why bother with the if-else stuff in the first place? Why make a simple matter complicated and the aspect slower? Besides, if you only want to log something and not influence the control flow, the method parameters or the return value, a simple #Before advice would do, the expensive #Around is unnecessary.
#Component
#Aspect
public class AopLoggingConfig {
private static Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class);
#Before("execution(* com.demo.apiservice.customer.*Controller.*(..))")
public void logPrint(JoinPoint joinPoint) {
logger.debug(joinPoint.getSignature().toString());
}
}
The log line becomes:
ResponseEntity com.demo.apiservice.customer.CustomerController.retrieveAll()
Isn't that enough?
Or even simpler:
logger.debug(joinPoint.toString());
Log:
execution(ResponseEntity com.demo.apiservice.customer.CustomerController.retrieveAll())
Keep it simple!
The following should work:
package com.demo.apiservice.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class AopLoggingConfig {
Logger logger = LoggerFactory.getLogger(AopLoggingConfig.class);
static String name = "";
static String type = "";
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void controllerClassMethods() {}
#Around("controllerClassMethods()")
public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable {
type = joinPoint.getSignature().getDeclaringTypeName();
if (type.indexOf("Controller") -1) {
name = "Controller \t: ";
}
else if (type.indexOf("Service") -1) {
name = "ServiceImpl \t: ";
}
else if (type.indexOf("DAO") -1) {
name = "DAO \t\t: ";
}
logger.debug(name + type + ".######################### " + joinPoint.getSignature().getName() + "()");
return joinPoint.proceed();
}
}
This will match all the methods in all classes annotated with #RestController.

Spring-WS SOAP server with existing WSDL not finding endpoint

I am trying to develop a quick SOAP server to use during testing using Spring-Boot and Spring-WS. It is contract first as I have existing WSDL and XSD files and have my java classes generated at build time from them. However, when I send a request through using either curl or SOAPUI, I get the following error:
$ curl --header "content-type: text/xml" -d #request.xml http://localhost:8080/ws
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">No adapter for endpoint [public org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation com.meanwhileinhell.mock.soap.MockVolumeChargingEndpoint.getAmount(java.lang.String,long,java.util.List<org.csapi.schema.parlayx.payment.v3_0.Property>) throws org.csapi.wsdl.parlayx.common.v3_0.faults.ServiceException,org.csapi.wsdl.parlayx.common.v3_0.faults.PolicyException]: Is your endpoint annotated with #Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint?</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
(Highlights: No adapter for endpoint [...] Is your endpoint annotated with #Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint?
The answer to this question is yes, my endpoint is annotated with #Endpoint. Full setup below:
build.gradle
plugins {
id 'org.springframework.boot'
}
configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-log4j2'
}
}
version = '0.0.1-SNAPSHOT'
dependencies {
compile("org.springframework.boot:spring-boot-starter-web-services")
compile("wsdl4j:wsdl4j:1.6.1")
compile("org.glassfish.jaxb:jaxb-xjc:2.2.11")
compile('javax.xml.soap:javax.xml.soap-api:1.4.0')
compile('com.sun.xml.messaging.saaj:saaj-impl:1.5.1')
compile project(':meanwhile-in-hell-volume-charging-v3-1-wsdl')
testCompile("org.springframework.boot:spring-boot-starter-test")
}
MockSoapConfig.java
package com.meanwhileinhell.mock.soap.config;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
#EnableWs
#Configuration
public class MockSoapConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(final ApplicationContext applicationContext) {
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(applicationContext);
messageDispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(messageDispatcherServlet, "/ws/*");
}
}
MockVolumeChargingEndpoint.java
package com.meanwhileinhell.mock.soap;
import java.util.List;
import org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation;
import org.csapi.schema.parlayx.bpxg.common.v3_1.ObjectFactory;
import org.csapi.schema.parlayx.payment.v3_0.Property;
import org.csapi.wsdl.parlayx.common.v3_0.faults.PolicyException;
import org.csapi.wsdl.parlayx.common.v3_0.faults.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import generated.org.csapi.wsdl.parlayx.bpxg.payment.volume_charging.v3_1._interface.VolumeCharging;
#Endpoint
public class MockVolumeChargingEndpoint implements VolumeCharging {
private static final Logger logger = LoggerFactory.getLogger(MockVolumeChargingEndpoint.class);
private static final String NAMESPACE = "http://www.csapi.org/wsdl/parlayx/bpxg/payment/volume_charging/v3_1/service";
#Autowired
public MockVolumeChargingEndpoint() {
}
#Override
#PayloadRoot(namespace = NAMESPACE, localPart = "getAmount")
#ResponsePayload
public ChargingInformation getAmount(final String endUserIdentifier,
final long volume,
final List<Property> parameters) throws
ServiceException,
PolicyException {
logger.debug("GetAmount for endUserIdentifier=[{}], volume=[{}], properties=[{}]",
endUserIdentifier,
volume,
parameters);
ObjectFactory objectFactory = new ObjectFactory();
return objectFactory.createChargingInformation();
}
}
This is the generated interface that I am implementing:
VolumeCharging.java
package generated.org.csapi.wsdl.parlayx.bpxg.payment.volume_charging.v3_1._interface;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by Apache CXF 3.3.2
* 2020-01-27T09:24:18.615Z
* Generated source version: 3.3.2
*
*/
#WebService(targetNamespace = "http://www.csapi.org/wsdl/parlayx/bpxg/payment/volume_charging/v3_1/interface", name = "VolumeCharging")
#XmlSeeAlso({org.csapi.schema.parlayx.payment.volume_charging.v3_1.local.ObjectFactory.class, org.csapi.schema.parlayx.bpxg.payment.volume_charging.v3_1.local.ObjectFactory.class, org.csapi.schema.parlayx.bpxg.common.v3_1.ObjectFactory.class, org.csapi.schema.parlayx.common.v3_1.ObjectFactory.class, org.csapi.schema.parlayx.payment.v3_0.ObjectFactory.class})
public interface VolumeCharging {
#WebMethod
#RequestWrapper(localName = "getAmount", targetNamespace = "http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local", className = "org.csapi.schema.parlayx.bpxg.payment.volume_charging.v3_1.local.GetAmount")
#ResponseWrapper(localName = "getAmountResponse", targetNamespace = "http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local", className = "org.csapi.schema.parlayx.bpxg.payment.volume_charging.v3_1.local.GetAmountResponse")
#WebResult(name = "result", targetNamespace = "http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local")
public org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation getAmount(
#WebParam(name = "endUserIdentifier", targetNamespace = "http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local")
java.lang.String endUserIdentifier,
#WebParam(name = "volume", targetNamespace = "http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local")
long volume,
#WebParam(name = "parameters", targetNamespace = "http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local")
java.util.List<org.csapi.schema.parlayx.payment.v3_0.Property> parameters
) throws org.csapi.wsdl.parlayx.common.v3_0.faults.ServiceException, org.csapi.wsdl.parlayx.common.v3_0.faults.PolicyException;
}
This is the SOAP packet that I am sending in my curl/SOAPUI request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:mock="http://www.csapi.org/wsdl/parlayx/bpxg/payment/volume_charging/v3_1/service">
<soapenv:Header/>
<soapenv:Body>
<mock:getAmount>
<mock:endUserIdentifier>TestUserId</mock:endUserIdentifier>
<mock:volume>123123</mock:volume>
<mock:parameters>
<name>ParameterName</name>
<value>ParameterValue</value>
</mock:parameters>
</mock:getAmount>
</soapenv:Body>
</soapenv:Envelope>
I just see why it's not hitting my endpoint. If I change the name getAmount in my request packet to something that is definitely not implemented, I get the message:
No endpoint mapping found for [SaajSoapMessage {http://www.csapi.org/wsdl/parlayx/bpxg/payment/volume_charging/v3_1/service}getTheWeather]
Turning on DEBUG logging for Spring WebServices, I see that my endpoints are being mapped to my endpoint method ok:
2020-01-28 16:36:35.881 DEBUG 72581 --- [ main] yloadRootAnnotationMethodEndpointMapping : Mapped [{http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local}getAmount] onto endpoint [public org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation com.meanwhileinhell.mock.soap.MockVolumeChargingEndpoint.getAmount(java.lang.String,long,java.util.List<org.csapi.schema.parlayx.payment.v3_0.Property>)]
2020-01-28 16:36:58.316 DEBUG 72581 --- [nio-8080-exec-1] .WebServiceMessageReceiverHandlerAdapter : Accepting incoming [org.springframework.ws.transport.http.HttpServletConnection#27ef75ac] at [http://localhost:8080/ws]
2020-01-28 16:36:58.481 DEBUG 72581 --- [nio-8080-exec-1] o.s.ws.server.MessageTracing.received : Received request [SaajSoapMessage {http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local}getAmount]
2020-01-28 16:36:58.541 DEBUG 72581 --- [nio-8080-exec-1] yloadRootAnnotationMethodEndpointMapping : Looking up endpoint for [{http://www.csapi.org/schema/parlayx/bpxg/payment/volume_charging/v3_1/local}getAmount]
2020-01-28 16:36:58.541 DEBUG 72581 --- [nio-8080-exec-1] o.s.w.soap.server.SoapMessageDispatcher : Endpoint mapping [org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping#623a8092] maps request to endpoint [public org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation com.meanwhileinhell.mock.soap.MockVolumeChargingEndpoint.getAmount(java.lang.String,long,java.util.List<org.csapi.schema.parlayx.payment.v3_0.Property>)]
2020-01-28 16:36:58.544 DEBUG 72581 --- [nio-8080-exec-1] o.s.w.soap.server.SoapMessageDispatcher : Testing endpoint adapter [org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter#3571b748]
2020-01-28 16:36:58.547 DEBUG 72581 --- [nio-8080-exec-1] s.e.SoapFaultAnnotationExceptionResolver : Resolving exception from endpoint [public org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation com.meanwhileinhell.mock.soap.MockVolumeChargingEndpoint.getAmount(java.lang.String,long,java.util.List<org.csapi.schema.parlayx.payment.v3_0.Property>)]: java.lang.IllegalStateException: No adapter for endpoint [public org.csapi.schema.parlayx.bpxg.common.v3_1.ChargingInformation com.meanwhileinhell.mock.soap.MockVolumeChargingEndpoint.getAmount(java.lang.String,long,java.util.List<org.csapi.schema.parlayx.payment.v3_0.Property>)]: Is your endpoint annotated with #Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint?
So it would seem that my endpoint is getting mapped ok, but there is some issue with the endpoint adapter?
Needed to wrap my payloads in JAXBElement type for Spring to get the message handler adapters to support the endpoint.
#PayloadRoot(namespace = NAMESPACE, localPart = "getAmount")
#ResponsePayload
public JAXBElement<GetAmountResponse> getAmount(#RequestPayload JAXBElement<GetAmount> request) {
...
}

Kotlin testing authentication to /actuator api

I have a ActuatorSecurity class which I use for authentication for /actuator actions.
package com.netapp.qronicle.config
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationManager
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.crypto.factory.PasswordEncoderFactories
#Configuration
#EnableWebSecurity
class ActuatorSecurity : WebSecurityConfigurerAdapter() {
#Value("\${security.user.actuator-username}")
private val actuatorUsername: String? = null
#Value("\${security.user.actuator-password}")
private val actuatorPassword: String? = null
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.csrf().disable().requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.httpBasic()
}
#Throws(Exception::class)
override fun configure(auth: AuthenticationManagerBuilder) {
val passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
val encodedPassword = passwordEncoder.encode(actuatorPassword)
auth.inMemoryAuthentication()
.withUser(actuatorUsername).password(encodedPassword).roles("USER")
}
#Bean
#Throws(Exception::class)
override fun authenticationManagerBean(): AuthenticationManager {
// ALTHOUGH THIS SEEMS LIKE USELESS CODE,
// IT'S REQUIRED TO PREVENT SPRING BOOT AUTO-CONFIGURATION
return super.authenticationManagerBean()
}
}
I have everything configured in my application.properties file
# spring boot actuator access control
management.endpoints.web.exposure.include=*
security.user.actuator-username=admin
security.user.actuator-password=admin123
I would like to just to do basic authentication api tests for /actuator/** but have not been able to do so, here is my test class
package com.netapp.qronicle.web
import com.netapp.qronicle.config.ActuatorSecurity
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import javax.inject.Inject
#ExtendWith(SpringExtension::class)
#WebMvcTest(ActuatorSecurity::class)
#ContextConfiguration(classes = [ActuatorSecurity::class])
class ActuatorTest {
#Inject
lateinit var mockMvc: MockMvc
#Test
fun `Basic authentication actuator test`() {
val result = mockMvc.perform(
MockMvcRequestBuilders.get("/actuator"))
.andExpect(MockMvcResultMatchers.status().isOk)
Assertions.assertNotNull(result)
}
}
The error thrown is:
2019-02-26 17:07:26.062 INFO 34766 --- [ main] com.netapp.qronicle.web.ActuatorTest : Starting ActuatorTest on jmasson-mac-0 with PID 34766 (started by jonma in /Users/jonma/Development/java/report-generator)
2019-02-26 17:07:26.099 INFO 34766 --- [ main] com.netapp.qronicle.web.ActuatorTest : No active profile set, falling back to default profiles: default
2019-02-26 17:07:29.324 INFO 34766 --- [ main] com.netapp.qronicle.web.ActuatorTest : Started ActuatorTest in 4.468 seconds (JVM running for 6.427)
MockHttpServletRequest:
HTTP Method = GET
Request URI = /actuator
Parameters = {}
Headers = {}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []

Spring Boot Swagger HTML documentation is not getting displayed

I am setting up a REST API / Micro-services project using Spring Boot. I am also trying to enable swagger documentation. When I run spring boot application, I am able to see browse to the localhost:8080/v2/api-docs and it returns API documentation. However, when I attempt browsing http://localhost:8080/documentation/swagger-ui.html or http://localhost:8080/swagger-ui.html browser does not display swagger UI documentation.
I have included following dependencies in the pom file to enable documentation :
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Also, I have create following swagger config class:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2);
}
}
The response I am seeing when trying to access http://localhost:8080/servlet-context :
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Dec 21 03:16:26 GMT 2018
There was an unexpected error (type=Not Found, status=404).
No message available
Server Logs :
2018-12-21 03:25:14.294 DEBUG 4049 --- [ restartedMain] o.s.c.e.PropertySourcesPropertyResolver : Found key 'spring.liveBeansView.mbeanDomain' in PropertySource 'systemProperties' with value of type String
2018-12-21 03:25:14.351 INFO 4049 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-12-21 03:25:14.355 INFO 4049 --- [ restartedMain] c.xxxx.springboot.demo.DemoApplication : Started DemoApplication in 3.951 seconds (JVM running for 4.723)
2018-12-21 03:25:14.373 DEBUG 4049 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Creating new Restarter for thread Thread[main,5,main]
2018-12-21 03:25:14.373 DEBUG 4049 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Immediately restarting application
2018-12-21 03:25:14.373 DEBUG 4049 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader#1b3b349
2018-12-21 03:25:14.373 DEBUG 4049 --- [ restartedMain] o.s.boot.devtools.restart.Restarter : Starting application com.xxxx.springboot.demo.DemoApplication with URLs [file:/Users/XXXXX/XXXXX/SpringMicroservices/SpringBoot/demo/target/classes/]
2018-12-21 03:25:14.763 DEBUG 4049 --- [on(2)-127.0.0.1] o.s.c.e.PropertySourcesPropertyResolver : Found key 'local.server.port' in PropertySource 'server.ports' with value of type Integer
2018-12-21 03:25:14.995 INFO 4049 --- [on(2)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2018-12-21 03:25:14.995 INFO 4049 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2018-12-21 03:25:14.995 DEBUG 4049 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected StandardServletMultipartResolver
2018-12-21 03:25:14.995 DEBUG 4049 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected AcceptHeaderLocaleResolver
2018-12-21 03:25:15.003 DEBUG 4049 --- [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
Please find below one of the controllers:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.validation.Valid;
import java.net.URI;
import java.util.List;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
#RestController
public class UserResource {
#Autowired
private UserDaoService service;
#GetMapping("/users")
public List<User> retrieveAllUsers(){
return service.findAll();
}
#GetMapping("/users/{id}")
public Resource<User> retrieveUser(#PathVariable int id){
User user = service.findOne(id);
if(user==null)
throw new UserNotFoundEception("id-" + id);
Resource<User> resource = new Resource<User>(user);
ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
resource.add(linkTo.withRel("all-users"));
return resource;
}
#PostMapping("/users")
public ResponseEntity<Object> CreateUser(#Valid #RequestBody User user){
User savedUser = service.save(user);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
#DeleteMapping("/users/{id}")
public void deleteUser(#PathVariable int id){
User user = service.deleteById(id);
if(user==null)
throw new UserNotFoundEception("id-" + id);
}
}
package com.example.config
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Predicate;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public #Bean Docket restApi() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().paths(paths()).build();
}
/**
* Url for documentation.
*
* #return
*/
private Predicate<String> paths() {
return regex("/basepackage of your restcontroller/*");
}
/**
* Description your application
*
* #return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("App title").description("App description")
.termsOfServiceUrl("").license("").licenseUrl("").version("1.0").build();
}
}
Here is an example of controller :
#Api(value = "membre")
#RestController
public class ExampleControleur {
#RequestMapping(value = "/find", method = RequestMethod.GET, produces = {
MediaType.APPLICATION_JSON_VALUE }, consumes = { MediaType.APPLICATION_JSON_VALUE })
#ApiOperation(httpMethod = "GET", value = "example text", notes = "note text ")
#ResponseBody
#ApiResponses(value = { #ApiResponse(code = 200, message = "OK !"),
#ApiResponse(code = 422, message = "description."),
#ApiResponse(code = 401, message = "description"),
#ApiResponse(code = 403, message = "description"),
#ApiResponse(code = 404, message = "description"),
#ApiResponse(code = 412, message = "description."),
#ApiResponse(code = 500, message = "description.") })
#ApiImplicitParams({
#ApiImplicitParam(name = "params1", value = "description.", required = true, dataType = "string", paramType = "query", defaultValue = "op"),
#ApiImplicitParam(name = "params2", value = "description.", required = true, dataType = "string", paramType = "query")})
public ResponseEntity<MyObject> getObjet(#RequestParam(value = "params1", required = true) Params1 params1,
#RequestParam(value = "params2", required = true) String param2){
}
}
You can test with this class, but i suppose that you have written all the swagger annotations in your controllers !
You can add to your main class #ComponentScan(basePackages={"the package of your config swagger class"})
Th acces url must be http://localhost:port/context-path if you have it/swagger-ui.html

Resources