springboot security swagger springfox-boot-starter - java

pom.xml
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
spring-security configure
protected void configure(HttpSecurity httpSecurity) throws Exception {
String[] SWAGGERS = {
"/swagger/**",
"/v3/**"
};
httpSecurity
.authorizeRequests(expressionInterceptUrlRegistry ->
expressionInterceptUrlRegistry
// 放行 druid 页面
.antMatchers("/zhy-druid/**").permitAll()
.antMatchers(SWAGGERS).anonymous()
.anyRequest().authenticated()
);
httpSecurity
.formLogin(httpSecurityFormLoginConfigurer ->
httpSecurityFormLoginConfigurer
.loginPage("/authentication")
.successHandler(accountAuthenticationSuccessHandler)
.failureHandler(accountAuthenticationFailureHandler)
);
httpSecurity
.logout(httpSecurityLogoutConfigurer ->
httpSecurityLogoutConfigurer
.logoutUrl("/cancellation")
.logoutSuccessHandler(accountLogoutSuccessHandler)
);
httpSecurity
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer
.authenticationEntryPoint(accountAuthenticationEntryPointHandler)
.accessDeniedHandler(accountAccessDeniedHandler)
);
httpSecurity
.cors();
httpSecurity
.csrf()
.disable();
}
application-local.yml
springfox:
documentation:
enabled: true
swagger-ui:
base-url: /swagger
I get this result.
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: 2.0 and those that match openapi: 3.0.n (for example, openapi: 3.0.0).

Openapi is the latest library and recommended for spring boot applications. It's the next version of swagger.
Add the below code for work openapi in your application.
pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.2.32</version>
</dependency>
Spring-Security configure to allow open api url.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userServiceImpl;
#Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers(SecurityConstants.SIGN_UP_URL).permitAll()
.antMatchers("/swagger-ui/**").permitAll()
.antMatchers("/v3/**").permitAll()
.antMatchers("/api-docs.html").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new AuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
application.yml
springdoc:
swagger-ui.path: /api-docs.html

Related

Documenting Spring Security's form login endpoint with springdoc-openapi

