Spring Security and custom ws authentication - soap

We just went over to using Spring Boot and Spring Security for a new project that we have. The problem is, our company uses CXF with a custom implemented SAML authentication regime. Of course the custom implementation is rather old, so we are locked to CXF 2.7.*.
A while back, all worked fine since we only exposed SOAP web services and did not use Spring Security. The authenticator that we used in the old solution, uses some kind of JBossWebRealm thingy to authenticate through org.apache.catlina.connector.Request.
But now, we are going to expose REST services as well, using LDAP as an authentication provider. This works like a dream using Spring Security, but now, the security on SOAP services fails. It now tries to use Spring Security to authenticate using the SAML token as a password into AD.
Currently we have the default Servlet that Spring Boot creates. This one exposes the REST resources and a simple health check webpage.
Then we have a servlet that exposes SOAP web services and one that exposes metrics (REST).
Servlet setup:
#Configuration
#EnableAutoConfiguration
#Import(ApplicationConfig.class)
public class ApplicationServletInitializer extends SpringBootServletInitializer {
#Bean
public WebMvcConfigurerAdapter dispatcherServletConfigurer(final MDCInterceptor mdcInterceptor) {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/internal/*");
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(mdcInterceptor);
}
};
}
#Bean(name = "webServiceServlet")
public ServletRegistrationBean webServiceServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new CXFServlet());
servletRegistrationBean.setName("webServiceServlet");
servletRegistrationBean.addUrlMappings("/ws/*");
servletRegistrationBean.setLoadOnStartup(2);
return servletRegistrationBean;
}
#Bean(name = "metricsServlet")
public ServletRegistrationBean metricsServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new MetricsServlet());
servletRegistrationBean.setName("metricsServlet");
servletRegistrationBean.addUrlMappings("/internal/metrics/*");
servletRegistrationBean.setLoadOnStartup(3);
return servletRegistrationBean;
}
}
Security setup:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackageClasses = {
MDCInterceptor.class,
WebSecurityConfigurerAdapterConfig.class
})
public class RestSecurityConfig {
#Value("${ldap.url}")
private String ldapUrl;
#Value("${ldap.domain}")
private String ldapDomain;
#Bean
public ActiveDirectoryLdapAuthenticationProvider authenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(ldapDomain, ldapUrl);
provider.setAuthoritiesMapper(authoritiesMapper());
provider.setUserDetailsContextMapper(userDetailsMapper());
provider.setUseAuthenticationRequestCredentials(true);
provider.setConvertSubErrorCodesToExceptions(true);
return provider;
}
#Bean
public MyAuthoritiesMapper authoritiesMapper() {
return new MyAuthoritiesMapper();
}
#Bean
public MyUserDetailsMapper userDetailsMapper() {
return new MyUserDetailsMapper();
}
}
#Component
public class WebSecurityConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ActiveDirectoryLdapAuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
.antMatchers("/api/**").hasRole("READ")
.and().httpBasic()
.and().anonymous()
.principal(anonymousPrincipal())
.authorities(anonymousRoles());
}
}
web.xml setup:
<security-constraint>
<web-resource-collection>
<web-resource-name>All pages</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
</security-constraint>
Does anyone know if it's possible to solve this? It's not an option to remove the usage of the old security framework that authenticates SAML for the SOAP web services.

Figured it out.
When configuring Spring Security, in WebSecurityConfigurerAdapter, you can also override:
protected void configure(WebSecurity web).
Within this one, you can specify what to ignore.
E.g.:
web.ignoring().antMatchers("/ws/**");

Related

Connecting OAuth2 resource server with authentication server

I'm trying to make a sample OAuth2 Spring authorization and resource server. My intention is to implement two separate applications - one representing authorization server ant the other representing resource server. Since I'm quite a beginner in Spring Security, I guess I need some guidance to complete my task.
I already managed to implement a simple authorization server using in-memory token store (app named "OAuth").
AuthServerOAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
private static final String RESOURCE_ID = "myResource";
#Autowired
private UserApprovalHandler handler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("test")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds(RESOURCE_ID)
.secret("test")
.accessTokenValiditySeconds(300).//invalid after 5 minutes.
refreshTokenValiditySeconds(600);//refresh after 10 minutes.
// #formatter:on
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).userApprovalHandler(handler).authenticationManager(authManager);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
OAuth2SecurityConfig.java
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2SecurityConfig.class);
#Autowired
private ClientDetailsService clientService;
#Autowired
private DataSource dataSource;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth.inMemoryAuthentication()
.withUser("javabycode").password("123456").roles("USER")
.and()
.withUser("admin").password("admin123").roles("ADMIN");
// #formatter:on
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
// #formatter:on
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#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;
}
}
Accessing http://localhost:9081/OAuth/oauth/token?grant_type=password&username=admin&password=admin123 returns token as expected, so I'm guessing that authorization server is configured ok.
Now there's a resource server part (app named "RestTest"). I've managed to find some examples using RemoteTokenServices to access token service that resides in another app. So here's my resource server so far.
OAuth2ResourceConfig.java
#Configuration
#EnableResourceServer
#EnableWebSecurity
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "myResource";
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.
anonymous().disable()
.requestMatchers().antMatchers("/v1/**")
.and().authorizeRequests()
.antMatchers("/v1/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws
Exception {
resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}
#Primary
#Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:9081/OAuth/oauth/check_token/");
tokenService.setClientId("test");
tokenService.setClientSecret("test");
return tokenService;
}
}
I'm trying to secure my REST API (http://localhost:9081/RestTest/v1/foobar) so I believe that configuration above is correct, right? Problem is that when I access v1/foobar endpoint (via Postman) it's accessible without any authentication. So I think I'm simply missing some part of configuration, but I can't figure it out how to connect to authorization server correctly. One more thing to mention - I'm not using Spring Boot!
I'd really appreciate some guidance to make my sample work. Thanks!
EDIT1: I've added resourceId to both authentication and resource server - no luck. Is resourceId even mandatory?
You should add RESOURCE_ID both in ResourceServer and AuthorizationServer in a way that, (you updated your question though with that snippet)
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}
And in your auth server
.scopes("read", "write", "trust").resourceIds(RESOURCE_ID)
Add a springSecurityFilterChain as you missing that in web.xml that you already said in comment
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
From spring docs:
It creates a Servlet Filter known as the springSecurityFilterChain which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within your application.

Spring Boot security for rest

I know there are a lot of topics on that but is there any way just modify the normal spring security to work with json objects.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true) //za pre i post authorize v servisa
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
//Koi service shte polzvame
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.antMatchers("/", "/user/register", "/css/**", "/js/**").permitAll()
.antMatchers("/user/user").access("hasRole('USER') or hasRole('ADMIN')")
.antMatchers("/user/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/user/login").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and()
.rememberMe().rememberMeCookieName("RememberMeFromLecture")
.rememberMeParameter("remember")
.key("golqmaTaina")
.and()
.logout().logoutSuccessUrl("/user/login?logout").logoutRequestMatcher(new AntPathRequestMatcher("/signout")).permitAll()
.and()
.exceptionHandling().accessDeniedPage("/user/unauthorized")
.and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(this.userService).passwordEncoder(getBCryptPasswordEncoder());
}
#Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
}
This is my config file and it works perfectly without rest, but my problem is just want to make the login page to work with rest that's all. If it's configed like this, my login is been done automatically I can't even set a break point inside my controllers. It works, but i want to make it work with rest.
I created a sample application (https://github.com/manishsingh27/TokenBasedAuth) and it is based on REST for authentication.
Client application is based on AngularJS and it has login page, files are here - https://github.com/manishsingh27/TokenBasedAuth/tree/main/authz/src/main/resources/static.
And REST APIs are present here - https://github.com/manishsingh27/TokenBasedAuth/blob/main/authz/src/main/java/com/adms/authz/self/user/controller/UsersController.java.
Config file is here -https://github.com/manishsingh27/TokenBasedAuth/blob/main/authz/src/main/java/com/adms/authz/config/SecurityConfiguration.java
You need to use the #EnableResourceServer annotation to secure the Rest APIs.

Spring security REST authentication with Spring social

I am creating an application where user can login by username and password or by facebook with spring social. I've taken an example from
this tutorial, but it's configured for Spring MVC not for REST.
Author creates special UserDetailsService and assign it to the authentication manager builder.
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
At this point I think everything is okay. UserDetailsService has method to find user by user id.
In http configuration in security config class should I change 'form login' to http 'basic authentication' If I want to have REST application?
If I change this, Should I add correct http authorization header with each request? Or can I use token auth with standard login (by username and password, not social)? Token based authentication will cooperate with Spring social login(there is also token)?
#Configuration
#EnableSocial
public class SocialContext implements SocialConfigurer {
#Autowired
private DataSource dataSource;
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
cfConfig.addConnectionFactory(new FacebookConnectionFactory(
env.getProperty("facebook.app.id"),
env.getProperty("facebook.app.secret")
));
}
#Override
public UserIdSource getUserIdSource() {
return new AuthenticationNameUserIdSource();
}
#Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
return new JdbcUsersConnectionRepository(
dataSource,
connectionFactoryLocator,
Encryptors.noOpText()
);
}
#Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
return new ConnectController(connectionFactoryLocator, connectionRepository);
}
}
Security config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserRepository userRepository;
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/authenticate")
.failureUrl("/login?error=bad_credentials")
//Configures the logout function
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
//Configures url based authorization
.and()
.authorizeRequests()
//Anyone can access the urls
.antMatchers(
"/auth/**",
"/login",
"/signup/**",
"/user/register/**",
"/greeting"
).permitAll()
//The rest of the our application is protected.
.antMatchers("/**").hasRole("USER")
//Adds the SocialAuthenticationFilter to Spring Security's filter chain.
.and()
.apply(new SpringSocialConfigurer())
.and()
.csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
public SocialUserDetailsService socialUserDetailsService() {
return new AppSocialUserDetailsService(userDetailsService());
}
#Bean
public UserDetailsService userDetailsService() {
return new AppUserDetailsService(userRepository);
}
}

Spring + Oauth2: how to refresh access token

I am building rest web services with Spring Boot. Authentication is implemented using Spring Security and OAuth2. Users are authenticated against LDAP server. Here is my websecurityconfig
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(
SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/logout").permitAll()
.antMatchers("/ristore/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.failureHandler(new SimpleUrlAuthenticationFailureHandler());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public RestAuthenticationSuccessHandler mySuccessHandler(){
return new RestAuthenticationSuccessHandler();
}
#Bean
public SimpleUrlAuthenticationFailureHandler myFailureHandler(){
return new SimpleUrlAuthenticationFailureHandler();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = getSource();
auth
.ldapAuthentication()
.userDnPatterns("cn={0},ou=institution,ou=people")
.groupSearchBase("ou=groups")
.contextSource(contextSource);
}
}
Additional config is done in authserverconfig including clientdetailservice.
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new InMemoryTokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ristoreclient")
.scopes("read")
.authorizedGrantTypes("password", "refresh_token", "client_credentials")
.secret("ristoresecret")
.accessTokenValiditySeconds(60);
}
}
It works for initial login. However when I try to get a new access token with refresh token when the old expires, I got the error "UserDetailsService is required". After searching for answers online, I found this post with similar problem: spring-security-oauth2 2.0.7 refresh token UserDetailsService Configuration. Basically the solution there was to create a custom LdapUserDetailsService. Thing is it was set up in xml config instead of java. Besides, it is not clear how and where this class is injected. In this other case, userdetailservice instance is added in auth server endpoint config instead. This article does not provide the implementation of this class.
The idea of having a userdetailservice, in my opinion, is to look up and see if this user is still active before issuing a new access token. What is contradictory is that the request of getting a refresh_token for oauth2 only consists of the following information which does not include username/password.
client_id=clientid
client_secret=clientsecret
refresh_token=1/6BMfW9j53gdGImsiyUH5kU5RsR4zwI9lUVX-tqf8JXQ&
grant_type=refresh_token
OAuth2 for a Spring REST uses Zuul proxy as a middle layer between front end and web api to handle refresh token which makes the configuration more complex. How should I implement a userdetailsservice for oauth2 in Spring Boot and where should I inject it?

Swagger UI does not list any of the controller/end points though I am able to see the json under v2/api-docs endpoint

I am not able to get my Swagger UI to work with my project. Swagger UI comes up fine but it does not list any of my REST controllers.
I am using SPRING 4.2.6.RELEASE and Swagger 2.5.0 . My rest services are deployed to Tomcat 7.0.54 .
When Tomcat 7.0.54 comes up, it is able to fetch the swagger end points.
I am able to hit the endpoint v2/api-docs that fetches the json messages.
I am also able to hit the swagger-ui but I dont see any controllers listed.
The dropdowns are empty, as below
**The issue I am facing currently is that
I am not able to fetch the /swagger-resources/configuration/ui, when I launch the swagger UI I get 404 (Not Found) errror while the UI is trying to fetch /swagger-resources/configuration/ui . I have setup resource handlers for swagger-resources, but that does not seem to help. Can you please let me know what could be missing?
Should I be seeing resources folder under META-INF in my expanded WAR? Should there be any springfox related files/folder inside META-INF?
**
Maven dependency for Swagger
io.springfox
springfox-swagger2
2.5.0
io.springfox
springfox-swagger-ui
2.5.0
Below is my SwaggerCongifuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket api() {
List<SecurityContext> security = new ArrayList<SecurityContext>();
security.add(securityContext());
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/").securityContexts(security);
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.forPaths(PathSelectors.regex("/"))
.build();
}
}
Below is my WebConfig.xml
#EnableWebMvc
#Configuration
#Import(SwaggerConfiguration.class)
#ComponentScan("com.bank.direct.services")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> pConverters) {
pConverters.add(RestUtils.getJSONMessageConverter());
}
#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/");
}
}
Below is the SecurityCongif.xml
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationService _authenticationService;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder pAuth) throws Exception {
pAuth.userDetailsService(_authenticationService);
}
#Override
protected void configure(HttpSecurity pHttp) throws Exception {
// Enable HTTP caching
pHttp.headers().cacheControl().disable();
// Configure security
pHttp.httpBasic()
// -- Allow only authenticated request
.and()
.authorizeRequests()
.anyRequest().authenticated()
// -- Logout configuration
.and()
.logout()
.logoutUrl("/rest/users/logout/")
.deleteCookies("XSRF-TOKEN")
.logoutSuccessUrl("/static/index.html")
.invalidateHttpSession(true)
// -- CSRF configuration
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
Rest Controller class as below
#RestController
#RequestMapping(value = "/vehicles", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class VehicleResource extends Resource {
#Autowired
private IVehicleService _vehicleService;
#RequestMapping(value = "/brands", method = RequestMethod.GET)
public APIResponseEntity getBrands(WebRequest pWebRequest) {
IUser user = getUser(pWebRequest);
BrandCriteria criteria = new BrandCriteria();
criteria.setLanguageCode(user.getLanguageCode());
List<Brand> res = _vehicleService.getBrands(user, criteria);
return newResponseOK(res);
}
#RequestMapping(value = "/brands/{brand_code}", method = RequestMethod.GET)
public APIResponseEntity getBrand(WebRequest pWebRequest, #PathVariable("brand_code") String pBrandCode) {
IUser user = getUser(pWebRequest);
BrandCriteria criteria = new BrandCriteria();
criteria.setLanguageCode(user.getLanguageCode());
criteria.setBrandCode(pBrandCode);
List<Brand> res = _vehicleService.getBrands(user, criteria);
return newResponseOK(res);
}
}
After migrating an older project from XML Spring configuration to Java Spring configuration and updating spring and Swagger versions I struggled with an issue that sounds exactly like this so I thought I'd document my solution here.
I had a number of problems but the main ones that match the OP's scenario were that while /v2/api-docs was accessible and returned JSON, my Controllers clearly weren't being picked up, and when I accessed the Swagger UI at /swagger-ui.html, I was getting a 404 when that page tried to request /swagger-resources/configuration/ui
My Swagger configuration class was:
#Configuration
#EnableSwagger2
public class SwaggerWebConfig {
#Bean
public Docket api() {
...
}
}
The #EnableSwagger2 annotation imports another configuration class Swagger2DocumentationConfiguration, which in turn imports SwaggerCommonConfiguration, which does a component scan for classes in springfox.documentation.swagger.web which finally loads the ApiResourceController, which is where
/swagger-resources/
/swagger-resources/configuration/security and
/swagger-resources/configuration/ui
are served from.
What I had incorrect was that my SwaggerWebConfig class was being loaded by the root application context, when it should belong to the web application context (see ApplicationContext vs WebApplicationContext).
Beans in the web application context can access beans in the root application context, but not the other way around, which explained why Docket bean (incorrectly in the root application context) could not pick up the #Controller beans in the web application context and also explained why despite the ApiResourceController bean being created, its methods were giving 404's when trying to access them (they should be in the web application context)
A few other notes for related issues:
If you can hit v2/api-docs then your Docket bean is working
In a non-spring-boot environment, you need to register two resource handlers yourself as spring boot's auto-configuration would have done this for you as explained in the answers to this question. That should solve 404's for:
/swagger-ui.html (i.e. 404 fetching the actual html swagger-ui.html page)
and the three webjars that swagger-ui.html loads:
/webjars/springfox-swagger-ui/springfox.js
/webjars/springfox-swagger-ui/swagger-ui-bundle.js
/webjars/springfox-swagger-ui/swagger-ui-standalone-preset.js
If you are getting an access denied rather than a 404 not found, then as shown in this answer, you might need to tell spring security to allow access to:
/webjars/**
/swagger-ui.html
/v2/api-docs
/swagger-resources/**
You need to point the the generated Swagger Definition in Swagger UI. i.e in place of http://example.com/api give your swagger definition path something like http://localhost:8080/RestResource/api/swagger.json
This article might help you more