I need help finding a way to make Spring Security's form login endpoint work in swagger-ui with springdoc-openapi. I'm using SpringBoot 2.7.5 and Spring Security 5.7.4, and these are the rest of the project's dependencies:
<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.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-security</artifactId>
<version>1.6.12</version>
</dependency>
</dependencies>
Spring Security simple configuration for securing /foos/ endpoints with form login and defining user login details.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/foos/**")
.authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, PasswordEncoder passwordEncoder) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder.encode("password"))
.roles("USER");
}
}
And the foos Controller:
#RestController
#RequestMapping("foos")
public class FooController {
#GetMapping(value = "/{id}")
public Foo findById(#PathVariable("id") final Long id) {
return new Foo(randomAlphabetic(6));
}
#GetMapping
public List<Foo> findAll() {
return Lists.newArrayList(new Foo(randomAlphabetic(6)));
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public Foo create(#RequestBody final Foo foo) {
return foo;
}
}
Also supplying this property: springdoc.show-login-endpoint=true and the login endpoint is exposed in swagger-ui but the only problem is that the only request body type is application/json and this sends the credentials as json in the request body which results in null username/password in UsernamePasswordAuthenticationFilter.
Sample code repo: https://github.com/adrianbob/springdoc-form-login
Can the request body type be configured to application/x-www-form-urlencoded, so that form login works?
I've raised an issue which was categorised as a bug and fixed. The latest release does not have this problem anymore, version 1.6.13.

Error in Spring Security with status code 404

I'm facing some problems with the spring security /login endpoint
I would like to have a auth api but
when I do any post request to the
localhost:8080/login
this is the response
{
"timestamp":"2020-11-12T17:00:32.691+00:00",
"status":404,
"error":"Not Found",
"message":"",
"path":"/login"
}
This is my configuration class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private ApplicationUserService applicationUserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I found out why!!
I was using this dependency
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
and the right one for my purpose its
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Add formlogin() to your configuration:
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin();
You will not need .antMatchers("/login").permitAll().
Your error looks like there is no login-endpoint at all. formLogin() takes care of its generation.
I had a similar problem before, and I solved it by adding a "/" at the end of the URL.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login/").permitAll()
.anyRequest().authenticated();
}
Please check your controller that you are entering the correct path. There is no issue with the Security Configuration.

Spring Security + Thymeleaf - hide specific data from user if not authenticated

I Think Thymeleaf doesn't know when user is logged in , I have hidden two <a> tags from users who are authenticated but they still are displayed.
pom.xml :
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Here's the code for problem - hidding two anchor tags from users
who are authenticated :
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
...
...
<div sec:authorize="isAnonymous()">
<a th:href="#{/login}">Log in</a>
<br>
<a th:href="#{/register}">Register</a>
</div>
<br>
<a th:href="#{/recipeList}">List Page</a>
Even after I log in , I still see the "login" and "register" tags
And here's the configuration, if its useful :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public DataSource dataSource;
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public JdbcUserDetailsManager jdbcUserDetailsManager() throws Exception{
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
jdbcUserDetailsManager.setDataSource(dataSource);
return jdbcUserDetailsManager;
}
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception{
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/recipeList").permitAll()
.antMatchers("/foodDescription/**").permitAll()
.antMatchers("/addNew/**").hasAnyRole("ADMIN","USER")
.antMatchers("/delete/**").hasRole("ADMIN")
.antMatchers("/edit/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/").permitAll();
http.csrf().disable();
}
}
My guess is that Thymeleaf doesn't know when user is logged in, if any other class is needed from my code, I'll edit it . Been stuck on this forever now.
I assume that you are using Spring Boot 2.1.x
Then you have to use the version 5:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

Swagger-ui with Spring security

I have a simple REST application with authentication service. I tried to add swagger and swagger-ui to it, but I can only see my endpoints in /v2/api-docs.
In swagger-ui.html I see only groups of endpoints but I am unable to extend any list.
In chrome debug I see:
Failed to load resource: the server responded with a status of 401 ()
Uncaught TypeError: Cannot read property 'indexOf' of undefined
and on a terminal with a server:
ERROR 10020 --- [nio-5001-exec-3] c.t.r.a.p.JwtAuthenticationEntryPoint : Responding with unauthorized error. Message - Full authentication is required to access this resource
It looks like my config files are missing something, I tried few solutions I found on a web but still nothing work.
This is my code:
pom
<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>
controller
#RestController
#PreAuthorize("hasRole('USER')")
#RequestMapping(path = "restaurant")
#Api(value="restaurant", description="Example operations for restaurants")
public class RestaurantController {
// endpoints
}
swagger bean
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.tablebooker.restaurantservice.restaurant"))
.paths(PathSelectors.any())
.build();
}
}
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//other methods
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/restaurant/**")
.hasRole("USER")
.anyRequest()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**");
}
}
Any ideas how can I make my configuration work?
For me, there was no issue in traditional Weblogic deployment without any mention of #Override public void configure(WebSecurity web) throws Exception ...Only #Override protected void configure(HttpSecurity http) throws Exception was enough and UI was visible on swagger.
But the same code was not working on Apache Tomcat server so below code was needed ,
#Override public void configure(WebSecurity web) throws Exception { web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**"); // ignore swagger web.ignoring().mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs","/webjars/**"); }
/webjars/** being missing in answer by AokoQin.
Answering here because I didn't faced any issues on Weblogic without above code but only Tomcat. I already had resources added via ResourceHandlerRegistry in mvc config.
First you should registry swagger's resources.
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
}
}
Then cause you're using Spring Security,maybe you should shutdown privileges.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**");
// ignore swagger
web.ignoring().mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs");
}
And maybe it's better for you to use swagger which the version is under 2.8.0,or you may have to face to lots of bugs.
The previous answers helped me, but are not quite complete / outdated. I was facing the same issue and it's working now:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
#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
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs", "/webjars/**");
}
...
}

CrossOrigin annotation doesn't work with spring security

When i enable the spring-boot-starter-security dependency. CORS support doesn't work.
This is my SecurityConfiguration Class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return authentication -> {
// ...
};
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
My Controller Class has a CrossOrigin Annotation:
#CrossOrigin
#RequestMapping("/frontend")
#RestController
public class FrontEndController extends BaseController {
I can handle CORS with custom CORS Filter but I want to use just one Annoation.
I found 2 methods for adding CORS support to spring-security enabled spring-boot project. We can add spring-web CorsFilter to security filter chain. The following example belongs to token based authentication project. So we used a custom RestAuthenticationFilter.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
source.registerCorsConfiguration("/**", config);
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding spring-web CORS filter
.addFilterBefore(new CorsFilter(source), LogoutFilter.class)
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
But in the above example our CrossOrigin annotations in the controllers are redundant. So we should give the ability to control CORS requests to spring-web layer. Therefore we can allow CORS pre-flight (OPTIONS HTTP Methods).
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf()
// Disabling CSRF
.disable()
// Disabling Session Management
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
// Adding custom REST Authentication filter
.addFilterBefore(new RestAuthenticationFilter(authenticationManager()), LogoutFilter.class)
// Authorizing requests
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.antMatchers("/", "/frontend/login")
.permitAll()
.antMatchers("/api/**", "/frontend/**")
.authenticated()
.antMatchers("/**")
.permitAll();
}
}
With the help of above configuration we can use both #CrossOrigin annotations and spring-security configuration.

Categories